diff options
794 files changed, 15312 insertions, 8486 deletions
diff --git a/Android.bp b/Android.bp index 295ae4c556..197860694b 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 174cde36ef..0a90a0bb24 100644 --- a/Android.mk +++ b/Android.mk @@ -135,11 +135,11 @@ test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) else test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) - adb wait-for-device push $(ANDROID_PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT) + adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT) # Push the contents of the `data` dir into `/data` on the device. If # `/data` already exists on the device, it is not overwritten, but its # contents are updated. - adb push $(ANDROID_PRODUCT_OUT)/data / + adb push $(PRODUCT_OUT)/data / endif endif @@ -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 diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp new file mode 100644 index 0000000000..441b706556 --- /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 0000000000..a5c885a933 --- /dev/null +++ b/adbconnection/adbconnection.cc @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <array> + +#include "adbconnection.h" + +#include "android-base/endian.h" +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" +#include "runtime-inl.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "well_known_classes.h" + +#include "jdwp/jdwp_priv.h" + +#include "fd_transport.h" + +#include "poll.h" + +#ifdef ART_TARGET_ANDROID +#include "cutils/sockets.h" +#endif + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/eventfd.h> +#include <jni.h> + +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<const uint8_t>& data) { + connection_->PublishDdmData(type, data); +} + +class ScopedEventFdLock { + public: + explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) { + TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_))); + } + + ~ScopedEventFdLock() { + TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); + } + + private: + int fd_; + uint64_t data_; +}; + +AdbConnectionState::AdbConnectionState(const std::string& agent_name) + : agent_name_(agent_name), + controller_(this), + ddm_callback_(this), + sleep_event_fd_(-1), + control_sock_(-1), + local_agent_control_sock_(-1), + remote_agent_control_sock_(-1), + adb_connection_socket_(-1), + adb_write_event_fd_(-1), + shutting_down_(false), + agent_loaded_(false), + agent_listening_(false), + 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<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName)); + ScopedLocalRef<jobject> thr_group( + env, + env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup, + art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup)); + return env->NewObject(art::WellKnownClasses::java_lang_Thread, + art::WellKnownClasses::java_lang_Thread_init, + thr_group.get(), + thr_name.get(), + /*Priority*/ 0, + /*Daemon*/ true); +} + +struct CallbackData { + AdbConnectionState* this_; + jobject thr_; +}; + +static void* CallbackFunction(void* vdata) { + std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata)); + 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<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self())); + pthread_t pthread; + std::unique_ptr<CallbackData> 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<const uint8_t>& 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<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type)) << " due to no connection!"; + // Adb is not connected. + return; + } + + // the adb_write_event_fd_ will ensure that the adb_connection_socket_ will not go away until + // after we have sent our data. + static constexpr uint32_t kDdmPacketHeaderSize = + kJDWPHeaderLen // jdwp command packet size + + sizeof(uint32_t) // Type + + sizeof(uint32_t); // length + std::array<uint8_t, kDdmPacketHeaderSize> pkt; + uint8_t* pkt_data = pkt.data(); + + // Write the length first. + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(kDdmPacketHeaderSize + data.size()); + pkt_data += sizeof(uint32_t); + + // Write the id next; + *reinterpret_cast<uint32_t*>(pkt_data) = htonl(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<uint32_t*>(pkt_data) = htonl(type); + pkt_data += sizeof(uint32_t); + + // Now the data.size() + *reinterpret_cast<uint32_t*>(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<uint8_t*>(data.data()), data.size() }, + }; + // now pkt_header has the header. + // use writev to send the actual data. + ssize_t res = TEMP_FAILURE_RETRY(writev(adb_connection_socket_, iovs, kIovSize)); + if (static_cast<size_t>(res) != (kDdmPacketHeaderSize + data.size())) { + PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)", + static_cast<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type), + res, data.size() + kDdmPacketHeaderSize); + } else { + VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu", + static_cast<char>(type >> 24), + static_cast<char>(type >> 16), + static_cast<char>(type >> 8), + static_cast<char>(type), + data.size() + kDdmPacketHeaderSize); + } +} + +void AdbConnectionState::SendAgentFds() { + // 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<int*>(CMSG_DATA(cmsg)))[0] = -1; + + int rc = TEMP_FAILURE_RETRY(recvmsg(control_sock_, &msg, 0)); + + if (rc <= 0) { + PLOG(WARNING) << "Receiving file descriptor from ADB failed (socket " << control_sock_ << ")"; + return android::base::unique_fd(-1); + } else { + VLOG(jdwp) << "Fds have been received from ADB!"; + } + + return android::base::unique_fd((reinterpret_cast<int*>(CMSG_DATA(cmsg)))[0]); +} + +bool AdbConnectionState::SetupAdbConnection() { + int sleep_ms = 500; + const int sleep_max_ms = 2*1000; + 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<const char*>(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(/* JNIEnv* */ nullptr, + MakeAgentArg(), + /* classloader */ nullptr); + if (self->IsExceptionPending()) { + LOG(ERROR) << "Failed to load agent " << agent_name_; + art::ScopedObjectAccess soa(self); + self->GetException()->Dump(); + self->ClearException(); + return; + } + agent_loaded_ = true; + } 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 0000000000..28a5a05af3 --- /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 <stdint.h> +#include <vector> +#include <limits> + +#include "android-base/unique_fd.h" + +#include "base/mutex.h" +#include "runtime_callbacks.h" + +#include <sys/socket.h> +#include <sys/un.h> +#include <jni.h> + +namespace adbconnection { + +static constexpr char kJdwpControlName[] = "\0jdwp-control"; +static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread"; + +// The default jdwp agent name. +static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so"; + +class AdbConnectionState; + +struct AdbConnectionDebuggerController : public art::DebuggerControlCallback { + explicit AdbConnectionDebuggerController(AdbConnectionState* connection) + : connection_(connection) {} + + // Begin running the debugger. + void StartDebugger() OVERRIDE; + + // The debugger should begin shutting down since the runtime is ending. + void StopDebugger() OVERRIDE; + + bool IsDebuggerConfigured() OVERRIDE; + + private: + AdbConnectionState* connection_; +}; + +struct AdbConnectionDdmCallback : public art::DdmCallback { + explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {} + + void DdmPublishChunk(uint32_t type, + const art::ArrayRef<const uint8_t>& data) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + AdbConnectionState* connection_; +}; + +class AdbConnectionState { + public: + explicit AdbConnectionState(const std::string& 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<const uint8_t>& 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<bool> shutting_down_; + + // True if we have loaded the agent library. + std::atomic<bool> agent_loaded_; + + // True if the dt_fd_forward transport is listening for a new communication channel. + std::atomic<bool> agent_listening_; + + // True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or + // the adb connection socket until connection goes away. + std::atomic<bool> agent_has_socket_; + + std::atomic<bool> sent_agent_fds_; + + std::atomic<uint32_t> 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 f5a95fa0cf..08962526dd 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 0f29f2901b..3d1f4343f1 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -23,10 +23,10 @@ include art/build/Android.common_path.mk ifneq ($(TMPDIR),) ART_HOST_TEST_DIR := $(TMPDIR)/test-art-$(shell echo $$PPID) else -# Use a BSD checksum calculated from ANDROID_BUILD_TOP and USER as one of the -# path components for the test output. This should allow us to run tests from multiple -# repositories at the same time. -ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo ${ANDROID_BUILD_TOP}-${USER} | sum | cut -d ' ' -f1) +# Use a BSD checksum calculated from PPID and USER as one of the path +# components for the test output. This should allow us to run tests from +# multiple repositories at the same time. +ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo $$PPID-${USER} | sum | cut -d ' ' -f1) endif # List of known broken tests that we won't attempt to execute. The test name must be the full diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3c8eade773..1f36cb4e46 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 @@ -251,6 +252,11 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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 := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -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-compat-dx +$$(rule_name): $$(dependencies) dx d8-compat-dx desugar $(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/Android.oat.mk b/build/Android.oat.mk index 3f9ea15fb3..517ac5c28d 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -111,6 +111,7 @@ $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \ --host --android-root=$$(HOST_OUT) \ --generate-debug-info --generate-build-id --compile-pic \ + --runtime-arg -XX:SlowDebug=true \ $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS) $$(core_oat_name): $$(core_image_name) @@ -214,6 +215,7 @@ $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \ --android-root=$$(PRODUCT_OUT)/system \ --generate-debug-info --generate-build-id --compile-pic \ + --runtime-arg -XX:SlowDebug=true \ $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1) $$(core_oat_name): $$(core_image_name) diff --git a/build/art.go b/build/art.go index 5704b43834..bf6eee6c41 100644 --- a/build/art.go +++ b/build/art.go @@ -66,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 @@ -100,6 +103,10 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { asflags = append(asflags, "-DART_MIPS32_CHECK_ALIGNMENT") } + if envTrueOrDefault(ctx, "USE_D8_DESUGAR") { + cflags = append(cflags, "-DUSE_D8_DESUGAR=1") + } + return cflags, asflags } @@ -363,3 +370,7 @@ func envTrue(ctx android.BaseContext, key string) bool { func envFalse(ctx android.BaseContext, key string) bool { return ctx.AConfig().Getenv(key) == "false" } + +func envTrueOrDefault(ctx android.BaseContext, key string) bool { + return ctx.AConfig().Getenv(key) != "false" +} diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 1536339515..5d672061df 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" @@ -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); } @@ -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 /* diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 37bdcdc5e2..d0d6bfd3ce 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" @@ -64,123 +65,30 @@ struct CmdlineType<Unit> : CmdlineTypeParser<Unit> { }; template <> -struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> { +struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> { /* - * Handle one of the JDWP name/value pairs. - * - * JDWP options are: - * help: if specified, show help message and bail - * transport: may be dt_socket or dt_shmem - * address: for dt_socket, "host:port", or just "port" when listening - * server: if "y", wait for debugger to attach; if "n", attach to debugger - * timeout: how long to wait for debugger to connect / listen - * - * Useful with server=n (these aren't supported yet): - * onthrow=<exception-name>: connect to debugger when exception thrown - * onuncaught=y|n: connect to debugger when uncaught exception thrown - * launch=<command-line>: launch the debugger itself - * - * The "transport" option is required, as is "address" if server=n. + * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of + * an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate. */ - Result Parse(const std::string& options) { - VLOG(jdwp) << "ParseJdwpOptions: " << options; - - if (options == "help") { + Result Parse(const std::string& option) { + if (option == "help") { return Result::Usage( - "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" - "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n"); - } - - const std::string s; - - std::vector<std::string> pairs; - Split(options, ',', &pairs); - - JDWP::JdwpOptions jdwp_options; - - for (const std::string& jdwp_option : pairs) { - std::string::size_type equals_pos = jdwp_option.find('='); - if (equals_pos == std::string::npos) { - return Result::Failure(s + - "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'"); - } - - Result parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos), - jdwp_option.substr(equals_pos + 1), - &jdwp_options); - if (parse_attempt.IsError()) { - // We fail to parse this JDWP option. - return parse_attempt; - } - } - - if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) { - return Result::Failure(s + "Must specify JDWP transport: " + options); - } - if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) { - return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options); - } - - return Result::Success(std::move(jdwp_options)); - } - - Result ParseJdwpOption(const std::string& name, const std::string& value, - JDWP::JdwpOptions* jdwp_options) { - if (name == "transport") { - if (value == "dt_socket") { - jdwp_options->transport = JDWP::kJdwpTransportSocket; - } else if (value == "dt_android_adb") { - jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb; - } else { - return Result::Failure("JDWP transport not supported: " + value); - } - } else if (name == "server") { - if (value == "n") { - jdwp_options->server = false; - } else if (value == "y") { - jdwp_options->server = true; - } else { - return Result::Failure("JDWP option 'server' must be 'y' or 'n'"); - } - } else if (name == "suspend") { - if (value == "n") { - jdwp_options->suspend = false; - } else if (value == "y") { - jdwp_options->suspend = true; - } else { - return Result::Failure("JDWP option 'suspend' must be 'y' or 'n'"); - } - } else if (name == "address") { - /* this is either <port> or <host>:<port> */ - std::string port_string; - jdwp_options->host.clear(); - std::string::size_type colon = value.find(':'); - if (colon != std::string::npos) { - jdwp_options->host = value.substr(0, colon); - port_string = value.substr(colon + 1); - } else { - port_string = value; - } - if (port_string.empty()) { - return Result::Failure("JDWP address missing port: " + value); - } - char* end; - uint64_t port = strtoul(port_string.c_str(), &end, 10); - if (*end != '\0' || port > 0xffff) { - return Result::Failure("JDWP address has junk in port field: " + value); - } - jdwp_options->port = port; - } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") { - /* valid but unsupported */ - LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'"; + "Example: -XjdwpProvider:none to disable JDWP\n" + "Example: -XjdwpProvider:internal for internal jdwp implementation\n" + "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n" + "Example: -XjdwpProvider:default for the default jdwp implementation" + " (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 <size_t Divisor> @@ -420,19 +328,19 @@ struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> }; template <> -struct CmdlineType<std::list<ti::Agent>> : CmdlineTypeParser<std::list<ti::Agent>> { +struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::AgentSpec>> { Result Parse(const std::string& args) { assert(false && "Use AppendValues() for an Agent list type"); return Result::Failure("Unconditional failure: Agent list must be appended: " + args); } Result ParseAndAppend(const std::string& args, - std::list<ti::Agent>& existing_value) { + std::list<ti::AgentSpec>& existing_value) { existing_value.emplace_back(args); return Result::SuccessNoValue(); } - static const char* Name() { return "std::list<ti::Agent>"; } + static const char* Name() { return "std::list<ti::AgentSpec>"; } }; template <> @@ -669,6 +577,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> { 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 37a18cb9e9..164f9c1e8f 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"], @@ -323,7 +318,7 @@ art_cc_test { "linker/linker_patch_test.cc", "linker/output_stream_test.cc", "optimizing/bounds_check_elimination_test.cc", - "optimizing/cloner_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/compiler.cc b/compiler/compiler.cc index c500921ab3..60977b6bd5 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -16,7 +16,10 @@ #include "compiler.h" -#include "base/logging.h" +#include <android-base/logging.h> + +#include "base/macros.h" +#include "dex/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 85abd6654c..b92bff61a9 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -18,7 +18,7 @@ #define ART_COMPILER_COMPILER_H_ #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "os.h" namespace art { diff --git a/compiler/debug/dwarf/writer.h b/compiler/debug/dwarf/writer.h index 95912ad6c9..afeb980352 100644 --- a/compiler/debug/dwarf/writer.h +++ b/compiler/debug/dwarf/writer.h @@ -19,8 +19,10 @@ #include <type_traits> #include <vector> + +#include <android-base/logging.h> + #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 d0c98a7b79..27b70c8caa 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<ElfTypes>* 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<uint8_t> buffer; // Small temporary buffer. diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index d5999941d7..e2bea8e096 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -27,8 +27,9 @@ #include "debug/elf_compilation_unit.h" #include "debug/elf_debug_loc_writer.h" #include "debug/method_debug_info.h" -#include "dex_file-inl.h" -#include "dex_file.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file.h" #include "heap_poisoning.h" #include "linear_alloc.h" #include "linker/elf_builder.h" @@ -48,10 +49,10 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { std::vector<const char*> names; - if (mi->code_item != nullptr) { + CodeItemDebugInfoAccessor accessor(mi->dex_file, mi->code_item); + if (accessor.HasCodeItem()) { DCHECK(mi->dex_file != nullptr); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item); - const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset); + const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset()); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. uint32_t parameters_size = DecodeUnsignedLeb128(&stream); @@ -162,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); @@ -204,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<DexRegisterMap> dex_reg_maps; - if (dex_code != nullptr && 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())); } } @@ -224,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); } @@ -244,30 +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; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code); - if (dex->DecodeDebugLocalInfo(dex_code, - debug_info_offset, - 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_); @@ -296,7 +294,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector<uint8_t> buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); @@ -461,7 +459,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector<uint8_t> buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 943e03a765..9910e7a4ce 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -24,7 +24,7 @@ #include "debug/dwarf/headers.h" #include "debug/elf_compilation_unit.h" #include "debug/src_map_elem.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "linker/elf_builder.h" #include "oat_file.h" #include "stack_map.h" @@ -60,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<dwarf::FileEntry> files; std::unordered_map<std::string, size_t> files_map; @@ -159,9 +159,9 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item); - if (!dex->DecodeDebugPositionInfo( - mi->code_item, debug_info_offset, 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; } @@ -268,7 +268,7 @@ class ElfDebugLineWriter { } std::vector<uint8_t> buffer; buffer.reserve(opcodes.data()->size() + KB); - size_t offset = builder_->GetDebugLine()->GetSize(); + size_t offset = builder_->GetDebugLine()->GetPosition(); WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_); builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size()); return buffer.size(); diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index bb856b29f4..34c2919a21 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -149,11 +149,12 @@ static std::vector<VariableLocation> 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 33c46d7e1f..a6267292bf 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -108,29 +108,32 @@ void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder, std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_size, + uint64_t text_address, size_t text_size, const ArrayRef<const MethodDebugInfo>& method_infos) { if (Is64BitInstructionSet(isa)) { return MakeMiniDebugInfoInternal<ElfTypes64>(isa, features, - rodata_size, + text_address, text_size, method_infos); } else { return MakeMiniDebugInfoInternal<ElfTypes32>(isa, features, - rodata_size, + text_address, text_size, method_infos); } } template <typename ElfTypes> -static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal( +static std::vector<uint8_t> MakeElfFileForJITInternal( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& mi) { + CHECK_EQ(mi.is_code_address_text_relative, false); + ArrayRef<const MethodDebugInfo> method_infos(&mi, 1); std::vector<uint8_t> buffer; buffer.reserve(KB); linker::VectorOutputStream out("Debug ELF file", &buffer); @@ -138,23 +141,34 @@ static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal( new linker::ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); - WriteDebugInfo(builder.get(), - method_infos, - dwarf::DW_DEBUG_FRAME_FORMAT, - false /* write_oat_patches */); + if (mini_debug_info) { + std::vector<uint8_t> 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<uint8_t> WriteDebugElfFileForMethods( +std::vector<uint8_t> MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodsInternal<ElfTypes64>(isa, features, method_infos); + return MakeElfFileForJITInternal<ElfTypes64>(isa, features, mini_debug_info, method_info); } else { - return WriteDebugElfFileForMethodsInternal<ElfTypes32>(isa, features, method_infos); + return MakeElfFileForJITInternal<ElfTypes32>(isa, features, mini_debug_info, method_info); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index d24ca9b203..a47bf076b9 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -43,14 +43,15 @@ void WriteDebugInfo( std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + uint64_t text_section_address, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos); -std::vector<uint8_t> WriteDebugElfFileForMethods( +std::vector<uint8_t> MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos); + bool mini_debug_info, + const MethodDebugInfo& method_info); std::vector<uint8_t> WriteDebugElfFileForClasses( InstructionSet isa, diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 1cdf6b0ad1..78b8e2780c 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -80,7 +80,7 @@ template <typename ElfTypes> static std::vector<uint8_t> MakeMiniDebugInfoInternal( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + typename ElfTypes::Addr text_section_address, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos) { std::vector<uint8_t> buffer; @@ -88,11 +88,9 @@ static std::vector<uint8_t> MakeMiniDebugInfoInternal( linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( new linker::ElfBuilder<ElfTypes>(isa, features, &out)); - builder->Start(); - // 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 0907e102a0..57e010f232 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -79,8 +79,9 @@ static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* 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/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index a8225fa2b4..43c8de26aa 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -21,7 +21,7 @@ #include "arch/instruction_set.h" #include "base/array_ref.h" -#include "dex_file.h" +#include "dex/dex_file.h" namespace art { namespace debug { diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index cc452fc1ae..52cb217980 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -16,16 +16,18 @@ #include "dex_to_dex_compiler.h" -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG +#include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" #include "compiled_method.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "mirror/dex_cache.h" @@ -112,7 +114,8 @@ class DexCompiler { void DexCompiler::Compile() { DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize); - IterationRange<DexInstructionIterator> instructions = unit_.GetCodeItem()->Instructions(); + IterationRange<DexInstructionIterator> instructions(unit_.GetCodeItemAccessor().begin(), + unit_.GetCodeItemAccessor().end()); for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) { const uint32_t dex_pc = it.DexPc(); Instruction* inst = const_cast<Instruction*>(&it.Inst()); @@ -294,7 +297,6 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - GetDexFile(), method_idx, unit_.GetDexCache(), unit_.GetClassLoader(), @@ -363,7 +365,7 @@ CompiledMethod* ArtCompileDEX( if (kIsDebugBuild) { // Double check that the counts line up with the size of the quicken info. size_t quicken_count = 0; - for (const DexInstructionPcPair& pair : code_item->Instructions()) { + for (const DexInstructionPcPair& pair : unit.GetCodeItemAccessor()) { if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) { ++quicken_count; } @@ -375,7 +377,7 @@ CompiledMethod* ArtCompileDEX( // Dex pc is not serialized, only used for checking the instructions. Since we access the // array based on the index of the quickened instruction, the indexes must line up perfectly. // The reader side uses the NeedsIndexForInstruction function too. - const Instruction& inst = code_item->InstructionAt(info.dex_pc); + const Instruction& inst = unit.GetCodeItemAccessor().InstructionAt(info.dex_pc); CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode(); // Add the index. quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0)); diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 87ddb395ad..80b94d2dc3 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -17,7 +17,7 @@ #ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ -#include "dex_file.h" +#include "dex/dex_file.h" #include "handle.h" #include "invoke_type.h" diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 979c4c4ce2..19b190093f 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -20,7 +20,7 @@ #include "common_compiler_test.h" #include "compiled_method-inl.h" #include "compiler_callbacks.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "handle_scope-inl.h" @@ -99,8 +99,10 @@ class DexToDexDecompilerTest : public CommonCompilerTest { if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); } - optimizer::ArtDecompileDEX( - *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true); + optimizer::ArtDecompileDEX(*updated_dex_file, + *it.GetMethodCodeItem(), + table, + /* decompile_return_instruction */ true); it.Next(); } DCHECK(!it.HasNext()); diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index b409eb2dbb..ce67b85b99 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -20,11 +20,11 @@ #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" -#include "dex_instruction_utils.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_instruction.h" +#include "dex/dex_instruction_utils.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" @@ -141,8 +141,11 @@ bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pat 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()); @@ -323,7 +326,7 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item, if (target_method->GetDeclaringClass()->IsObjectClass()) { DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID); } else { - CodeItemDataAccessor target_code_item = CodeItemDataAccessor::CreateNullable(target_method); + CodeItemDataAccessor target_code_item(target_method); if (!target_code_item.HasCodeItem()) { return false; // Native constructor? } @@ -427,7 +430,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { - CodeItemDataAccessor code_item = CodeItemDataAccessor::CreateNullable(method); + CodeItemDataAccessor code_item(method); if (!code_item.HasCodeItem()) { // Native or abstract. return false; diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h index cde2147995..837cc85456 100644 --- a/compiler/dex/inline_method_analyser.h +++ b/compiler/dex/inline_method_analyser.h @@ -19,8 +19,8 @@ #include "base/macros.h" #include "base/mutex.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "method_reference.h" /* diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc index 92b123013d..540bd0ce45 100644 --- a/compiler/dex/quick_compiler_callbacks.cc +++ b/compiler/dex/quick_compiler_callbacks.cc @@ -38,7 +38,7 @@ ClassStatus QuickCompilerCallbacks::GetPreviousClassState(ClassReference ref) { // If we don't have class unloading enabled in the compiler, we will never see class that were // previously verified. Return false to avoid overhead from the lookup in the compiler driver. if (!does_class_unloading_) { - return ClassStatus::kStatusNotReady; + return ClassStatus::kNotReady; } DCHECK(compiler_driver_ != nullptr); // In the case of the quicken filter: avoiding verification of quickened instructions, which the diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 03c90d82c8..1e0b94de81 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 <android-base/logging.h> + #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 524b0a6911..f2da3ffc2f 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -19,10 +19,11 @@ #include <algorithm> #include <memory> -#include "base/logging.h" -#include "code_item_accessors-inl.h" -#include "dex_file.h" -#include "dex_instruction-inl.h" +#include <android-base/logging.h> + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction-inl.h" #include "runtime.h" #include "verifier/method_verifier-inl.h" #include "verifier/reg_type-inl.h" diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index 64b3f448e6..2ed17f1dfd 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -20,7 +20,7 @@ #include <vector> #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "method_reference.h" #include "safe_map.h" diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index c739333cee..c8c2b6998f 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 <android-base/logging.h> + #include "compiled_method.h" #include "linker/linker_patch.h" #include "thread-current-inl.h" diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index b04392918d..294072d7e7 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -32,14 +32,16 @@ namespace art { -inline mirror::Class* CompilerDriver::ResolveClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index, +inline ObjPtr<mirror::Class> CompilerDriver::ResolveClass( + const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - mirror::Class* cls = mUnit->GetClassLinker()->ResolveType( - *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); + ObjPtr<mirror::Class> cls = + mUnit->GetClassLinker()->ResolveType(cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(cls == nullptr)) { // Clean up any exception left by type resolution. @@ -48,9 +50,11 @@ inline mirror::Class* CompilerDriver::ResolveClass( return cls; } -inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { +inline ObjPtr<mirror::Class> CompilerDriver::ResolveCompilingMethodsClass( + const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); const DexFile::MethodId& referrer_method_id = @@ -58,13 +62,13 @@ 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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexFile* dex_file, - uint32_t field_idx, bool is_static) { - DCHECK_EQ(dex_cache->GetDexFile(), dex_file); +inline ArtField* CompilerDriver::ResolveField(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + uint32_t field_idx, + bool is_static) { ArtField* resolved_field = Runtime::Current()->GetClassLinker()->ResolveField( - *dex_file, field_idx, dex_cache, class_loader, is_static); + field_idx, dex_cache, class_loader, is_static); DCHECK_EQ(resolved_field == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(resolved_field == nullptr)) { // Clean up any exception left by type resolution. @@ -79,18 +83,11 @@ inline ArtField* CompilerDriver::ResolveFieldWithDexFile( return resolved_field; } -inline ArtField* CompilerDriver::ResolveField( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, - uint32_t field_idx, bool is_static) { - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx, - is_static); -} - inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx) { + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::Class> referrer_class, + ArtField* resolved_field, + uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass(); bool fast_get = referrer_class != nullptr && @@ -112,7 +109,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(soa.Self()->IsExceptionPending()); // Clean up any exception left by type resolution. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0ca3c8f613..fe83a66d0f 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" @@ -41,13 +42,13 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_instruction-inl.h" #include "dex/dex_to_dex_compiler.h" #include "dex/verification_results.h" #include "dex/verified_method.h" #include "dex_compilation_unit.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" -#include "dex_instruction-inl.h" #include "driver/compiler_options.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" @@ -710,14 +711,14 @@ static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache, } ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - for (const DexInstructionPcPair& inst : code_item->Instructions()) { + for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(&dex_file, code_item)) { switch (inst->Opcode()) { case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: { dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) ? inst->VRegB_21c() : inst->VRegB_31c()); - mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); + ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; } @@ -949,14 +950,14 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { ArtMethod* method, std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - if (code_item == nullptr) { + if (method->GetCodeItem() == nullptr) { return; // native or abstract method } - if (code_item->tries_size_ == 0) { + CodeItemDataAccessor accessor(method); + 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); @@ -1048,22 +1049,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<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); - Handle<mirror::Class> klass(hs2.NewHandle( + ObjPtr<mirror::Class> klass = (dex_cache != nullptr) - ? class_linker->ResolveType(*dex_file, - exception_type_idx, + ? class_linker->ResolveType(exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>()) - : nullptr)); + : nullptr; if (klass == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); LOG(FATAL) << "Failed to resolve class " << descriptor; } - DCHECK(java_lang_Throwable->IsAssignableFrom(klass.Get())); + DCHECK(java_lang_Throwable->IsAssignableFrom(klass)); } // Resolving exceptions may load classes that reference more exceptions, iterate until no // more are found @@ -1367,17 +1367,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<mirror::Class> referrer_class; Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache()); { - Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader(); - resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false); + Handle<mirror::ClassLoader> class_loader = mUnit->GetClassLoader(); + resolved_field = ResolveField(soa, dex_cache, class_loader, field_idx, /* is_static */ false); referrer_class = resolved_field != nullptr - ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; + ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit) : nullptr; } bool can_link = false; if (resolved_field != nullptr && referrer_class != nullptr) { @@ -1542,7 +1543,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<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(klass != nullptr); const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile(); @@ -1610,7 +1611,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(); @@ -1636,8 +1637,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache( soa.Self(), dex_file))); // Resolve the class. - mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, - class_loader); + ObjPtr<mirror::Class> klass = + class_linker->ResolveType(class_def.class_idx_, dex_cache, class_loader); bool resolve_fields_and_methods; if (klass == nullptr) { // Class couldn't be resolved, for example, super-class is in a different dex file. Don't @@ -1663,8 +1664,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()); } @@ -1678,8 +1679,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()); } @@ -1689,7 +1690,10 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { if (resolve_fields_and_methods) { while (it.HasNextMethod()) { ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, + it.GetMemberIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, it.GetMethodInvokeType(class_def)); if (method == nullptr) { CheckAndClearResolveException(soa.Self()); @@ -1725,7 +1729,7 @@ class ResolveTypeVisitor : public CompilationVisitor { dex_file, class_loader.Get()))); ObjPtr<mirror::Class> klass = (dex_cache != nullptr) - ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) + ? class_linker->ResolveType(dex::TypeIndex(type_idx), dex_cache, class_loader) : nullptr; if (klass == nullptr) { @@ -1805,7 +1809,7 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, static void LoadAndUpdateStatus(const DexFile& dex_file, const DexFile::ClassDef& class_def, - mirror::Class::Status status, + ClassStatus status, Handle<mirror::ClassLoader> class_loader, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1864,16 +1868,16 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. ClassReference ref(dex_file, i); - mirror::Class::Status existing = mirror::Class::kStatusNotReady; + ClassStatus existing = ClassStatus::kNotReady; DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); ClassStateTable::InsertResult result = - compiled_classes_.Insert(ref, existing, mirror::Class::kStatusVerified); + compiled_classes_.Insert(ref, existing, ClassStatus::kVerified); CHECK_EQ(result, ClassStateTable::kInsertResultSuccess); } else { // Update the class status, so later compilation stages know they don't need to verify // the class. LoadAndUpdateStatus( - *dex_file, class_def, mirror::Class::kStatusVerified, class_loader, soa.Self()); + *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: @@ -1887,7 +1891,7 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // this class again. LoadAndUpdateStatus(*dex_file, class_def, - mirror::Class::kStatusRetryVerificationAtRuntime, + ClassStatus::kRetryVerificationAtRuntime, class_loader, soa.Self()); } @@ -1955,7 +1959,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); @@ -2084,7 +2088,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); @@ -2101,10 +2105,10 @@ class SetVerifiedClassVisitor : public CompilationVisitor { // Only do this if the class is resolved. If even resolution fails, quickening will go very, // very wrong. if (klass->IsResolved() && !klass->IsErroneousResolved()) { - if (klass->GetStatus() < mirror::Class::kStatusVerified) { + if (klass->GetStatus() < ClassStatus::kVerified) { ObjectLock<mirror::Class> lock(soa.Self(), klass); // Set class status to verified. - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self()); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, soa.Self()); // Mark methods as pre-verified. If we don't do this, the interpreter will run with // access checks. klass->SetSkipAccessChecksFlagOnAllMethods( @@ -2148,7 +2152,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); @@ -2181,7 +2185,7 @@ class InitializeClassVisitor : public CompilationVisitor { const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage(); const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage(); - mirror::Class::Status old_status = klass->GetStatus(); + ClassStatus old_status = klass->GetStatus(); // Don't initialize classes in boot space when compiling app image if (is_app_image && klass->IsBootStrapClassLoaded()) { // Also return early and don't store the class status in the recorded class status. @@ -2306,7 +2310,7 @@ class InitializeClassVisitor : public CompilationVisitor { // would do so they can be skipped at runtime. if (!klass->IsInitialized() && manager_->GetClassLinker()->ValidateSuperClassDescriptors(klass)) { - old_status = mirror::Class::kStatusSuperclassValidated; + old_status = ClassStatus::kSuperclassValidated; } else { soa.Self()->ClearException(); } @@ -2328,22 +2332,20 @@ class InitializeClassVisitor : public CompilationVisitor { DCHECK(!klass->IsInitialized()); StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile* dex_file = manager_->GetDexFile(); + Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache()); const DexFile::ClassDef* class_def = klass->GetClassDef(); ClassLinker* class_linker = manager_->GetClassLinker(); // Check encoded final field values for strings and intern. - annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file, - &h_dex_cache, - &class_loader, + annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache, + class_loader, manager_->GetClassLinker(), *class_def); for ( ; value_it.HasNext(); value_it.Next()) { if (value_it.GetValueType() == annotations::RuntimeEncodedStaticFieldValueIterator::kString) { // Resolve the string. This will intern the string. art::ObjPtr<mirror::String> resolved = class_linker->ResolveString( - *dex_file, dex::StringIndex(value_it.GetJavaValue().i), h_dex_cache); + dex::StringIndex(value_it.GetJavaValue().i), dex_cache); CHECK(resolved != nullptr); } } @@ -2351,16 +2353,14 @@ class InitializeClassVisitor : public CompilationVisitor { // Intern strings seen in <clinit>. ArtMethod* clinit = klass->FindClassInitializer(class_linker->GetImagePointerSize()); if (clinit != nullptr) { - const DexFile::CodeItem* code_item = clinit->GetCodeItem(); - DCHECK(code_item != nullptr); - for (const DexInstructionPcPair& inst : code_item->Instructions()) { + for (const DexInstructionPcPair& inst : clinit->DexInstructions()) { if (inst->Opcode() == Instruction::CONST_STRING) { ObjPtr<mirror::String> s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_21c()), h_dex_cache); + dex::StringIndex(inst->VRegB_21c()), dex_cache); CHECK(s != nullptr); } else if (inst->Opcode() == Instruction::CONST_STRING_JUMBO) { ObjPtr<mirror::String> s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_31c()), h_dex_cache); + dex::StringIndex(inst->VRegB_31c()), dex_cache); CHECK(s != nullptr); } } @@ -2664,7 +2664,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(); @@ -2771,36 +2771,36 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.PrettyMethod(); } -bool CompilerDriver::GetCompiledClass(const ClassReference& ref, - mirror::Class::Status* status) const { +bool CompilerDriver::GetCompiledClass(const ClassReference& ref, ClassStatus* status) const { DCHECK(status != nullptr); // The table doesn't know if something wasn't inserted. For this case it will return - // kStatusNotReady. To handle this, just assume anything we didn't try to verify is not compiled. + // ClassStatus::kNotReady. To handle this, just assume anything we didn't try to verify + // is not compiled. if (!compiled_classes_.Get(ref, status) || - *status < mirror::Class::kStatusRetryVerificationAtRuntime) { + *status < ClassStatus::kRetryVerificationAtRuntime) { return false; } return true; } -mirror::Class::Status CompilerDriver::GetClassStatus(const ClassReference& ref) const { - mirror::Class::Status status = ClassStatus::kStatusNotReady; +ClassStatus CompilerDriver::GetClassStatus(const ClassReference& ref) const { + ClassStatus status = ClassStatus::kNotReady; if (!GetCompiledClass(ref, &status)) { classpath_classes_.Get(ref, &status); } return status; } -void CompilerDriver::RecordClassStatus(const ClassReference& ref, mirror::Class::Status status) { +void CompilerDriver::RecordClassStatus(const ClassReference& ref, ClassStatus status) { switch (status) { - case mirror::Class::kStatusErrorResolved: - case mirror::Class::kStatusErrorUnresolved: - case mirror::Class::kStatusNotReady: - case mirror::Class::kStatusResolved: - case mirror::Class::kStatusRetryVerificationAtRuntime: - case mirror::Class::kStatusVerified: - case mirror::Class::kStatusSuperclassValidated: - case mirror::Class::kStatusInitialized: + case ClassStatus::kErrorResolved: + case ClassStatus::kErrorUnresolved: + case ClassStatus::kNotReady: + case ClassStatus::kResolved: + case ClassStatus::kRetryVerificationAtRuntime: + case ClassStatus::kVerified: + case ClassStatus::kSuperclassValidated: + case ClassStatus::kInitialized: break; // Expected states. default: LOG(FATAL) << "Unexpected class status for class " @@ -2812,7 +2812,7 @@ void CompilerDriver::RecordClassStatus(const ClassReference& ref, mirror::Class: ClassStateTable::InsertResult result; ClassStateTable* table = &compiled_classes_; do { - mirror::Class::Status existing = mirror::Class::kStatusNotReady; + ClassStatus existing = ClassStatus::kNotReady; if (!table->Get(ref, &existing)) { // A classpath class. if (kIsDebugBuild) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index d2141e8bc7..ef16212fb7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -31,13 +31,13 @@ #include "base/mutex.h" #include "base/timing_logger.h" #include "class_reference.h" +#include "class_status.h" #include "compiler.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "driver/compiled_method_storage.h" #include "jit/profile_compilation_info.h" #include "method_reference.h" -#include "mirror/class.h" // For mirror::Class::Status. #include "os.h" #include "safe_map.h" #include "thread_pool.h" @@ -47,6 +47,7 @@ namespace art { namespace mirror { +class Class; class DexCache; } // namespace mirror @@ -55,18 +56,21 @@ class MethodVerifier; class VerifierDepsTest; } // namespace verifier +class ArtField; class BitVector; class CompiledMethod; class CompilerOptions; class DexCompilationUnit; +template<class T> class Handle; struct InlineIGetIPutData; class InstructionSetFeatures; class InternTable; enum InvokeType : uint32_t; +class MemberOffset; +template<class MirrorType> class ObjPtr; class ParallelCompilationManager; class ScopedObjectAccess; template <class Allocator> class SrcMap; -template<class T> class Handle; class TimingLogger; class VdexFile; class VerificationResults; @@ -152,8 +156,8 @@ class CompilerDriver { std::unique_ptr<const std::vector<uint8_t>> CreateQuickResolutionTrampoline() const; std::unique_ptr<const std::vector<uint8_t>> CreateQuickToInterpreterBridge() const; - mirror::Class::Status GetClassStatus(const ClassReference& ref) const; - bool GetCompiledClass(const ClassReference& ref, mirror::Class::Status* status) const; + ClassStatus GetClassStatus(const ClassReference& ref) const; + bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const; CompiledMethod* GetCompiledMethod(MethodReference ref) const; size_t GetNonRelativeLinkerPatchCount() const; @@ -219,36 +223,33 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Resolve compiling method's class. Returns null on failure. - mirror::Class* ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) + ObjPtr<mirror::Class> ResolveCompilingMethodsClass(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* ResolveClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, dex::TypeIndex type_index, - const DexCompilationUnit* mUnit) + ObjPtr<mirror::Class> ResolveClass(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + dex::TypeIndex type_index, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a field. Returns null on failure, including incompatible class change. // NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static. - ArtField* ResolveField( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, - uint32_t field_idx, bool is_static) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Resolve a field with a given dex file. - ArtField* ResolveFieldWithDexFile( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexFile* dex_file, - uint32_t field_idx, bool is_static) + ArtField* ResolveField(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + uint32_t field_idx, + bool is_static) REQUIRES_SHARED(Locks::mutator_lock_); // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. - std::pair<bool, bool> IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx) + std::pair<bool, bool> IsFastInstanceField(ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::Class> referrer_class, + ArtField* resolved_field, + uint16_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method. Returns null on failure, including incompatible class change. @@ -270,9 +271,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_); @@ -324,7 +325,7 @@ class CompilerDriver { // according to the profile file. bool ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, uint16_t class_idx) const; - void RecordClassStatus(const ClassReference& ref, mirror::Class::Status status); + void RecordClassStatus(const ClassReference& ref, ClassStatus status); // Checks if the specified method has been verified without failures. Returns // false if the method is not in the verification results (GetVerificationResults). @@ -479,7 +480,7 @@ class CompilerDriver { GUARDED_BY(requires_constructor_barrier_lock_); // All class references that this compiler has compiled. Indexed by class defs. - using ClassStateTable = AtomicDexRefMap<ClassReference, mirror::Class::Status>; + using ClassStateTable = AtomicDexRefMap<ClassReference, ClassStatus>; ClassStateTable compiled_classes_; // All class references that are in the classpath. Indexed by class defs. ClassStateTable classpath_classes_; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 278358b250..162904c0e7 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -16,16 +16,18 @@ #include "driver/compiler_driver.h" +#include <limits> #include <stdint.h> #include <stdio.h> #include <memory> #include "art_method-inl.h" +#include "base/casts.h" #include "class_linker-inl.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "gc/heap.h" #include "handle_scope-inl.h" #include "jit/profile_compilation_info.h" @@ -344,11 +346,11 @@ class CompilerDriverVerifyTest : public CompilerDriverTest { ASSERT_NE(klass, nullptr); EXPECT_TRUE(klass->IsVerified()); - mirror::Class::Status status; + ClassStatus status; bool found = compiler_driver_->GetCompiledClass( ClassReference(&klass->GetDexFile(), klass->GetDexTypeIndex().index_), &status); ASSERT_TRUE(found); - EXPECT_EQ(status, mirror::Class::kStatusVerified); + EXPECT_EQ(status, ClassStatus::kVerified); } }; @@ -367,8 +369,8 @@ TEST_F(CompilerDriverVerifyTest, VerifyCompilation) { CheckVerifiedClass(class_loader, "LSecond;"); } -// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the -// driver. +// Test that a class of status ClassStatus::kRetryVerificationAtRuntime is indeed +// recorded that way in the driver. TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { Thread* const self = Thread::Current(); jobject class_loader; @@ -386,17 +388,19 @@ TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { callbacks_->SetDoesClassUnloading(true, compiler_driver_.get()); ClassReference ref(dex_file, 0u); // Test that the status is read from the compiler driver as expected. - for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime; - i < mirror::Class::kStatusMax; - ++i) { - const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i); + static_assert(enum_cast<size_t>(ClassStatus::kLast) < std::numeric_limits<size_t>::max(), + "Make sure incrementing the class status does not overflow."); + for (size_t i = enum_cast<size_t>(ClassStatus::kRetryVerificationAtRuntime); + i <= enum_cast<size_t>(ClassStatus::kLast); + ++i) { + const ClassStatus expected_status = enum_cast<ClassStatus>(i); // Skip unsupported status that are not supposed to be ever recorded. - if (expected_status == mirror::Class::kStatusVerifyingAtRuntime || - expected_status == mirror::Class::kStatusInitializing) { + if (expected_status == ClassStatus::kVerifyingAtRuntime || + expected_status == ClassStatus::kInitializing) { continue; } compiler_driver_->RecordClassStatus(ref, expected_status); - mirror::Class::Status status = {}; + ClassStatus status = {}; ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status)); EXPECT_EQ(status, expected_status); } diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index c0a9a05aa6..1780b1d7ed 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -20,6 +20,7 @@ #include "android-base/stringprintf.h" +#include "base/runtime_debug.h" #include "base/variant_map.h" #include "cmdline_parser.h" #include "compiler_options_map-inl.h" @@ -68,17 +69,16 @@ CompilerOptions::~CompilerOptions() { // because we don't want to include the PassManagerOptions definition from the header file. } +namespace { + +bool kEmitRuntimeReadBarrierChecks = kIsDebugBuild && + RegisterRuntimeDebugFlag(&kEmitRuntimeReadBarrierChecks); + +} // namespace + bool CompilerOptions::EmitRunTimeChecksInDebugMode() const { - // Run-time checks (e.g. Marking Register checks) are only emitted - // in debug mode, and - // - when running on device; or - // - when running on host, but only - // - when compiling the core image (which is used only for testing); or - // - when JIT compiling (only relevant for non-native methods). - // This is to prevent these checks from being emitted into pre-opted - // boot image or apps, as these are compiled with dex2oatd. - return kIsDebugBuild && - (kIsTargetBuild || IsCoreImage() || Runtime::Current()->UseJitCompilation()); + // Run-time checks (e.g. Marking Register checks) are only emitted in slow-debug mode. + return kEmitRuntimeReadBarrierChecks; } bool CompilerOptions::ParseDumpInitFailures(const std::string& option, std::string* error_msg) { diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index 7e8e812c4a..1fe30de355 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -16,6 +16,7 @@ #include "dex_compilation_unit.h" +#include "dex/code_item_accessors-inl.h" #include "mirror/dex_cache.h" #include "utils.h" @@ -38,7 +39,8 @@ DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader, dex_method_idx_(method_idx), access_flags_(access_flags), verified_method_(verified_method), - dex_cache_(dex_cache) { + dex_cache_(dex_cache), + code_item_accessor_(&dex_file, code_item) { } const std::string& DexCompilationUnit::GetSymbol() { diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 24a9a5b653..c1ae3c938b 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -20,7 +20,8 @@ #include <stdint.h> #include "base/arena_object.h" -#include "dex_file.h" +#include "dex/code_item_accessors.h" +#include "dex/dex_file.h" #include "handle.h" #include "jni.h" @@ -112,6 +113,10 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { return dex_cache_; } + const CodeItemDataAccessor& GetCodeItemAccessor() const { + return code_item_accessor_; + } + private: const Handle<mirror::ClassLoader> class_loader_; @@ -127,6 +132,8 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { const Handle<mirror::DexCache> dex_cache_; + const CodeItemDataAccessor code_item_accessor_; + std::string symbol_; }; diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index 897b50bdac..8f7ab05791 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -21,8 +21,10 @@ #include "base/enums.h" #include "class_linker.h" #include "common_runtime_test.h" -#include "dex_file-inl.h" -#include "dex_file.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_file_exception_helpers.h" #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "leb128.h" @@ -128,19 +130,18 @@ 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_, dex_->GetCodeItem(method_f_->GetCodeItemOffset())); - ASSERT_TRUE(code_item != nullptr); + ASSERT_TRUE(accessor.HasCodeItem()); - 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); - t1 = dex_->GetTryItems(*code_item, 1); - EXPECT_LE(t0->start_addr_, t1->start_addr_); + const DexFile::TryItem& t0 = accessor.TryItems().begin()[0]; + const DexFile::TryItem& t1 = accessor.TryItems().begin()[1]; + EXPECT_LE(t0.start_addr_, t1.start_addr_); { - CatchHandlerIterator iter(*code_item, 4 /* Dex PC in the first try block */); + CatchHandlerIterator iter(accessor, 4 /* Dex PC in the first try block */); EXPECT_STREQ("Ljava/io/IOException;", dex_->StringByTypeIdx(iter.GetHandlerTypeIndex())); ASSERT_TRUE(iter.HasNext()); iter.Next(); @@ -150,14 +151,14 @@ TEST_F(ExceptionTest, FindCatchHandler) { EXPECT_FALSE(iter.HasNext()); } { - CatchHandlerIterator iter(*code_item, 8 /* Dex PC in the second try block */); + CatchHandlerIterator iter(accessor, 8 /* Dex PC in the second try block */); EXPECT_STREQ("Ljava/io/IOException;", dex_->StringByTypeIdx(iter.GetHandlerTypeIndex())); ASSERT_TRUE(iter.HasNext()); iter.Next(); EXPECT_FALSE(iter.HasNext()); } { - CatchHandlerIterator iter(*code_item, 11 /* Dex PC not in any try block */); + CatchHandlerIterator iter(accessor, 11 /* Dex PC not in any try block */); EXPECT_FALSE(iter.HasNext()); } } diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index f33c5e1b97..88e3e5b230 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" @@ -162,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/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index daf64d1298..f34e9b844b 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -24,7 +24,7 @@ #include "class_linker.h" #include "common_compiler_test.h" #include "compiler.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "gtest/gtest.h" #include "indirect_reference_table.h" #include "java_vm_ext.h" @@ -299,7 +299,6 @@ class JniCompilerTest : public CommonCompilerTest { } // JNI operations after runtime start. env_ = Thread::Current()->GetJniEnv(); - library_search_path_ = env_->NewStringUTF(""); jklass_ = env_->FindClass("MyClassNatives"); ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig; @@ -380,7 +379,6 @@ class JniCompilerTest : public CommonCompilerTest { void CriticalNativeImpl(); JNIEnv* env_; - jstring library_search_path_; jmethodID jmethod_; private: @@ -660,7 +658,7 @@ void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() { std::string reason; ASSERT_TRUE(Runtime::Current()->GetJavaVM()-> - LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason)) + LoadNativeLibrary(env_, "", class_loader_, &reason)) << reason; jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24); @@ -676,7 +674,7 @@ void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() { std::string reason; ASSERT_TRUE(Runtime::Current()->GetJavaVM()-> - LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason)) + LoadNativeLibrary(env_, "", class_loader_, &reason)) << reason; jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42); diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index 3e637bcf43..54f193b551 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 <android-base/logging.h> + +#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 3afd7011ca..328ecbbc5c 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 <android-base/logging.h> + #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 55c27d1a6a..ff814c8a6b 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 <android-base/logging.h> #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 37f7d632ca..fc44927231 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -25,12 +25,12 @@ #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 "debug/dwarf/debug_frame_opcode_writer.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index 0e0716e911..5ec1addcb9 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 <android-base/logging.h> + #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 afe6a762eb..a7012aefa8 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 <android-base/logging.h> + #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 0bfcc3fb4d..ad58e3820d 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 <android-base/logging.h> + #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 ba654f4750..e5e96d01fc 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 <android-base/logging.h> + #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_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 2cb23d1710..cedbe5d97f 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -19,7 +19,7 @@ #include "base/stl_util.h" #include "compiled_method-inl.h" #include "debug/method_debug_info.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "linker/linker_patch.h" #include "linker/output_stream.h" #include "oat.h" diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 48747fc379..78755176e4 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 <sstream> + #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 b30b55e9b4..aa3cd98595 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<Section*>& 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<ElfTypes>* owner_; Elf_Shdr header_; Elf_Word section_index_; const std::string name_; const Section* const link_; - bool started_; - bool finished_; Elf_Word phdr_flags_; Elf_Word phdr_type_; @@ -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<Elf_Word>(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<Elf_Word>(kPageSize)); - DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - Elf_Word rodata_address = rodata_.GetAddress(); - Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); - Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); - Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize); - Elf_Word abiflags_size = 0; + // 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<Elf_Word>(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<Section*> 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 33e6b5ab23..659f1dc093 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 <android-base/logging.h> + +#include "base/macros.h" namespace art { namespace linker { diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 0ac149029a..6f4e7746a6 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -20,8 +20,9 @@ #include <iosfwd> #include <stdint.h> +#include <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" #include "method_reference.h" namespace art { diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc index ad298406be..f93ea7a709 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 <android-base/logging.h> + +#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/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc index 75f90e5f94..f2cae5b1d5 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 <android-base/logging.h> namespace art { namespace linker { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index a6687fe258..2b568bcffd 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -16,11 +16,34 @@ #include "block_builder.h" +#include "base/logging.h" // FOR VLOG. #include "bytecode_utils.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file_exception_helpers.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<uint32_t>(), + local_allocator->Adapter(kArenaAllocGraphBuilder)) {} + HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t dex_pc) { return MaybeCreateBlockAt(dex_pc, dex_pc); } @@ -40,30 +63,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 +99,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Iterate over all instructions and find branching instructions. Create blocks for // the locations these instructions branch to. - IterationRange<DexInstructionIterator> 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 +129,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 +150,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 +233,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<uint32_t, HBasicBlock*>& 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 +254,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 +272,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } void HBasicBlockBuilder::InsertTryBoundaryBlocks() { - if (code_item_->tries_size_ == 0) { + if (code_item_accessor_.TriesSize() == 0) { return; } @@ -269,12 +294,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(DexFile::GetTryItems(*code_item_, 0u), - code_item_->tries_size_, - 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); } } } @@ -285,7 +308,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); @@ -333,7 +356,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; } } @@ -361,13 +384,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_ != nullptr); + DCHECK(code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc)); @@ -385,7 +408,7 @@ bool HBasicBlockBuilder::Build() { } void HBasicBlockBuilder::BuildIntrinsic() { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); // Create blocks. diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h index 7d0f56db34..2c1f034d80 100644 --- a/compiler/optimizing/block_builder.h +++ b/compiler/optimizing/block_builder.h @@ -19,7 +19,8 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" -#include "dex_file.h" +#include "dex/code_item_accessors.h" +#include "dex/dex_file.h" #include "nodes.h" namespace art { @@ -28,22 +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 != nullptr ? code_item->insns_size_in_code_units_ - : /* 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<uint32_t>(), - 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. @@ -83,7 +70,7 @@ class HBasicBlockBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem* const code_item_; // null for intrinsic graph. + CodeItemDataAccessor code_item_accessor_; // null code item for intrinsic graph. ScopedArenaAllocator* const local_allocator_; ScopedArenaVector<HBasicBlock*> branch_targets_; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index d73ef1f3a1..af537dd653 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -37,7 +37,7 @@ namespace art { HGraphBuilder::HGraphBuilder(HGraph* graph, - const DexFile::CodeItem* code_item, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -47,7 +47,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), - code_item_(code_item), + code_item_accessor_(accessor), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(outer_compilation_unit), compiler_driver_(driver), @@ -57,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. @@ -69,20 +86,20 @@ 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"; + << ": " << 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"; + << ": " << code_units << " code units"; MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches); return true; } @@ -91,17 +108,17 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { } GraphAnalysisResult HGraphBuilder::BuildGraph() { - DCHECK(code_item_ != nullptr); + 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(), @@ -111,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_, @@ -150,7 +167,7 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { } void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); // Determine the number of arguments and associated vregs. @@ -170,7 +187,10 @@ void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); - HBasicBlockBuilder block_builder(graph_, dex_file_, /* code_item */ nullptr, &local_allocator); + HBasicBlockBuilder block_builder(graph_, + dex_file_, + CodeItemDebugInfoAccessor(), + &local_allocator); SsaBuilder ssa_builder(graph_, dex_compilation_unit_->GetClassLoader(), dex_compilation_unit_->GetDexCache(), @@ -180,7 +200,7 @@ void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { &block_builder, &ssa_builder, dex_file_, - /* code_item */ nullptr, + CodeItemDebugInfoAccessor(), return_type_, dex_compilation_unit_, outer_compilation_unit_, diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 0bb3a051f7..c16a3a928d 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,8 +18,9 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" -#include "dex_file-inl.h" -#include "dex_file.h" +#include "dex/code_item_accessors.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file.h" #include "driver/compiler_driver.h" #include "nodes.h" @@ -33,7 +34,7 @@ class OptimizingCompilerStats; class HGraphBuilder : public ValueObject { public: HGraphBuilder(HGraph* graph, - const DexFile::CodeItem* code_item, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -45,20 +46,9 @@ 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_(&graph->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); @@ -70,7 +60,7 @@ class HGraphBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem* const code_item_; // null for intrinsic graph. + 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/code_generator.cc b/compiler/optimizing/code_generator.cc index aff6f9f64f..07894fd1b1 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -46,6 +46,7 @@ #include "bytecode_utils.h" #include "class_linker.h" #include "compiled_method.h" +#include "dex/code_item_accessors-inl.h" #include "dex/verified_method.h" #include "driver/compiler_driver.h" #include "graph_visualizer.h" @@ -295,15 +296,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<mirror::Object>) * index; -} - -size_t CodeGenerator::GetCachePointerOffset(uint32_t index) { - PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet()); - return static_cast<size_t>(pointer_size) * index; -} - uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { return array_length->IsStringLength() ? mirror::String::CountOffset().Uint32Value() @@ -919,7 +911,8 @@ static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph, } ArenaVector<size_t> covered( loop_headers.size(), 0, graph.GetAllocator()->Adapter(kArenaAllocMisc)); - for (const DexInstructionPcPair& pair : code_item.Instructions()) { + for (const DexInstructionPcPair& pair : CodeItemInstructionAccessor(&graph.GetDexFile(), + &code_item)) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); if (instruction.IsBranch()) { @@ -981,21 +974,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 +981,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 +1001,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()); } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 08e4462356..3c5a37f958 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -388,13 +388,6 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { 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<kArenaAllocCodeGenerator> { 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 5054a299d3..13886b32b3 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -478,7 +478,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -2103,9 +2103,8 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod // TODO(vixl): Let the MacroAssembler handle MemOperand. __ Add(temp, class_reg, status_offset); __ Ldarb(temp, HeapOperand(temp)); - __ Cmp(temp, mirror::Class::kStatusInitialized); - __ B(ne, slow_path->GetEntryLabel()); - // Use Bne instead of Blt because ARM64 doesn't have Ldarsb. + __ Cmp(temp, enum_cast<>(ClassStatus::kInitialized)); + __ B(lo, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } @@ -3822,11 +3821,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: @@ -3875,13 +3875,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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()) { @@ -3891,13 +3893,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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; @@ -3907,7 +3911,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); @@ -3920,13 +3924,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -3937,7 +3943,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); @@ -3950,13 +3956,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -3967,7 +3975,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)); @@ -4048,26 +4056,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()); @@ -4098,18 +4088,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); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e53773c73d..f92c94fda7 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -20,7 +20,7 @@ #include "arch/arm64/quick_method_frame_info_arm64.h" #include "code_generator.h" #include "common_arm64.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 3f8f0c44f3..7f8353312f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -619,7 +619,7 @@ class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -1128,7 +1128,7 @@ class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL // // Note that this field could also hold a different object, if // another thread had concurrently changed it. In that case, the - // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set + // LDREX/CMP/BNE sequence of instructions in the compare-and-set // (CAS) operation below would abort the CAS, leaving the field // as-is. __ Cmp(temp1_, ref_reg); @@ -1168,28 +1168,16 @@ class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL // tmp = [r_ptr] - expected; // } while (tmp == 0 && failure([r_ptr] <- r_new_value)); - vixl32::Label loop_head, exit_loop; + vixl32::Label loop_head, comparison_failed, exit_loop; __ Bind(&loop_head); - __ Ldrex(tmp, MemOperand(tmp_ptr)); - - __ Subs(tmp, tmp, expected); - - { - ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - - __ it(ne); - __ clrex(ne); - } - - __ B(ne, &exit_loop, /* far_target */ false); - + __ Cmp(tmp, expected); + __ B(ne, &comparison_failed, /* far_target */ false); __ Strex(tmp, value, MemOperand(tmp_ptr)); - __ Cmp(tmp, 1); - __ B(eq, &loop_head, /* far_target */ false); - + __ CompareAndBranchIfZero(tmp, &exit_loop, /* far_target */ false); + __ B(&loop_head); + __ Bind(&comparison_failed); + __ Clrex(); __ Bind(&exit_loop); if (kPoisonHeapReferences) { @@ -7185,12 +7173,12 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - GetAssembler()->LoadFromOffset(kLoadSignedByte, + GetAssembler()->LoadFromOffset(kLoadUnsignedByte, temp, class_reg, mirror::Class::StatusOffset().Int32Value()); - __ Cmp(temp, mirror::Class::kStatusInitialized); - __ B(lt, slow_path->GetEntryLabel()); + __ Cmp(temp, enum_cast<>(ClassStatus::kInitialized)); + __ B(lo, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we may be in a situation where caches are not synced // properly. Therefore, we do a memory fence. __ Dmb(ISH); @@ -7377,11 +7365,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: @@ -7434,13 +7423,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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 @@ -7467,13 +7458,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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; @@ -7483,7 +7476,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); @@ -7493,13 +7486,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -7510,7 +7505,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); @@ -7547,13 +7542,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -7564,7 +7561,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); @@ -7654,26 +7651,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()); @@ -7702,18 +7681,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 d6922d2f3f..ebe252a9c8 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1095,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>(); - FRegister f2 = loc2.AsFpuRegister<FRegister>(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, f2); - __ MovS(f2, f1); - __ MovS(f1, FTMP); + if (codegen_->GetGraph()->HasSIMD()) { + __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast<VectorRegister>(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, f2); - __ MovD(f2, f1); - __ MovD(f1, FTMP); + FRegister f1 = loc1.AsFpuRegister<FRegister>(); + FRegister f2 = loc2.AsFpuRegister<FRegister>(); + 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())) { @@ -1152,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<Register>() : loc2.AsRegister<Register>(); @@ -1174,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<VectorRegister>(FTMP), VectorRegisterFrom(fp_loc)); + __ LoadQFromOffset(fp_loc.AsFpuRegister<FRegister>(), SP, offset); + __ StoreQToOffset(FTMP, SP, offset); } else if (loc1.IsFpuRegister() || loc2.IsFpuRegister()) { FRegister reg = loc1.IsFpuRegister() ? loc1.AsFpuRegister<FRegister>() : loc2.AsFpuRegister<FRegister>(); @@ -1225,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_; @@ -1790,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; @@ -1888,9 +1915,9 @@ void CodeGeneratorMIPS::GenerateInvokeRuntime(int32_t entry_point_offset, bool d void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg) { - __ LoadFromOffset(kLoadSignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); - __ LoadConst32(AT, mirror::Class::kStatusInitialized); - __ Blt(TMP, AT, slow_path->GetEntryLabel()); + __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); + __ LoadConst32(AT, enum_cast<>(ClassStatus::kInitialized)); + __ Bltu(TMP, AT, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we need to ensure consistent memory ordering. __ Sync(0); __ Bind(slow_path->GetExitLabel()); @@ -1941,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()); @@ -1950,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) @@ -1988,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: { @@ -2019,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; } @@ -3103,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>(); - Register length = locations->InAt(1).AsRegister<Register>(); + 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<Register>(); + 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<Register>(); + 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<Register>(); + __ Bgeu(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. @@ -3650,8 +3774,12 @@ void InstructionCodeGeneratorMIPS::DivRemByPowerOfTwo(HBinaryOperation* instruct if (IsUint<16>(abs_imm - 1)) { __ Andi(out, out, abs_imm - 1); } else { - __ Sll(out, out, 32 - ctz_imm); - __ Srl(out, out, 32 - ctz_imm); + if (codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2()) { + __ Ins(out, ZERO, ctz_imm, 32 - ctz_imm); + } else { + __ Sll(out, out, 32 - ctz_imm); + __ Srl(out, out, 32 - ctz_imm); + } } __ Subu(out, out, TMP); } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 7845e312cb..32b3e4221f 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -18,7 +18,7 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS_H_ #include "code_generator.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.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 ee33b3f335..3ea7b827bb 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1061,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<int>(reg)); } @@ -1370,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(); @@ -1382,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>(); - FpuRegister r2 = loc2.AsFpuRegister<FpuRegister>(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, r1); - __ MovS(r1, r2); - __ MovS(r2, FTMP); + if (GetGraph()->HasSIMD()) { + __ MoveV(static_cast<VectorRegister>(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast<VectorRegister>(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, r1); - __ MovD(r1, r2); - __ MovD(r2, FTMP); + FpuRegister r1 = loc1.AsFpuRegister<FpuRegister>(); + FpuRegister r2 = loc2.AsFpuRegister<FpuRegister>(); + 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 @@ -1421,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<FpuRegister>(), + SP, + mem_loc.GetStackIndex()); + __ MoveV(VectorRegisterFrom(fp_reg_loc), static_cast<VectorRegister>(FTMP)); } else { LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2; } @@ -1653,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; @@ -1730,9 +1761,9 @@ void CodeGeneratorMIPS64::GenerateInvokeRuntime(int32_t entry_point_offset) { void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg) { - __ LoadFromOffset(kLoadSignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); - __ LoadConst32(AT, mirror::Class::kStatusInitialized); - __ Bltc(TMP, AT, slow_path->GetEntryLabel()); + __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); + __ LoadConst32(AT, enum_cast<>(ClassStatus::kInitialized)); + __ Bltuc(TMP, AT, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we need to ensure consistent memory ordering. __ Sync(0); __ Bind(slow_path->GetExitLabel()); @@ -1793,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) @@ -1855,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; @@ -2614,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>(); - GpuRegister length = locations->InAt(1).AsRegister<GpuRegister>(); + 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<GpuRegister>(); + 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<GpuRegister>(); + 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<GpuRegister>(); + __ Bgeuc(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. @@ -3143,12 +3311,7 @@ void InstructionCodeGeneratorMIPS64::DivRemByPowerOfTwo(HBinaryOperation* instru __ Sra(TMP, dividend, 31); __ Srl(TMP, TMP, 32 - ctz_imm); __ Addu(out, dividend, TMP); - if (IsUint<16>(abs_imm - 1)) { - __ Andi(out, out, abs_imm - 1); - } else { - __ Sll(out, out, 32 - ctz_imm); - __ Srl(out, out, 32 - ctz_imm); - } + __ Ins(out, ZERO, ctz_imm, 32 - ctz_imm); __ Subu(out, out, TMP); } } else { @@ -3167,17 +3330,7 @@ void InstructionCodeGeneratorMIPS64::DivRemByPowerOfTwo(HBinaryOperation* instru __ Dsrl32(TMP, TMP, 32 - ctz_imm); } __ Daddu(out, dividend, TMP); - if (IsUint<16>(abs_imm - 1)) { - __ Andi(out, out, abs_imm - 1); - } else { - if (ctz_imm > 32) { - __ Dsll(out, out, 64 - ctz_imm); - __ Dsrl(out, out, 64 - ctz_imm); - } else { - __ Dsll32(out, out, 32 - ctz_imm); - __ Dsrl32(out, out, 32 - ctz_imm); - } - } + __ DblIns(out, ZERO, ctz_imm, 64 - ctz_imm); __ Dsubu(out, out, TMP); } } diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 2a95b3775d..d479410f07 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 96fe2a17e6..dd47a1fc6c 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 <android-base/logging.h> + +#include "nodes.h" namespace art { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2e8170ecc4..68532386e1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -317,7 +317,14 @@ class TypeCheckSlowPathX86 : public SlowPathCode { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(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<Register>()); + } + + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -5732,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<Register>(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<Register>(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) { @@ -5800,7 +5801,7 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movss(destination.AsFpuRegister<XmmRegister>(), 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()) { @@ -5811,11 +5812,15 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movsd(destination.AsFpuRegister<XmmRegister>(), 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<XmmRegister>(), Address(ESP, source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister<XmmRegister>(), 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()) { @@ -5915,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()); @@ -5925,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<Register>(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); - __ movl(static_cast<Register>(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); - __ movl(Address(ESP, mem2 + stack_offset), static_cast<Register>(ensure_scratch1.GetRegister())); - __ movl(Address(ESP, mem1 + stack_offset), static_cast<Register>(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<Register>(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); + __ movl(static_cast<Register>(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); + __ movl(Address(ESP, mem2 + stack_offset), static_cast<Register>(ensure_scratch1.GetRegister())); + __ movl(Address(ESP, mem1 + stack_offset), static_cast<Register>(ensure_scratch2.GetRegister())); + stack_offset += kX86WordSize; + } } void ParallelMoveResolverX86::EmitSwap(size_t index) { @@ -5947,7 +5966,7 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange(destination.AsRegister<Register>(), 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()); @@ -5983,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<XmmRegister>(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister<XmmRegister>(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented: source: " << source << ", destination: " << destination; } @@ -6196,8 +6220,8 @@ void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) { void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( SlowPathCode* slow_path, Register class_reg) { __ cmpb(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), - Immediate(mirror::Class::kStatusInitialized)); - __ j(kLess, slow_path->GetEntryLabel()); + Immediate(enum_cast<>(ClassStatus::kInitialized))); + __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); // No need for memory fence, thanks to the X86 memory model. } @@ -6369,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); @@ -6383,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: @@ -6435,12 +6460,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister<Register>()); } else { @@ -6456,12 +6483,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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; @@ -6471,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); @@ -6490,12 +6519,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -6511,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`. @@ -6525,12 +6556,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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()) { @@ -6546,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); @@ -6630,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()); @@ -6691,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); @@ -6849,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<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference<Class> */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister<Register>(), 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<Register>(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister<Register>(), - 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<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference<Class> */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister<Register>()); + // 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<Register>(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<Register>(), + 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<Register>()); break; } } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 176e4dfda0..0082853184 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -20,7 +20,7 @@ #include "arch/x86/instruction_set_features_x86.h" #include "base/enums.h" #include "code_generator.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.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 e25688c9a3..1f8d822507 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -337,7 +337,14 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(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<CpuRegister>()); + } + + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -5220,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<XmmRegister>(), - Address(CpuRegister(RSP), source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister<XmmRegister>(), + 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()) { @@ -5290,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); @@ -5315,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); @@ -5340,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(); @@ -5352,13 +5383,13 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange32(destination.AsRegister<CpuRegister>(), 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<CpuRegister>(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsRegister()) { Exchange64(destination.AsRegister<CpuRegister>(), 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<XmmRegister>()); __ movaps(source.AsFpuRegister<XmmRegister>(), destination.AsFpuRegister<XmmRegister>()); @@ -5371,6 +5402,12 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { Exchange64(source.AsFpuRegister<XmmRegister>(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) { Exchange64(destination.AsFpuRegister<XmmRegister>(), source.GetStackIndex()); + } else if (source.IsSIMDStackSlot() && destination.IsSIMDStackSlot()) { + ExchangeMemory64(destination.GetStackIndex(), source.GetStackIndex(), 2); + } else if (source.IsFpuRegister() && destination.IsSIMDStackSlot()) { + Exchange128(source.AsFpuRegister<XmmRegister>(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister<XmmRegister>(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination; } @@ -5389,8 +5426,8 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) { void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( SlowPathCode* slow_path, CpuRegister class_reg) { __ cmpb(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), - Immediate(mirror::Class::kStatusInitialized)); - __ j(kLess, slow_path->GetEntryLabel()); + Immediate(enum_cast<>(ClassStatus::kInitialized))); + __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); // No need for memory fence, thanks to the x86-64 memory model. } @@ -5729,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; } @@ -5756,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: @@ -5811,12 +5849,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister<CpuRegister>()); } else { @@ -5837,12 +5877,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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; @@ -5852,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); @@ -5871,12 +5913,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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); @@ -5892,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`. @@ -5906,12 +5950,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference<Class> */ 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()) { @@ -5927,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); @@ -6011,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()); @@ -6075,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); @@ -6233,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<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference<Class> */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), 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<CpuRegister>(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister<CpuRegister>(), - 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<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference<Class> */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister<CpuRegister>()); + // 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<CpuRegister>(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister<CpuRegister>(), + 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<CpuRegister>()); break; } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 00c5c27470..e86123ef01 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/codegen_test.cc b/compiler/optimizing/codegen_test.cc index ba431a5b08..6eda289861 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -20,8 +20,8 @@ #include "base/macros.h" #include "builder.h" #include "codegen_test_utils.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index bb586bf096..6f11e628ee 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/data_type.h b/compiler/optimizing/data_type.h index d253036479..548fe28cee 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/bit_utils.h" namespace art { diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc index 6bf3a5943f..572466eec8 100644 --- a/compiler/optimizing/dominator_test.cc +++ b/compiler/optimizing/dominator_test.cc @@ -16,7 +16,7 @@ #include "base/arena_allocator.h" #include "builder.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc index c91752855b..b799fb4688 100644 --- a/compiler/optimizing/find_loops_test.cc +++ b/compiler/optimizing/find_loops_test.cc @@ -16,8 +16,8 @@ #include "base/arena_allocator.h" #include "builder.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "pretty_printer.h" diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 813772e9af..71c394ec1f 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -301,8 +301,11 @@ class ValueSet : public ArenaObject<kArenaAllocGvn> { // 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/inliner.cc b/compiler/optimizing/inliner.cc index 2444e43d64..7a66d807cf 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<mirror::Class> resolved = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> resolved = compilation_unit.GetClassLinker()->LookupResolvedType( index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); if (resolved != cls) { index = dex::TypeIndex::Invalid(); @@ -682,7 +682,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( << "is invalid in location" << dex_cache->GetDexFile()->GetLocation(); return kInlineCacheNoData; } - ObjPtr<mirror::Class> clazz = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType( class_ref.type_index, dex_cache, caller_compilation_unit_.GetClassLoader().Get()); @@ -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. @@ -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,26 +1296,27 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); } return_replacement = new_invoke; - // Directly check if the new virtual can be recognized as an intrinsic. - // This way, we avoid running a full recognition pass just to detect - // these relative rare cases. - bool wrong_invoke_type = false; - if (IntrinsicsRecognizer::Recognize(new_invoke, &wrong_invoke_type)) { - MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); - } + // 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 @@ -1342,26 +1381,26 @@ 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) { + 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) { + if (accessor.TriesSize() != 0) { LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << method->PrettyMethod() << " is not inlined because of try block"; return false; @@ -1621,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<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), @@ -1675,7 +1715,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } HGraphBuilder builder(callee_graph, - code_item, + code_item_accessor, &dex_compilation_unit, &outer_compilation_unit_, compiler_driver_, @@ -1893,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_); @@ -1928,6 +1968,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, return; } + CodeItemDataAccessor accessor(&callee_graph->GetDexFile(), code_item); HInliner inliner(callee_graph, outermost_graph_, codegen_, @@ -1936,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 042eee3204..e81d97b0a8 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -17,7 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INLINER_H_ #define ART_COMPILER_OPTIMIZING_INLINER_H_ -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "invoke_type.h" #include "jit/profile_compilation_info.h" #include "optimization.h" diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 978d0c2225..72a93c1f77 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -23,7 +23,7 @@ #include "bytecode_utils.h" #include "class_linker.h" #include "data_type-inl.h" -#include "dex_instruction-inl.h" +#include "dex/dex_instruction-inl.h" #include "driver/compiler_driver-inl.h" #include "driver/dex_compilation_unit.h" #include "driver/compiler_options.h" @@ -39,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); } @@ -273,7 +311,7 @@ static bool IsBlockPopulated(HBasicBlock* block) { } bool HInstructionBuilder::Build() { - DCHECK(code_item_ != nullptr); + DCHECK(code_item_accessor_.HasCodeItem()); locals_for_.resize( graph_->GetBlocks().size(), ScopedArenaVector<HInstruction*>(local_allocator_->Adapter(kArenaAllocGraphBuilder))); @@ -323,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; @@ -367,7 +405,7 @@ bool HInstructionBuilder::Build() { } void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(method->IsIntrinsic()); locals_for_.resize( @@ -442,17 +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(); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_); - dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(code_item_accessor_.DebugInfoOffset(), + Callback::Position, + locations); // Instruction-specific tweaks. - IterationRange<DexInstructionIterator> 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. @@ -461,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; @@ -796,7 +833,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - *dex_compilation_unit_->GetDexFile(), method_idx, dex_compilation_unit_->GetDexCache(), class_loader, @@ -831,7 +867,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } ObjPtr<mirror::Class> 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()); @@ -1128,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<mirror::Class> to_test, ObjPtr<mirror::Class> super_class) REQUIRES_SHARED(Locks::mutator_lock_) { return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class); } @@ -1351,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); @@ -1422,8 +1459,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio return true; } -static mirror::Class* GetClassFrom(CompilerDriver* driver, - const DexCompilationUnit& compilation_unit) { +static ObjPtr<mirror::Class> GetClassFrom(CompilerDriver* driver, + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); @@ -1431,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<mirror::Class> HInstructionBuilder::GetOutermostCompilingClass() const { return GetClassFrom(compiler_driver_, *outer_compilation_unit_); } -mirror::Class* HInstructionBuilder::GetCompilingClass() const { +ObjPtr<mirror::Class> HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } @@ -1482,12 +1519,10 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> 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(); @@ -1523,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(); @@ -1537,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); @@ -1555,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; @@ -1591,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, @@ -1711,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<const Instruction::ArrayDataPayload*>(code_item_->insns_ + payload_offset); + reinterpret_cast<const Instruction::ArrayDataPayload*>( + code_item_accessor_.Insns() + payload_offset); const uint8_t* data = payload->data; uint32_t element_count = payload->element_count; @@ -1799,6 +1834,17 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> 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(); @@ -1811,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<mirror::Class> compiling_class = GetCompilingClass(); if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { needs_access_check = false; } @@ -1856,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; } @@ -2058,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); @@ -2083,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); @@ -2758,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; @@ -2778,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; @@ -2791,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; } @@ -2804,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; } @@ -2837,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; } @@ -2930,7 +2970,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, ObjPtr<mirror::Class> 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 f551ac4280..708a09711a 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -20,8 +20,9 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "data_type.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/code_item_accessors.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "handle.h" #include "nodes.h" #include "quicken_info.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,30 +60,7 @@ 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); @@ -175,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, @@ -240,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, @@ -253,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<mirror::Class> GetOutermostCompilingClass() const; // Returns the class whose method is being compiled. - mirror::Class* GetCompilingClass() const; + ObjPtr<mirror::Class> GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; @@ -328,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* const code_item_; // null for intrinsic graph. + 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 bd20d28992..a42a85dc1d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1081,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<HInstruction*>& 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(); @@ -1168,16 +1220,13 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct RecordSimplification(); return; } - } else if (input->IsIntConstant()) { - // Try to eliminate type conversion on int constant whose value falls into - // the range of the result type. - int32_t value = input->AsIntConstant()->GetValue(); - if (DataType::IsTypeConversionImplicit(value, result_type)) { - instruction->ReplaceWith(input); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } + } + + if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) { + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; } } @@ -2045,7 +2094,9 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { optimizations.SetArgumentIsString(); } else if (kUseReadBarrier) { DCHECK(instruction->GetResolvedMethod() != nullptr); - DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass()); + 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. @@ -2276,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/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 77199242f5..6928b70df7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -137,7 +137,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) case kVirtual: // Call might be devirtualized. - return (invoke_type == kVirtual || invoke_type == kDirect); + return (invoke_type == kVirtual || invoke_type == kDirect || invoke_type == kInterface); case kSuper: case kInterface: @@ -148,8 +148,12 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) UNREACHABLE(); } -bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) { - ArtMethod* art_method = invoke->GetResolvedMethod(); +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; @@ -182,7 +186,7 @@ void IntrinsicsRecognizer::Run() { HInstruction* inst = inst_it.Current(); if (inst->IsInvoke()) { bool wrong_invoke_type = false; - if (Recognize(inst->AsInvoke(), &wrong_invoke_type)) { + if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } else if (wrong_invoke_type) { LOG(WARNING) diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index c07a99032a..62991435c7 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -47,7 +47,7 @@ class IntrinsicsRecognizer : public HOptimization { // 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, /*out*/ bool* wrong_invoke_type) + static bool Recognize(HInvoke* invoke, ArtMethod* method, /*out*/ bool* wrong_invoke_type) REQUIRES_SHARED(Locks::mutator_lock_); static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7af1a20f98..d3a0376e9c 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/linearize_test.cc b/compiler/optimizing/linearize_test.cc index b2a9c0a8e7..43b63a73ef 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -21,8 +21,8 @@ #include "builder.h" #include "code_generator.h" #include "code_generator_x86.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "driver/compiler_options.h" #include "graph_visualizer.h" #include "nodes.h" diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index ddcad5aed8..e45d7c820c 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -19,8 +19,8 @@ #include "builder.h" #include "code_generator.h" #include "code_generator_x86.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 3eadc8f8fc..35bc4ff8b3 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -19,8 +19,8 @@ #include "builder.h" #include "code_generator.h" #include "code_generator_x86.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 89ad85e0b4..88326d321b 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); } @@ -328,8 +419,7 @@ class LSEVisitor : public HGraphDelegateVisitor { 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; } @@ -342,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) { @@ -362,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); } } @@ -385,6 +476,8 @@ class LSEVisitor : public HGraphDelegateVisitor { 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( @@ -679,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/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1ca096035e..3dc1ef7534 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1749,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_test.cc b/compiler/optimizing/loop_optimization_test.cc index 4e1857df5b..db8368986c 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 fa580d9bed..727431a493 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(); @@ -1403,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<int8_t>(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc()); case DataType::Type::kInt64: return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc()); case DataType::Type::kFloat32: @@ -1415,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<int8_t>(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc()); case DataType::Type::kInt32: return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc()); case DataType::Type::kFloat32: @@ -2814,21 +2931,6 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadClass::SetLoadKind(LoadKind load_kind) { - SetPackedField<LoadKindField>(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: @@ -2871,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<LoadKindField>(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: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 66d5bfea32..d4382c6b4c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -30,8 +30,8 @@ #include "base/transform_array_ref.h" #include "data_type.h" #include "deoptimization_kind.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -423,6 +423,17 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { 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() { @@ -4614,7 +4625,6 @@ class HInvokeInterface FINAL : public HInvoke { } uint32_t GetImtIndex() const { return imt_index_; } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } DECLARE_INSTRUCTION(InvokeInterface); @@ -5787,10 +5797,10 @@ 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<kFlagIsStringCharAt>(string_char_at); + SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } @@ -6062,6 +6072,20 @@ class HLoadClass FINAL : public HInstruction { 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<LoadKindField>(load_kind); + if (load_kind != LoadKind::kRuntimeCall && load_kind != LoadKind::kReferrersClass) { + special_input_ = HUserRecord<HInstruction*>(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, // including literal pool loads, which are PC-relative too. @@ -6209,6 +6233,21 @@ class HLoadString FINAL : public HInstruction { 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<LoadKindField>(load_kind); + if (load_kind != LoadKind::kRuntimeCall) { + special_input_ = HUserRecord<HInstruction*>(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, // including literal pool loads, which are PC-relative too. @@ -6231,7 +6270,7 @@ 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); } @@ -6562,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, @@ -6571,7 +6610,13 @@ class HInstanceOf FINAL : public HExpression<2> { SetPackedField<TypeCheckKindField>(check_kind); SetPackedFlag<kFlagMustDoNullCheck>(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; } @@ -6665,14 +6710,20 @@ class HBoundType FINAL : public HExpression<1> { 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<TypeCheckKindField>(check_kind); SetPackedFlag<kFlagMustDoNullCheck>(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; } diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 59d5b9f847..87dff8403b 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: @@ -879,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]); } } diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 7edb642c5b..92b427cafa 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -39,6 +39,7 @@ #include "constant_folding.h" #include "constructor_fence_redundancy_elimination.h" #include "dead_code_elimination.h" +#include "dex/code_item_accessors-inl.h" #include "driver/dex_compilation_unit.h" #include "gvn.h" #include "induction_var_analysis.h" @@ -241,7 +242,8 @@ ArenaVector<HOptimization*> ConstructOptimizations( opt = new (allocator) HDeadCodeElimination(graph, stats, name); break; case OptimizationPass::kInliner: { - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(dex_compilation_unit.GetDexFile(), + dex_compilation_unit.GetCodeItem()); opt = new (allocator) HInliner(graph, // outer_graph graph, // outermost_graph codegen, @@ -250,7 +252,7 @@ ArenaVector<HOptimization*> ConstructOptimizations( driver, handles, stats, - number_of_dex_registers, + accessor.RegistersSize(), /* total_number_of_instructions */ 0, /* parent */ nullptr, /* depth */ 0, @@ -258,8 +260,7 @@ ArenaVector<HOptimization*> ConstructOptimizations( break; } case OptimizationPass::kSharpening: - opt = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles, name); + opt = new (allocator) HSharpening(graph, codegen, driver, name); break; case OptimizationPass::kSelectGenerator: opt = new (allocator) HSelectGenerator(graph, handles, stats, name); diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index 4ad29961be..e2b2106f65 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -18,6 +18,7 @@ #include <vector> #include "arch/instruction_set.h" +#include "base/runtime_debug.h" #include "cfi_test.h" #include "driver/compiler_options.h" #include "gtest/gtest.h" @@ -56,6 +57,9 @@ class OptimizingCFITest : public CFITest { ArenaAllocator* GetAllocator() { return pool_and_allocator_.GetAllocator(); } void SetUpFrame(InstructionSet isa) { + // Ensure that slow-debug is off, so that there is no unexpected read-barrier check emitted. + SetRuntimeDebugFlagsEnabled(false); + // Setup simple context. std::string error; isa_features_ = InstructionSetFeatures::FromVariant(isa, "default", &error); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 73c72fc57a..b64f82caee 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -36,9 +36,9 @@ #include "compiler.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dex/dex_file_types.h" #include "dex/verification_results.h" #include "dex/verified_method.h" -#include "dex_file_types.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" @@ -766,11 +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)) { + && (CodeItemInstructionAccessor(&dex_file, code_item).InsnsSizeInCodeUnits() > + kSpaceFilterOptimizingThreshold)) { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; } + CodeItemDebugInfoAccessor code_item_accessor(&dex_file, code_item); HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -814,7 +816,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VLOG(compiler) << "Building " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, - code_item, + code_item_accessor, &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -932,7 +934,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, - /* code_item */ nullptr, + CodeItemDebugInfoAccessor(), // Null code item. &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -1224,7 +1226,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1244,10 +1246,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = nullptr; info.cfi = jni_compiled_method.GetCfi(); - std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef<const debug::MethodDebugInfo>(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } @@ -1352,7 +1357,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1372,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<const uint8_t>(*codegen->GetAssembler()->cfi().data()); - std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef<const debug::MethodDebugInfo>(&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 a2e92d2931..32a94ab5e4 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -23,6 +23,7 @@ #include <type_traits> #include "atomic.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "globals.h" namespace art { diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 158c252f45..661abb125c 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -20,8 +20,9 @@ #include "base/scoped_arena_allocator.h" #include "builder.h" #include "common_compiler_test.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "driver/dex_compilation_unit.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" @@ -145,7 +146,8 @@ class OptimizingUnitTest : public CommonCompilerTest { /* access_flags */ 0u, /* verified_method */ nullptr, handles_->NewHandle<mirror::DexCache>(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/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 1ed190d328..f843c008d8 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -16,7 +16,7 @@ #include "prepare_for_register_allocation.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "jni_internal.h" #include "optimizing_compiler_stats.h" #include "well_known_classes.h" diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 4aec6d3999..4fc7fe9427 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -18,8 +18,8 @@ #include "base/arena_allocator.h" #include "builder.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 7246129e25..8bb124e066 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<mirror::ClassLoader> loader(hs.NewHandle<mirror::ClassLoader>(nullptr)); ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - 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<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + ObjPtr<mirror::Class> 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/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 69ed8c7fcc..3748d599a3 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -21,9 +21,9 @@ #include "builder.h" #include "code_generator.h" #include "code_generator_x86.h" -#include "dex_file.h" -#include "dex_file_types.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction.h" #include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index bb7c353bc2..dfa077f7de 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; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 32f161f26a..f71cb5b784 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/sharpening.cc b/compiler/optimizing/sharpening.cc index e46c9a7081..1e49411c72 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<mirror::Class> 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<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() + Handle<mirror::DexCache> 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<mirror::String> 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<HLoadString::LoadKind>(-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 bb1954eeeb..6df7d6d91e 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -34,26 +34,29 @@ class HSharpening : public HOptimization { public: HSharpening(HGraph* graph, CodeGenerator* codegen, - const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles, 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, @@ -61,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 e4edbfdc24..cb384768b7 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 9ab7a89b33..f6bd05269e 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/ssa_test.cc b/compiler/optimizing/ssa_test.cc index e08904e84b..77e70d733e 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -18,8 +18,8 @@ #include "base/arena_allocator.h" #include "builder.h" -#include "dex_file.h" -#include "dex_instruction.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "pretty_printer.h" diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 4f43eb374c..7010e3f380 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -18,7 +18,7 @@ #include "art_method-inl.h" #include "base/stl_util.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "optimizing/optimizing_compiler.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/compiler/optimizing/cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc index d34dd81767..fd77eb81fc 100644 --- a/compiler/optimizing/cloner_test.cc +++ b/compiler/optimizing/superblock_cloner_test.cc @@ -24,9 +24,9 @@ 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 ClonerTest : public OptimizingUnitTest { +class SuperblockClonerTest : public OptimizingUnitTest { public: - ClonerTest() + SuperblockClonerTest() : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, @@ -154,7 +154,7 @@ class ClonerTest : public OptimizingUnitTest { HInstruction* parameter_; }; -TEST_F(ClonerTest, IndividualInstrCloner) { +TEST_F(SuperblockClonerTest, IndividualInstrCloner) { HBasicBlock* header = nullptr; HBasicBlock* loop_body = nullptr; diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc index 88336b0009..7e83f8ce5f 100644 --- a/compiler/optimizing/suspend_check_test.cc +++ b/compiler/optimizing/suspend_check_test.cc @@ -15,7 +15,7 @@ */ #include "builder.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "pretty_printer.h" diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h index 1a10e4c799..64c1eb5022 100644 --- a/compiler/trampolines/trampoline_compiler.h +++ b/compiler/trampolines/trampoline_compiler.h @@ -21,6 +21,7 @@ #include <vector> #include "driver/compiler_driver.h" +#include "offsets.h" namespace art { diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h index 0e73e6bf9e..1377e64073 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 <android-base/logging.h> + #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 5b87e3e7f8..66252bed86 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -21,9 +21,10 @@ #include <iosfwd> +#include <android-base/logging.h> + #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 c13c9af819..4bc5d69f4d 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 <android-base/logging.h> + #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 2be2d5638e..26f23b2ed6 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 <android-base/logging.h> + #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 bb989588d6..c83fd4404a 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 e5ec24add0..8983af2677 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -21,8 +21,10 @@ #include <memory> #include <vector> +#include <android-base/logging.h> + #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 573bb6d4be..a5aa1c12b3 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 ce39a13692..f531b2aa51 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.h +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h @@ -21,10 +21,12 @@ #include <memory> #include <vector> +#include <android-base/logging.h> + #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 7378a0a081..9ce7ec9a97 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 <android-base/logging.h> + #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 e0cef859e1..5b0cd6baa8 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -19,6 +19,8 @@ #include <vector> +#include <android-base/logging.h> + #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/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 33d59f9d42..203e484fb7 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -21,8 +21,8 @@ #include <type_traits> -#include "dex_file-inl.h" #include "class_reference.h" +#include "dex/dex_file-inl.h" #include "method_reference.h" #include "type_reference.h" @@ -58,7 +58,7 @@ inline typename AtomicDexRefMap<DexFileReferenceType, Value>::InsertResult return kInsertResultInvalidDexFile; } DCHECK_LT(ref.index, array->size()); - return (*array)[ref.index].CompareExchangeStrongSequentiallyConsistent(expected, desired) + return (*array)[ref.index].CompareAndSetStrongSequentiallyConsistent(expected, desired) ? kInsertResultSuccess : kInsertResultCASFailure; } diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h index 205543f31d..9ff506d6a4 100644 --- a/compiler/utils/atomic_dex_ref_map.h +++ b/compiler/utils/atomic_dex_ref_map.h @@ -18,7 +18,7 @@ #define ART_COMPILER_UTILS_ATOMIC_DEX_REF_MAP_H_ #include "base/dchecked_vector.h" -#include "dex_file_reference.h" +#include "dex/dex_file_reference.h" #include "safe_map.h" namespace art { diff --git a/compiler/utils/atomic_dex_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc index 8fce36f021..d58d60b4f3 100644 --- a/compiler/utils/atomic_dex_ref_map_test.cc +++ b/compiler/utils/atomic_dex_ref_map_test.cc @@ -19,7 +19,7 @@ #include <memory> #include "common_runtime_test.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "method_reference.h" #include "scoped_thread_state_change-inl.h" diff --git a/compiler/utils/intrusive_forward_list.h b/compiler/utils/intrusive_forward_list.h index 5a358ac2c4..ccdd32aad4 100644 --- a/compiler/utils/intrusive_forward_list.h +++ b/compiler/utils/intrusive_forward_list.h @@ -23,8 +23,9 @@ #include <memory> #include <type_traits> +#include <android-base/logging.h> + #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h index 0fc1353bf5..f5df926749 100644 --- a/compiler/utils/jni_macro_assembler.h +++ b/compiler/utils/jni_macro_assembler.h @@ -19,12 +19,13 @@ #include <vector> +#include <android-base/logging.h> + #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 b9d4e9c521..3c91b2ffd1 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 <android-base/logging.h> +#include <android-base/macros.h> namespace art { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index eb75f8b67c..2218ef9af2 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<Register>(0), 0, 0x18), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18)).GprIns(rs, rt); } void MipsAssembler::MultuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19)).GprIns(rs, rt); } void MipsAssembler::DivR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a)).GprIns(rs, rt); } void MipsAssembler::DivuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast<Register>(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<Register>(0), rd, 0x01, 0x10), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast<Register>(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<Register>(0), rd, 0x01, 0x11), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast<Register>(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<Register>(0), rt, rd, 0x10, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Seh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Wsbh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Bitswap(Register rd, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast<Register>(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<Register>(0), rt, rd, shamt, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast<Register>(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<Register>(0), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast<Register>(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<Register>(1), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast<Register>(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<Register>(0), rt, rd, shamt, 0x03), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast<Register>(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<Register>(size - 1), pos, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast<Register>(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<Register>(pos + size - 1), pos, 0x04), rd, rd, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast<Register>(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<Register>(0), rt, imm16), rt, ZERO, ZERO); + DsFsmInstr(EmitI(0xf, static_cast<Register>(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<FRegister>(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<FRegister>(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(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<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast<Register>(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<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast<Register>((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<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(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<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>((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<FRegister>((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>((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<FRegister>(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(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<FRegister>(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(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<FRegister>(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b)).FprOuts(fd).FprIns(fs); } void MipsAssembler::ClassD(FRegister fd, FRegister fs) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncLD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtds(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast<FRegister>(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<FRegister>(reg & ~1u) : reg; } void MipsAssembler::Mfc1(Register rt, FRegister fs) { - DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(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<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0), - fs, - rt); + uint32_t encoding = + EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(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<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(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<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0), - fs, - rt); + DsFsmInstr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(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<Register>(ft), imm16), ft, rs); + uint32_t encoding = EmitI(0x31, rs, static_cast<Register>(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<Register>(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x35, rs, static_cast<Register>(ft), imm16)).FprOuts(ft).GprIns(rs); } void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x39, rs, static_cast<Register>(ft), imm16)).FprIns(GetFpuRegLow(ft)).GprIns(rs); } void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x3d, rs, static_cast<Register>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<VectorRegister>(rd), 0x19), - rd, - static_cast<FRegister>(ws)); + DsFsmInstr(EmitMsaELM(0x2, n4 | kMsaDfNByteMask, ws, static_cast<VectorRegister>(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<VectorRegister>(rd), 0x19), - rd, - static_cast<FRegister>(ws)); + DsFsmInstr(EmitMsaELM(0x2, n3 | kMsaDfNHalfwordMask, ws, static_cast<VectorRegister>(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<VectorRegister>(rd), 0x19), - rd, - static_cast<FRegister>(ws)); + DsFsmInstr(EmitMsaELM(0x2, n2 | kMsaDfNWordMask, ws, static_cast<VectorRegister>(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<VectorRegister>(rd), 0x19), - rd, - static_cast<FRegister>(ws)); + DsFsmInstr(EmitMsaELM(0x3, n4 | kMsaDfNByteMask, ws, static_cast<VectorRegister>(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<VectorRegister>(rd), 0x19), - rd, - static_cast<FRegister>(ws)); + DsFsmInstr(EmitMsaELM(0x3, n3 | kMsaDfNHalfwordMask, ws, static_cast<VectorRegister>(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<VectorRegister>(rs), wd, 0x19), - static_cast<FRegister>(wd), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n4 | kMsaDfNByteMask, static_cast<VectorRegister>(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<VectorRegister>(rs), wd, 0x19), - static_cast<FRegister>(wd), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n3 | kMsaDfNHalfwordMask, static_cast<VectorRegister>(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<VectorRegister>(rs), wd, 0x19), - static_cast<FRegister>(wd), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n2 | kMsaDfNWordMask, static_cast<VectorRegister>(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::FillB(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x0, static_cast<VectorRegister>(rs), wd, 0x1e), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x0, static_cast<VectorRegister>(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillH(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x1, static_cast<VectorRegister>(rs), wd, 0x1e), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x1, static_cast<VectorRegister>(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillW(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x2, static_cast<VectorRegister>(rs), wd, 0x1e), - static_cast<FRegister>(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x2, static_cast<VectorRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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<FRegister>(wd), - static_cast<FRegister>(ws), - static_cast<FRegister>(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 1c3097ac58..7de8e2e366 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<typename T, typename... Ts> + 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<typename T, typename... Ts> + 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<typename T, typename... Ts> + 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<FRegister>(reg)); } + template<typename T, typename... Ts> + 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<FRegister>(reg)); } + template<typename T, typename... Ts> + 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<FRegister>(reg)); + } + template<typename T, typename... Ts> + 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<typename T, typename... Ts> + 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<typename T, typename... Ts> + 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<PointerSi void FloorWS(FRegister fd, FRegister fs); void FloorWD(FRegister fd, 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. This applies to all 32-bit FPR loads: + // Mtc1(), Mthc1(), MoveToFpuHigh(), Lwc1(). Even if you need two Mtc1()'s or two + // Lwc1()'s to load a pair of 32-bit FPRs and these loads do not interfere with one + // another (unlike Mtc1() and Mthc1() with 64-bit FPRs), maintain the order: + // low then high. + // + // Also, prefer MoveFromFpuHigh()/MoveToFpuHigh() over Mfhc1()/Mthc1() and Mfc1()/Mtc1(). + // This will save you some if statements. + FRegister GetFpuRegLow(FRegister reg); void Mfc1(Register rt, FRegister fs); void Mtc1(Register rt, FRegister fs); void Mfhc1(Register rt, FRegister fs); @@ -1337,23 +1422,13 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi // Used to make the decision of moving the instruction into a delay slot. struct DelaySlot { DelaySlot(); + // Encoded instruction that may be used to fill the delay slot or 0 // (0 conveniently represents NOP). uint32_t instruction_; - // Mask of output GPRs for the instruction. - uint32_t gpr_outs_mask_; - // Mask of input GPRs for the instruction. - uint32_t gpr_ins_mask_; - // Mask of output FPRs for the instruction. - uint32_t fpr_outs_mask_; - // Mask of input FPRs for the instruction. - uint32_t fpr_ins_mask_; - // Mask of output FPU condition code flags for the instruction. - uint32_t cc_outs_mask_; - // Mask of input FPU condition code flags for the instruction. - uint32_t cc_ins_mask_; - // Branches never operate on the LO and HI registers, hence there's - // no mask for LO and HI. + + // Input/output register masks. + InOutRegMasks masks_; // Label for patchable instructions to allow moving them into delay slots. MipsLabel* patcher_label_; @@ -1646,30 +1721,8 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void FinalizeLabeledBranch(MipsLabel* label); // Various helpers for branch delay slot management. - void 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 = nullptr); + InOutRegMasks& DsFsmInstr(uint32_t instruction, MipsLabel* patcher_label = nullptr); void DsFsmInstrNop(uint32_t instruction); - void DsFsmInstrRrr(uint32_t instruction, - Register out, - Register in1, - Register in2, - MipsLabel* patcher_label = nullptr); - void DsFsmInstrRrrr(uint32_t instruction, Register in1_out, Register in2, Register in3); - void DsFsmInstrFff(uint32_t instruction, FRegister out, FRegister in1, FRegister in2); - void DsFsmInstrFfff(uint32_t instruction, FRegister in1_out, FRegister in2, FRegister in3); - void DsFsmInstrFffr(uint32_t instruction, FRegister in1_out, FRegister in2, Register in3); - void DsFsmInstrRf(uint32_t instruction, Register out, FRegister in); - void DsFsmInstrFr(uint32_t instruction, FRegister out, Register in); - void DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2); - void DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2); - void DsFsmInstrRrrc(uint32_t instruction, Register in1_out, Register in2, int cc_in); - void DsFsmInstrFffc(uint32_t instruction, FRegister in1_out, FRegister in2, int cc_in); void DsFsmLabel(); void DsFsmCommitLabel(); void DsFsmDropLabel(); diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h index b4dfdbd8d3..016c0dbb2e 100644 --- a/compiler/utils/mips/constants_mips.h +++ b/compiler/utils/mips/constants_mips.h @@ -19,8 +19,9 @@ #include <iosfwd> +#include <android-base/logging.h> + #include "arch/mips/registers_mips.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index bf56877499..e1b0e75108 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -430,6 +430,20 @@ void Mips64Assembler::Dext(GpuRegister rt, GpuRegister rs, int pos, int size) { EmitR(0x1f, rs, rt, static_cast<GpuRegister>(size - 1), pos, 0x3); } +void Mips64Assembler::Ins(GpuRegister rd, GpuRegister rt, int pos, int size) { + CHECK(IsUint<5>(pos)) << pos; + CHECK(IsUint<5>(size - 1)) << size; + CHECK(IsUint<5>(pos + size - 1)) << pos << " + " << size; + EmitR(0x1f, rt, rd, static_cast<GpuRegister>(pos + size - 1), pos, 0x04); +} + +void Mips64Assembler::Dinsm(GpuRegister rt, GpuRegister rs, int pos, int size) { + CHECK(IsUint<5>(pos)) << pos; + CHECK(2 <= size && size <= 64) << size; + CHECK(IsUint<5>(pos + size - 33)) << pos << " + " << size; + EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos, 0x5); +} + void Mips64Assembler::Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size) { CHECK(IsUint<5>(pos - 32)) << pos; CHECK(IsUint<5>(size - 1)) << size; @@ -437,6 +451,23 @@ void Mips64Assembler::Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size) { EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos - 32, 0x6); } +void Mips64Assembler::Dins(GpuRegister rt, GpuRegister rs, int pos, int size) { + CHECK(IsUint<5>(pos)) << pos; + CHECK(IsUint<5>(size - 1)) << size; + CHECK(IsUint<5>(pos + size - 1)) << pos << " + " << size; + EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 1), pos, 0x7); +} + +void Mips64Assembler::DblIns(GpuRegister rt, GpuRegister rs, int pos, int size) { + if (pos >= 32) { + Dinsu(rt, rs, pos, size); + } else if ((static_cast<int64_t>(pos) + size - 1) >= 32) { + Dinsm(rt, rs, pos, size); + } else { + Dins(rt, rs, pos, size); + } +} + void Mips64Assembler::Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) { CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne; int sa = saPlusOne - 1; diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 9f5e6aaa88..7a61f39e64 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -478,7 +478,11 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Dsbh(GpuRegister rd, GpuRegister rt); // MIPS64 void Dshd(GpuRegister rd, GpuRegister rt); // MIPS64 void Dext(GpuRegister rs, GpuRegister rt, int pos, int size); // MIPS64 + void Ins(GpuRegister rt, GpuRegister rs, int pos, int size); + void Dins(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 + void Dinsm(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 + void DblIns(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 void Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne); void Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne); // MIPS64 void Wsbh(GpuRegister rd, GpuRegister rt); diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index d89ca3d6ea..b0e1d91c3f 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -1353,23 +1353,42 @@ TEST_F(AssemblerMIPS64Test, Dext) { DriverStr(expected.str(), "Dext"); } -TEST_F(AssemblerMIPS64Test, Dinsu) { +TEST_F(AssemblerMIPS64Test, Ins) { + std::vector<mips64::GpuRegister*> regs = GetRegisters(); + WarnOnCombinations(regs.size() * regs.size() * 33 * 16); + std::string expected; + for (mips64::GpuRegister* reg1 : regs) { + for (mips64::GpuRegister* reg2 : regs) { + for (int32_t pos = 0; pos < 32; pos++) { + for (int32_t size = 1; pos + size <= 32; size++) { + __ Ins(*reg1, *reg2, pos, size); + std::ostringstream instr; + instr << "ins $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n"; + expected += instr.str(); + } + } + } + } + DriverStr(expected, "Ins"); +} + +TEST_F(AssemblerMIPS64Test, DblIns) { std::vector<mips64::GpuRegister*> reg1_registers = GetRegisters(); std::vector<mips64::GpuRegister*> reg2_registers = GetRegisters(); - WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * 33 * 16); + WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * 65 * 32); std::ostringstream expected; for (mips64::GpuRegister* reg1 : reg1_registers) { for (mips64::GpuRegister* reg2 : reg2_registers) { - for (int32_t pos = 32; pos < 64; pos++) { + for (int32_t pos = 0; pos < 64; pos++) { for (int32_t size = 1; pos + size <= 64; size++) { - __ Dinsu(*reg1, *reg2, pos, size); - expected << "dinsu $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n"; + __ DblIns(*reg1, *reg2, pos, size); + expected << "dins $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n"; } } } } - DriverStr(expected.str(), "Dinsu"); + DriverStr(expected.str(), "DblIns"); } TEST_F(AssemblerMIPS64Test, Lsa) { diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index bc8e40b437..310f23c287 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -19,8 +19,9 @@ #include <iosfwd> +#include <android-base/logging.h> + #include "arch/mips64/registers_mips64.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/string_reference_test.cc b/compiler/utils/string_reference_test.cc index 90335eb048..4b07e65771 100644 --- a/compiler/utils/string_reference_test.cc +++ b/compiler/utils/string_reference_test.cc @@ -18,7 +18,7 @@ #include <memory> -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "gtest/gtest.h" #include "utils/test_dex_file_builder.h" diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 12d113d420..1f9ad4242d 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -22,7 +22,6 @@ #include <numeric> #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 2280f8b993..76df527108 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -24,7 +24,8 @@ #include <set> #include <vector> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/macros.h" #include "base/mutex.h" diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index 0da30fe768..04fba51dc1 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -24,10 +24,11 @@ #include <set> #include <vector> +#include <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" -#include "dex_file_loader.h" -#include "standard_dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/standard_dex_file.h" namespace art { diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc index c76739b3b1..736a17edac 100644 --- a/compiler/utils/test_dex_file_builder_test.cc +++ b/compiler/utils/test_dex_file_builder_test.cc @@ -16,7 +16,7 @@ #include "test_dex_file_builder.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gtest/gtest.h" #include "utils.h" diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 0bc1560ed7..2e03b9fc3c 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -19,8 +19,9 @@ #include <iosfwd> +#include <android-base/logging.h> + #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 cc508a196b..2af3e7be16 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -19,8 +19,9 @@ #include <iosfwd> +#include <android-base/logging.h> + #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 4709fd0e9e..76448d819c 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -21,10 +21,10 @@ #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" #include "dex/verification_results.h" #include "dex/verified_method.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "handle_scope-inl.h" @@ -158,11 +158,10 @@ class VerifierDepsTest : public CommonCompilerTest { while (it.HasNextDirectMethod()) { ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *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()) { @@ -238,9 +237,9 @@ class VerifierDepsTest : public CommonCompilerTest { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { - ASSERT_EQ(cls->GetStatus(), mirror::Class::kStatusVerified); + ASSERT_EQ(cls->GetStatus(), ClassStatus::kVerified); } else { - ASSERT_LT(cls->GetStatus(), mirror::Class::kStatusVerified); + ASSERT_LT(cls->GetStatus(), ClassStatus::kVerified); } } } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 27bec1d3e1..dabe07f9ce 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -58,11 +58,12 @@ #include "compiler_callbacks.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dexlayout.h" +#include "dex/dex_file-inl.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "dex2oat_options.h" #include "dex2oat_return_codes.h" -#include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "driver/compiler_options_map-inl.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. @@ -1686,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<mirror::ClassLoader>(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<mirror::ClassLoader> decoded_weak = soa.Decode<mirror::ClassLoader>(weak_class_loader); if (decoded_weak != nullptr) { @@ -2233,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 @@ -2814,7 +2816,7 @@ class Dex2Oat FINAL { // Dex files we are compiling, does not include the class path dex files. std::vector<const DexFile*> dex_files_; std::string no_inline_from_string_; - CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; + CompactDexLevel compact_dex_level_ = kDefaultCompactDexLevel; std::vector<std::unique_ptr<linker::ElfWriter>> elf_writers_; std::vector<std::unique_ptr<linker::OatWriter>> oat_writers_; @@ -2885,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 a02fbf862f..980363b1bb 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -22,14 +22,15 @@ #include <sys/wait.h> #include <unistd.h> +#include <android-base/logging.h> + #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" -#include "dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "jit/profile_compilation_info.h" #include "method_reference.h" #include "runtime.h" diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h index f8198ee08b..ccc85c8951 100644 --- a/dex2oat/dex2oat_options.h +++ b/dex2oat/dex2oat_options.h @@ -22,9 +22,9 @@ #include <vector> #include "base/variant_map.h" -#include "cdex/compact_dex_level.h" #include "cmdline_types.h" // TODO: don't need to include this file here #include "compiler.h" +#include "dex/compact_dex_level.h" #include "driver/compiler_options_map.h" #include "image.h" diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index ad287b0745..c91240e19d 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -22,18 +22,19 @@ #include <sys/wait.h> #include <unistd.h> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "common_runtime_test.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex-inl.h" #include "bytecode_utils.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "dex2oat_environment_test.h" #include "dex2oat_return_codes.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" #include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" @@ -875,6 +876,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 +944,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest { class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { for (const DexInstructionPcPair& inst : - class_it.GetMethodCodeItem()->Instructions()) { + CodeItemInstructionAccessor(dex_file.get(), class_it.GetMethodCodeItem())) { ASSERT_FALSE(inst->IsQuickened()); } } @@ -953,6 +956,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 +996,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" }); } diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index b139a12fd4..aa64b7d59e 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -20,8 +20,9 @@ #include <unordered_map> #include <unordered_set> +#include <android-base/logging.h> + #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 <typename ElfTypes> void ElfWriterQuick<ElfTypes>::Start() { builder_->Start(); if (compiler_options_->GetGenerateBuildId()) { + builder_->GetBuildId()->AllocateVirtualMemory(builder_->GetBuildId()->GetSize()); builder_->WriteBuildIdSection(); } } @@ -224,9 +226,6 @@ void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) { template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::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 cedbccf7cc..85145d3d64 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -293,14 +293,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr<BufferedOutputStream> vdex_out = - std::make_unique<BufferedOutputStream>( - std::make_unique<FileOutputStream>(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()); @@ -308,6 +301,14 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ElfWriter* const elf_writer = elf_writers[i].get(); std::vector<const DexFile*> cur_dex_files(1u, class_path[i]); oat_writer->Initialize(driver, writer.get(), cur_dex_files); + + std::unique_ptr<BufferedOutputStream> vdex_out = + std::make_unique<BufferedOutputStream>( + std::make_unique<FileOutputStream>(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 68c9f80a44..73eaad47a1 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -29,12 +29,12 @@ #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" -#include "dex_file-inl.h" -#include "dex_file_types.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" #include "driver/compiler_driver.h" #include "elf_file.h" #include "elf_utils.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; - } else if (klass->GetStatus() == Class::kStatusInitialized) { - bin = kBinClassInitialized; + bin = Bin::kKnownDirty; + } else if (klass->GetStatus() == ClassStatus::kInitialized) { + 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<kVerifyNone>()->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<kVerifyNone>() == 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; @@ -649,7 +650,7 @@ bool ImageWriter::WillMethodBeDirty(ArtMethod* m) const { } mirror::Class* declaring_class = m->GetDeclaringClass(); // Initialized is highly unlikely to dirty since there's no entry points to mutate. - return declaring_class == nullptr || declaring_class->GetStatus() != Class::kStatusInitialized; + return declaring_class == nullptr || declaring_class->GetStatus() != ClassStatus::kInitialized; } bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { @@ -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<uint32_t>(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<mirror::DexCache> 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<mirror::DexCache> 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<mirror::DexCache> dex_cache, uint32_t stored_index = pair.index; ObjPtr<mirror::Class> 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<mirror::DexCache> dex_cache, uint32_t stored_index = pair.index; ObjPtr<mirror::String> 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<Bin>(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<size_t>(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<ArtField*>(dest)->GetDeclaringClassAddressWithoutBarrier(), reinterpret_cast<ArtField*>(pair.first)->GetDeclaringClass().Ptr()); break; } - case kNativeObjectRelocationTypeRuntimeMethod: - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodDirty: { + case NativeObjectRelocationType::kRuntimeMethod: + case NativeObjectRelocationType::kArtMethodClean: + case NativeObjectRelocationType::kArtMethodDirty: { CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first), reinterpret_cast<ArtMethod*>(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<ArtField>::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<ArtMethod>::ComputeSize(0, size, alignment)); @@ -2090,16 +2094,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(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<ImTable*>(pair.first); ImTable* dest_imt = reinterpret_cast<ImTable*>(dest); CopyAndFixupImTable(orig_imt, dest_imt); break; } - case kNativeObjectRelocationTypeIMTConflictTable: { + case NativeObjectRelocationType::kIMTConflictTable: { auto* orig_table = reinterpret_cast<ImtConflictTable*>(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<ArtField*>(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<const uint8_t*>(header.GetQuickGenericJniTrampoline()); - case kOatAddressInterpreterToInterpreterBridge: + case StubType::kInterpreterToInterpreterBridge: return static_cast<const uint8_t*>(header.GetInterpreterToInterpreterBridge()); - case kOatAddressInterpreterToCompiledCodeBridge: + case StubType::kInterpreterToCompiledCodeBridge: return static_cast<const uint8_t*>(header.GetInterpreterToCompiledCodeBridge()); - case kOatAddressJNIDlsymLookup: + case StubType::kJNIDlsymLookup: return static_cast<const uint8_t*>(header.GetJniDlsymLookup()); - case kOatAddressQuickIMTConflictTrampoline: + case StubType::kQuickIMTConflictTrampoline: return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline()); - case kOatAddressQuickResolutionTrampoline: + case StubType::kQuickResolutionTrampoline: return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline()); - case kOatAddressQuickToInterpreterBridge: + case StubType::kQuickToInterpreterBridge: return static_cast<const uint8_t*>(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<size_t>(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<size_t>(up_to), kNumberOfBins); + return std::accumulate(&bin_slot_sizes_[0], + &bin_slot_sizes_[0] + static_cast<size_t>(up_to), + /*init*/ static_cast<size_t>(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 68c7b594fb..a7c14395dd 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -62,6 +62,7 @@ class ClassLoader; } // namespace mirror class ClassLoaderVisitor; +class ImTable; class ImtConflictTable; static constexpr int kInvalidFd = -1; @@ -161,70 +162,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<uint32_t>(kBinMirrorCount - 1); + static constexpr size_t kBinBits = + MinimumBitsToStore<uint32_t>(static_cast<size_t>(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 +233,12 @@ class ImageWriter FINAL { // 111000.....0 static const size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift; + // Number of bins, including non-mirror bins. + static constexpr size_t kNumberOfBins = static_cast<size_t>(Bin::kLast) + 1u; + + // Number of stub types. + static constexpr size_t kNumberOfStubTypes = static_cast<size_t>(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 +269,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<size_t>(stub_type), kNumberOfStubTypes); + return stub_offsets_[static_cast<size_t>(stub_type)]; + } + + void SetStubOffset(StubType stub_type, size_t offset) { + DCHECK_LT(static_cast<size_t>(stub_type), kNumberOfStubTypes); + stub_offsets_[static_cast<size_t>(stub_type)] = offset; + } + + size_t GetBinSlotOffset(Bin bin) const { + DCHECK_LT(static_cast<size_t>(bin), kNumberOfBins); + return bin_slot_offsets_[static_cast<size_t>(bin)]; + } + + void IncrementBinSlotSize(Bin bin, size_t size_to_add) { + DCHECK_LT(static_cast<size_t>(bin), kNumberOfBins); + bin_slot_sizes_[static_cast<size_t>(bin)] += size_to_add; + } + + size_t GetBinSlotSize(Bin bin) const { + DCHECK_LT(static_cast<size_t>(bin), kNumberOfBins); + return bin_slot_sizes_[static_cast<size_t>(bin)]; + } + + void IncrementBinSlotCount(Bin bin, size_t count_to_add) { + DCHECK_LT(static_cast<size_t>(bin), kNumberOfBins); + bin_slot_count_[static_cast<size_t>(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<MemMap> 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 +340,12 @@ class ImageWriter FINAL { SafeMap<const DexFile*, size_t> 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 +407,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 +483,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 +609,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<void*, NativeObjectRelocation> native_object_relocations_; diff --git a/dex2oat/linker/index_bss_mapping_encoder.h b/dex2oat/linker/index_bss_mapping_encoder.h index 9bc14329ff..c6326ed1cf 100644 --- a/dex2oat/linker/index_bss_mapping_encoder.h +++ b/dex2oat/linker/index_bss_mapping_encoder.h @@ -17,9 +17,10 @@ #ifndef ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ #define ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ +#include <android-base/logging.h> + #include "base/bit_utils.h" #include "base/bit_vector-inl.h" -#include "base/logging.h" #include "index_bss_mapping.h" namespace art { diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 178a78f0bb..1abaf7dfd1 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 <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" #include "globals.h" namespace art { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 293078acee..cb1b80d590 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -26,16 +26,18 @@ #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" #include "class_table-inl.h" #include "compiled_method-inl.h" #include "debug/method_debug_info.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" +#include "dex/standard_dex_file.h" #include "dex/verification_results.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" #include "dexlayout.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" @@ -53,7 +55,6 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" -#include "standard_dex_file.h" #include "oat_quick_method_header.h" #include "os.h" #include "safe_map.h" @@ -187,8 +188,8 @@ class OatWriter::OatClassHeader { OatClassHeader(uint32_t offset, uint32_t num_non_null_compiled_methods, uint32_t num_methods, - mirror::Class::Status status) - : status_(status), + ClassStatus status) + : status_(enum_cast<uint16_t>(status)), offset_(offset) { // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use // kOatClassAllCompiled unless there is at least one compiled method. This means in an @@ -209,8 +210,8 @@ class OatWriter::OatClassHeader { } // Data to write. - static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits"); - int16_t status_; + static_assert(enum_cast<>(ClassStatus::kLast) < (1 << 16), "class status won't fit in 16bits"); + uint16_t status_; static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits"); uint16_t type_; @@ -930,17 +931,17 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { bool EndClass() OVERRIDE { ClassReference class_ref(dex_file_, class_def_index_); - mirror::Class::Status status; + ClassStatus status; bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status); if (!found) { VerificationResults* results = writer_->compiler_driver_->GetVerificationResults(); if (results != nullptr && results->IsClassRejected(class_ref)) { // The oat class status is used only for verification of resolved classes, - // so use kStatusErrorResolved whether the class was resolved or unresolved + // so use ClassStatus::kErrorResolved whether the class was resolved or unresolved // during compile-time verification. - status = mirror::Class::kStatusErrorResolved; + status = ClassStatus::kErrorResolved; } else { - status = mirror::Class::kStatusNotReady; + status = ClassStatus::kNotReady; } } @@ -1591,11 +1592,10 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *dex_file_, it.GetMemberIndex(), hs.NewHandle(dex_cache), ScopedNullHandle<mirror::ClassLoader>(), - nullptr, + /* referrer */ nullptr, invoke_type); if (method == nullptr) { LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " @@ -1951,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<mirror::Class> GetTargetType(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(writer_->HasImage()); ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); ObjPtr<mirror::Class> 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<mirror::String> 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<mirror::String> string = + linker->LookupString(patch.TargetStringIndex(), GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); DCHECK(writer_->HasBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); @@ -1980,13 +1981,14 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method) - oat_data_begin); } - uint32_t GetTargetObjectOffset(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t GetTargetObjectOffset(ObjPtr<mirror::Object> 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<uint32_t>(reinterpret_cast<uintptr_t>(object) - oat_data_begin); + return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object.Ptr()) - oat_data_begin); } void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object) @@ -2694,7 +2696,8 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { CompiledMethod* compiled_method = driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t existing_debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); + 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 = @@ -2742,10 +2745,6 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { 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<off_t>(start_offset)) { PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset @@ -2789,7 +2788,16 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { 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; } @@ -3314,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; @@ -3433,6 +3442,7 @@ 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<MemMap> mem_map(dex_layout.GetAndReleaseMemMap()); @@ -3864,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_; diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 4055878b55..ba29e3b3a2 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -24,7 +24,7 @@ #include "base/array_ref.h" #include "base/dchecked_vector.h" -#include "cdex/compact_dex_level.h" +#include "dex/compact_dex_level.h" #include "linker/relative_patcher.h" // For RelativePatcherTargetProvider. #include "mem_map.h" #include "method_reference.h" diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index fec05cc393..488806092b 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -26,7 +26,7 @@ #include "compiled_method-inl.h" #include "compiler.h" #include "debug/method_debug_info.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "driver/compiler_driver.h" @@ -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 { @@ -225,6 +226,9 @@ class OatTest : public CommonCompilerTest { 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; } @@ -451,7 +455,7 @@ TEST_F(OatTest, WriteRead) { ScopedNullHandle<mirror::ClassLoader>()); const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i); - CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class.GetStatus()) << descriptor; + CHECK_EQ(ClassStatus::kNotReady, oat_class.GetStatus()) << descriptor; CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled, oat_class.GetType()) << descriptor; @@ -652,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) { diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index a7af193f0a..8a06f44628 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -44,10 +44,12 @@ #include "android-base/stringprintf.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" -#include "dex_instruction-inl.h" +#include "dex/code_item_accessors-no_art-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction-inl.h" #include "dexdump_cfg.h" namespace art { @@ -734,7 +736,8 @@ 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_; + CodeItemDataAccessor accessor(pDexFile, pCode); + const u4 triesSize = accessor.TriesSize(); // No catch table. if (triesSize == 0) { @@ -744,12 +747,11 @@ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) // Dump all table entries. fprintf(gOutFile, " catches : %d\n", triesSize); - for (u4 i = 0; i < triesSize; i++) { - const DexFile::TryItem* pTry = pDexFile->GetTryItems(*pCode, i); - const u4 start = pTry->start_addr_; - const u4 end = start + pTry->insn_count_; + for (const DexFile::TryItem& try_item : accessor.TryItems()) { + const u4 start = try_item.start_addr_; + const u4 end = start + try_item.insn_count_; fprintf(gOutFile, " 0x%04x - 0x%04x\n", start, end); - for (CatchHandlerIterator it(*pCode, *pTry); it.HasNext(); it.Next()) { + for (CatchHandlerIterator it(accessor, try_item); it.HasNext(); it.Next()) { const dex::TypeIndex tidx = it.GetHandlerTypeIndex(); const char* descriptor = (!tidx.IsValid()) ? "<any>" : pDexFile->StringByTypeIdx(tidx); fprintf(gOutFile, " %s -> 0x%04x\n", descriptor, it.GetHandlerAddress()); @@ -949,14 +951,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 +968,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 +1169,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 +1186,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,11 +1205,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"); - uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo( - pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr); + accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr); } /* diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 62c970de8a..f08ea746d3 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -23,9 +23,12 @@ #include <map> #include <ostream> #include <set> +#include <sstream> -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/code_item_accessors-no_art-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_instruction-inl.h" namespace art { @@ -36,17 +39,17 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, os << "digraph {\n"; os << " # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n"; + CodeItemDataAccessor accessor(dex_file, code_item); + std::set<uint32_t> 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<const uint16_t*>(inst); int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16); const uint16_t* switch_insns = insns + switch_offset; uint32_t switch_count = switch_insns[1]; @@ -62,7 +65,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, int32_t offset = static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) | static_cast<int32_t>(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 +76,10 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, std::map<uint32_t, uint32_t> 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 +108,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 +137,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 +162,9 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, uint32_t last_node_id = std::numeric_limits<uint32_t>::max(); uint32_t old_dex_pc = 0; uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::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()) { @@ -196,7 +196,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, } // Look at the exceptions of the first entry. - CatchHandlerIterator catch_it(*code_item, dex_pc); + CatchHandlerIterator catch_it(accessor, dex_pc); for (; catch_it.HasNext(); catch_it.Next()) { exception_targets.insert(catch_it.GetHandlerAddress()); } @@ -221,7 +221,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<const uint16_t*>(inst); /* make sure the start of the switch is in range */ int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16); /* offset to switch table is a relative branch-style offset */ @@ -255,7 +255,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Exception edges. If this is not the first instruction in the block if (block_start_dex_pc != dex_pc) { std::set<uint32_t> current_handler_pcs; - CatchHandlerIterator catch_it(*code_item, dex_pc); + CatchHandlerIterator catch_it(accessor, dex_pc); for (; catch_it.HasNext(); catch_it.Next()) { current_handler_pcs.insert(catch_it.GetHandlerAddress()); } @@ -271,6 +271,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // No fall-through. last_node_id = std::numeric_limits<uint32_t>::max(); } + old_dex_pc = pair.DexPc(); } // Finish up the last block, if it had common exceptions. if (!exception_targets.empty()) { @@ -292,10 +293,10 @@ 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); + CatchHandlerIterator catch_it(accessor, dex_pc); if (catch_it.HasNext()) { std::set<uint32_t> handled_targets; for (; catch_it.HasNext(); catch_it.Next()) { @@ -321,7 +322,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()) { diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index 43c3d12de5..382b551a1a 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -28,7 +28,9 @@ #include <string.h> #include <unistd.h> -#include "base/logging.h" +#include <android-base/logging.h> + +#include <base/logging.h> // For InitLogging. #include "mem_map.h" #include "runtime.h" diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index f2d46199a2..1c5b16d84b 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -16,7 +16,7 @@ #include "compact_dex_writer.h" -#include "cdex/compact_dex_file.h" +#include "dex/compact_dex_file.h" namespace art { @@ -27,7 +27,9 @@ void CompactDexWriter::WriteHeader() { header.checksum_ = header_->Checksum(); std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_); header.file_size_ = header_->FileSize(); - header.header_size_ = header_->GetSize(); + // Since we are not necessarily outputting the same format as the input, avoid using the stored + // header size. + header.header_size_ = GetHeaderSize(); header.endian_tag_ = header_->EndianTag(); header.link_size_ = header_->LinkSize(); header.link_off_ = header_->LinkOffset(); @@ -47,7 +49,17 @@ void CompactDexWriter::WriteHeader() { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + header.feature_flags_ = 0u; + // In cases where apps are converted to cdex during install, maintain feature flags so that + // the verifier correctly verifies apps that aren't targetting default methods. + if (header_->SupportDefaultMethods()) { + header.feature_flags_ |= static_cast<uint32_t>(CompactDexFile::FeatureFlags::kDefaultMethods); + } UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u)); } +size_t CompactDexWriter::GetHeaderSize() const { + return sizeof(CompactDexFile::Header); +} + } // namespace art diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index e28efab5c1..d13333bb18 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -35,6 +35,8 @@ class CompactDexWriter : public DexWriter { protected: void WriteHeader() OVERRIDE; + size_t GetHeaderSize() const OVERRIDE; + const CompactDexLevel compact_dex_level_; private: diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 2af579c73c..8ed3a79542 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -21,7 +21,10 @@ */ #include "dex_ir.h" -#include "dex_instruction-inl.h" + +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_instruction-inl.h" #include "dex_ir_builder.h" namespace art { @@ -564,13 +567,14 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( 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. - uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&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) { @@ -584,20 +588,19 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, } } - 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<const CatchHandler>& existing_handlers : *handler_list) { if (handler_off == existing_handlers->GetListOffset()) { @@ -608,7 +611,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(accessor, 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; @@ -622,7 +625,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, tries->push_back(std::unique_ptr<const TryItem>(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()) { @@ -666,7 +669,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, } } - uint32_t size = DexFile::GetCodeItemSize(disk_code_item); + uint32_t size = dex_file.GetCodeItemSize(disk_code_item); CodeItem* code_item = new CodeItem( registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); code_item->SetSize(size); @@ -674,20 +677,20 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, // 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<std::vector<TypeId*>> type_ids(new std::vector<TypeId*>()); - std::unique_ptr<std::vector<StringId*>> string_ids(new std::vector<StringId*>()); - std::unique_ptr<std::vector<MethodId*>> method_ids(new std::vector<MethodId*>()); - std::unique_ptr<std::vector<FieldId*>> field_ids(new std::vector<FieldId*>()); + std::vector<TypeId*> type_ids; + std::vector<StringId*> string_ids; + std::vector<MethodId*> method_ids; + std::vector<FieldId*> 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); } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 8421774104..6797fa5dd6 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -25,8 +25,8 @@ #include <vector> #include "base/stl_util.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" #include "leb128.h" #include "utf.h" @@ -524,7 +524,8 @@ class Header : public Item { uint32_t link_size, uint32_t link_offset, uint32_t data_size, - uint32_t data_offset) + uint32_t data_offset, + bool support_default_methods) : Item(0, kHeaderItemSize), checksum_(checksum), endian_tag_(endian_tag), @@ -533,7 +534,8 @@ class Header : public Item { link_size_(link_size), link_offset_(link_offset), data_size_(data_size), - data_offset_(data_offset) { + data_offset_(data_offset), + support_default_methods_(support_default_methods) { memcpy(magic_, magic, sizeof(magic_)); memcpy(signature_, signature, sizeof(signature_)); } @@ -567,6 +569,10 @@ class Header : public Item { void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } + bool SupportDefaultMethods() const { + return support_default_methods_; + } + private: uint8_t magic_[8]; uint32_t checksum_; @@ -578,6 +584,7 @@ class Header : public Item { uint32_t link_offset_; uint32_t data_size_; uint32_t data_offset_; + const bool support_default_methods_; Collections collections_; @@ -1013,25 +1020,25 @@ using TryItemVector = std::vector<std::unique_ptr<const TryItem>>; class CodeFixups { public: - CodeFixups(std::vector<TypeId*>* type_ids, - std::vector<StringId*>* string_ids, - std::vector<MethodId*>* method_ids, - std::vector<FieldId*>* field_ids) - : type_ids_(type_ids), - string_ids_(string_ids), - method_ids_(method_ids), - field_ids_(field_ids) { } - - std::vector<TypeId*>* TypeIds() const { return type_ids_.get(); } - std::vector<StringId*>* StringIds() const { return string_ids_.get(); } - std::vector<MethodId*>* MethodIds() const { return method_ids_.get(); } - std::vector<FieldId*>* FieldIds() const { return field_ids_.get(); } + CodeFixups(std::vector<TypeId*> type_ids, + std::vector<StringId*> string_ids, + std::vector<MethodId*> method_ids, + std::vector<FieldId*> 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<TypeId*>& TypeIds() const { return type_ids_; } + const std::vector<StringId*>& StringIds() const { return string_ids_; } + const std::vector<MethodId*>& MethodIds() const { return method_ids_; } + const std::vector<FieldId*>& FieldIds() const { return field_ids_; } private: - std::unique_ptr<std::vector<TypeId*>> type_ids_; - std::unique_ptr<std::vector<StringId*>> string_ids_; - std::unique_ptr<std::vector<MethodId*>> method_ids_; - std::unique_ptr<std::vector<FieldId*>> field_ids_; + std::vector<TypeId*> type_ids_; + std::vector<StringId*> string_ids_; + std::vector<MethodId*> method_ids_; + std::vector<FieldId*> field_ids_; DISALLOW_COPY_AND_ASSIGN(CodeFixups); }; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 1fd963fe22..231826b7a8 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -37,7 +37,8 @@ Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) { disk_header.link_size_, disk_header.link_off_, disk_header.data_size_, - disk_header.data_off_); + disk_header.data_off_, + dex_file.SupportsDefaultMethods()); Collections& collections = header->GetCollections(); collections.SetEagerlyAssignOffsets(eagerly_assign_offsets); // Walk the rest of the header fields. diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 4b46341ada..e4ed69b8d2 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<dex_ir::TypeId*>* 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<dex_ir::StringId*>* 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<dex_ir::MethodId*>* 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<dex_ir::FieldId*>* 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 1fac2359b0..a18a2cfd8a 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -20,12 +20,12 @@ #include <vector> -#include "cdex/compact_dex_file.h" #include "compact_dex_writer.h" -#include "dex_file_layout.h" -#include "dex_file_types.h" +#include "dex/compact_dex_file.h" +#include "dex/dex_file_layout.h" +#include "dex/dex_file_types.h" +#include "dex/standard_dex_file.h" #include "dexlayout.h" -#include "standard_dex_file.h" #include "utf.h" namespace art { @@ -780,7 +780,7 @@ void DexWriter::WriteHeader() { header.checksum_ = header_->Checksum(); std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_); header.file_size_ = header_->FileSize(); - header.header_size_ = header_->GetSize(); + header.header_size_ = GetHeaderSize(); header.endian_tag_ = header_->EndianTag(); header.link_size_ = header_->LinkSize(); header.link_off_ = header_->LinkOffset(); @@ -801,13 +801,18 @@ void DexWriter::WriteHeader() { header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + CHECK_EQ(sizeof(header), GetHeaderSize()); static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec"); UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u)); } +size_t DexWriter::GetHeaderSize() const { + return sizeof(StandardDexFile::Header); +} + void DexWriter::WriteMemMap() { // Starting offset is right after the header. - uint32_t offset = sizeof(StandardDexFile::Header); + uint32_t offset = GetHeaderSize(); dex_ir::Collections& collection = header_->GetCollections(); @@ -893,6 +898,12 @@ void DexWriter::WriteMemMap() { 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, diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index c47898e533..92a002edc7 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -22,7 +22,7 @@ #include <functional> #include "base/unix_file/fd_file.h" -#include "cdex/compact_dex_level.h" +#include "dex/compact_dex_level.h" #include "dex_ir.h" #include "mem_map.h" #include "os.h" @@ -91,6 +91,7 @@ class DexWriter { // Header and id section virtual void WriteHeader(); + virtual size_t GetHeaderSize() const; // 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); diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index e83f98ee6b..99b1f38f73 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -26,9 +26,10 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For InitLogging. #include "base/stringpiece.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "dex_ir.h" #include "dex_ir_builder.h" #ifdef ART_TARGET_ANDROID diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index d904a52f0c..47a3e943a5 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -33,12 +33,13 @@ #include "android-base/stringprintf.h" -#include "dex_file-inl.h" -#include "dex_file_layout.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" -#include "dex_file_verifier.h" -#include "dex_instruction-inl.h" +#include "base/logging.h" // For VLOG_IS_ON. +#include "dex/dex_file-inl.h" +#include "dex/dex_file_layout.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" +#include "dex/dex_file_verifier.h" +#include "dex/dex_instruction-inl.h" #include "dex_ir_builder.h" #include "dex_verify.h" #include "dex_visualize.h" @@ -52,10 +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; - /* * Flags for use with createAccessFlagStr(). */ @@ -1593,7 +1590,7 @@ void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { } CHECK_EQ(class_data_index, class_datas.size()); - if (kChangeClassDefOrder) { + 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<dex_ir::ClassDef>::Vector& class_defs = @@ -1656,11 +1653,11 @@ void 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; @@ -1668,7 +1665,7 @@ void 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; diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 8a277b7afe..25afb773bd 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -27,8 +27,8 @@ #include <stdio.h> #include <unordered_map> -#include "cdex/compact_dex_level.h" -#include "dex_file_layout.h" +#include "dex/compact_dex_level.h" +#include "dex/dex_file_layout.h" #include "dex_ir.h" #include "mem_map.h" @@ -63,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; @@ -79,6 +80,10 @@ class DexLayoutHotnessInfo { 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, @@ -102,6 +107,10 @@ class DexLayout { 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); diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 17097f1728..5bb7196531 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -29,7 +29,9 @@ #include <sys/types.h> #include <unistd.h> -#include "base/logging.h" +#include <android-base/logging.h> + +#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 f994fd6533..b8cff6dc59 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -23,8 +23,9 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "exec_utils.h" #include "jit/profile_compilation_info.h" #include "utils.h" @@ -698,7 +699,7 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { while (it.HasNextMethod()) { DexFile::CodeItem* item = const_cast<DexFile::CodeItem*>(it.GetMethodCodeItem()); if (item != nullptr) { - IterationRange<DexInstructionIterator> instructions = item->Instructions(); + CodeItemInstructionAccessor instructions(dex, item); if (instructions.begin() != instructions.end()) { DexInstructionIterator last_instruction = instructions.begin(); for (auto dex_it = instructions.begin(); dex_it != instructions.end(); ++dex_it) { diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 3bd903de5b..348f501ef5 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -26,8 +26,10 @@ #include <stdio.h> #include <stdlib.h> -#include "dex_file-inl.h" -#include "dex_file_loader.h" +#include "base/logging.h" // For InitLogging. +#include "dex/code_item_accessors-no_art-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "mem_map.h" #include "runtime.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,8 +123,7 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; - uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); @@ -129,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); diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 39c9b9993b..bedc4576d5 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -16,12 +16,14 @@ #include <string> +#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" +#include "dex/dex_file.h" #include "noop_compiler_callbacks.h" #include "oat_file_assistant.h" #include "os.h" diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 2c800e7af3..b5f5d6ff10 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -177,7 +177,9 @@ static const MipsInstruction gMipsInstructions[] = { { kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", }, + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 5, "dinsm", "TSAJ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 6, "dinsu", "TSFz", }, + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 7, "dins", "TSAz", }, { kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f, (31 << kOpcodeShift) | (16 << 6) | 32, "seb", @@ -584,6 +586,9 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 'i': // Sign-extended lower 16-bit immediate. args << static_cast<int16_t>(instruction & 0xffff); break; + case 'J': // sz (dinsm size). + args << (rd - sa + 33); + break; case 'j': // sa value for lsa/dlsa. args << (sa + 1); break; diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp new file mode 100644 index 0000000000..1ba2323a15 --- /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 0000000000..e69de29bb2 --- /dev/null +++ b/dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION diff --git a/dt_fd_forward/NOTICE b/dt_fd_forward/NOTICE new file mode 100644 index 0000000000..4fd88fa841 --- /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 0000000000..c7ac840928 --- /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 0000000000..cf3088dc60 --- /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 <string> +#include <vector> + +#include <android-base/endian.h> +#include <android-base/logging.h> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> + +#include <sys/ioctl.h> +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include <poll.h> + +#include <jni.h> +#include <jdwpTransport.h> + +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 <typename T> static T HostToNetwork(T in); +template <typename T> 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<std::mutex> 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<std::mutex> 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<uint8_t*>(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<size_t>(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<std::mutex> 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<uint8_t*>(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<const uint8_t*>(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<std::mutex> 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<int*>(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<std::mutex> 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<std::mutex> 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 <typename T> + 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<jbyte*>(transport_->Alloc(rem)); + IOResult res = transport_->ReadFully(out, rem); + jbyte* ret = HandleResult(res, out, static_cast<jbyte*>(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<jbyte>(-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<jshort>(-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<uint8_t*>(d); + data_.insert(data_.end(), bytes, bytes + size); + } + + FdForwardTransport* transport_; + const jdwpPacket* pkt_; + std::vector<uint8_t> 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<char*>(Alloc(data.size() + 1)); + strcpy(*err, data.c_str()); + return OK; +} + +static FdForwardTransport* AsFdForward(jdwpTransportEnv* env) { + return reinterpret_cast<FdForwardTransport*>(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 <fd_num> 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 <sock_fd> + 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<char*>(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 0000000000..9303c59acd --- /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 <atomic> +#include <condition_variable> +#include <mutex> +#include <string> + +#include <android-base/logging.h> +#include <android-base/thread_annotations.h> +#include <android-base/unique_fd.h> + +#include <arpa/inet.h> +#include <sys/eventfd.h> +#include <unistd.h> +#include <poll.h> + +#include <jni.h> +#include <jvmti.h> +#include <jdwpTransport.h> + +#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<uint64_t> 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 0000000000..c3a63212b9 --- /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 0000000000..e69de29bb2 --- /dev/null +++ b/dt_fd_forward/export/MODULE_LICENSE_APACHE2 diff --git a/dt_fd_forward/export/fd_transport.h b/dt_fd_forward/export/fd_transport.h new file mode 100644 index 0000000000..245f0c2275 --- /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 <stdint.h> + +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<int*>(buf); + ibuf[0] = read_fd_; + ibuf[1] = write_fd_; + ibuf[2] = write_lock_fd_; + } + + static FdSet ReadData(void* buf) { + int* ibuf = reinterpret_cast<int*>(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 05fce96780..8aa638a0e0 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; } diff --git a/oatdump/Android.mk b/oatdump/Android.mk index 906404b18c..667c37c33d 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 2c15087612..6a99c5ab2f 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -37,12 +37,12 @@ #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" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" #include "disassembler.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/space/image_space.h" @@ -146,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(); @@ -160,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(); } @@ -1270,8 +1261,7 @@ class OatDumper { bool* addr_found) { bool success = true; - CodeItemDataAccessor code_item_accessor(CodeItemDataAccessor::CreateNullable(&dex_file, - code_item)); + CodeItemDataAccessor code_item_accessor(&dex_file, code_item); // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); @@ -2269,7 +2259,7 @@ class ImageDumper { os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str()); } else { // Grab the field type without causing resolution. - ObjPtr<mirror::Class> field_type = field->LookupType(); + ObjPtr<mirror::Class> field_type = field->LookupResolvedType(); if (field_type != nullptr) { PrettyObjectValue(os, field_type, value); } else { @@ -2385,10 +2375,10 @@ class ImageDumper { PrettyObjectValue(os, value_class, value); } } else if (obj->IsClass()) { - mirror::Class* klass = obj->AsClass(); + ObjPtr<mirror::Class> klass = obj->AsClass(); os << "SUBTYPE_CHECK_BITS: "; - SubtypeCheck<mirror::Class*>::Dump(klass, os); + SubtypeCheck<ObjPtr<mirror::Class>>::Dump(klass, os); os << "\n"; if (klass->NumStaticFields() != 0) { diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 29ebefddea..3c87fe2303 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -40,9 +40,10 @@ #include <sys/time.h> #include <unistd.h> +#include <android-base/logging.h> + #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" @@ -321,8 +322,7 @@ JNIEXPORT __attribute__((noreturn)) void JVM_Exit(jint status) { JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, - jobject javaLoader, - jstring javaLibrarySearchPath) { + jobject javaLoader) { ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == NULL) { return NULL; @@ -334,7 +334,6 @@ JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, - javaLibrarySearchPath, &error_msg); if (success) { return nullptr; @@ -386,7 +385,7 @@ JNIEXPORT void JVM_Interrupt(JNIEnv* env, jobject jthread) { JNIEXPORT jboolean JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clearInterrupted) { if (clearInterrupted) { - return static_cast<art::JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast<art::JNIEnvExt*>(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/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 62f723dec1..aae805569f 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -33,12 +33,14 @@ #include <type_traits> #include <vector> +#include <android-base/logging.h> + #include <jni.h> #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" @@ -1437,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 e8e62c2b40..2a8c2e91df 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -39,9 +39,10 @@ #include <jni.h> +#include <android-base/logging.h> + #include "deopt_manager.h" #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/strlcpy.h" #include "base/mutex.h" diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index f843054681..aced769cb5 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -37,7 +37,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex-inl.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "events-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 05f9125df9..d98fda5f9c 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -36,9 +36,8 @@ #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 "dex/dex_file_types.h" #include "gc/allocation_listener.h" #include "gc/gc_pause_listener.h" #include "gc/heap.h" @@ -139,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) { @@ -147,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) { @@ -1134,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); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index b05136661b..81edb931cd 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -20,7 +20,10 @@ #include <bitset> #include <vector> -#include "base/logging.h" +#include <android-base/logging.h> + +#include "base/macros.h" +#include "base/mutex.h" #include "jvmti.h" #include "thread.h" @@ -149,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 diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index 64dc3a5f02..ad928d9b37 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -30,8 +30,8 @@ */ #include "fixed_up_dex_file.h" -#include "dex_file_loader.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" // Runtime includes. #include "dex_to_dex_decompiler.h" diff --git a/openjdkjvmti/fixed_up_dex_file.h b/openjdkjvmti/fixed_up_dex_file.h index 4cb39cfe93..b8f349cf8c 100644 --- a/openjdkjvmti/fixed_up_dex_file.h +++ b/openjdkjvmti/fixed_up_dex_file.h @@ -39,7 +39,7 @@ #include "jvmti.h" #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" namespace openjdkjvmti { diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index 11af7b67e7..bd4c85bd7c 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 <android-base/logging.h> +#include <android-base/macros.h> + #include "jvmti.h" #include "ti_allocator.h" diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 5d20946070..699004298e 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -36,8 +36,9 @@ #include <limits> +#include <android-base/logging.h> + #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/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 8e5b56e9bf..fa7a34401d 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -38,7 +38,7 @@ #include "base/enums.h" #include "base/mutex-inl.h" #include "deopt_manager.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "events-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" @@ -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<uint32_t>(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 e69c78bab1..f9eb008af2 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -42,8 +42,8 @@ #include "class_linker.h" #include "class_table-inl.h" #include "common_throws.h" -#include "dex_file_annotations.h" -#include "dex_file_loader.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_loader.h" #include "events-inl.h" #include "fixed_up_dex_file.h" #include "gc/heap-visit-objects-inl.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_definition.cc b/openjdkjvmti/ti_class_definition.cc index c73ef0d31c..6560570136 100644 --- a/openjdkjvmti/ti_class_definition.cc +++ b/openjdkjvmti/ti_class_definition.cc @@ -33,7 +33,7 @@ #include "base/array_slice.h" #include "class_linker-inl.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "fixed_up_dex_file.h" #include "handle.h" #include "handle_scope-inl.h" diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index b551b55e18..d594d6ee29 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -33,13 +33,13 @@ #include <limits> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_jvmti.h" -#include "base/logging.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "events-inl.h" #include "gc/allocation_listener.h" #include "gc/heap.h" diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index 767e258a77..27ea3f5191 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -40,7 +40,7 @@ #include "art_method.h" #include "base/array_slice.h" #include "class_linker.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "gc_root-inl.h" #include "globals.h" #include "jni_env_ext-inl.h" diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index 500a453f78..0b4906d798 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -49,14 +49,16 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, /*out*/jint* type_out, /*out*/jint* data_length_out, /*out*/jbyte** data_out) { - constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; - if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + if (env == nullptr || type_out == nullptr || data_out == nullptr || data_length_out == nullptr) { return ERR(NULL_POINTER); - } else if (length_in < static_cast<jint>(kDdmHeaderSize)) { - // need to get type and length at least. + } 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); @@ -71,13 +73,15 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, return ERR(INTERNAL); } else { jvmtiError error = OK; - JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(env, out_data.size(), &error); - if (error != OK) { - return error; + if (!out_data.empty()) { + JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(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<jint>(out_data.size()); } - memcpy(ret.get(), out_data.data(), out_data.size()); - *data_out = ret.release(); - *data_length_out = static_cast<jint>(out_data.size()); return OK; } } diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index afd0723d0f..79a8cd6304 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -216,7 +216,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, { { "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, 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 } diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index b8691837eb..db5c31c43d 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -34,7 +34,7 @@ #include "art_field-inl.h" #include "art_jvmti.h" #include "base/enums.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "mirror/object_array-inl.h" #include "modifiers.h" diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 448ce41d0f..57fb699435 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -37,8 +37,9 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex-inl.h" -#include "dex_file_annotations.h" -#include "dex_file_types.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_types.h" #include "events-inl.h" #include "jit/jit.h" #include "jni_internal.h" @@ -122,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; } @@ -167,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); } @@ -190,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); } @@ -260,13 +266,10 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, - 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 { @@ -298,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); } @@ -413,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); } @@ -462,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()); @@ -477,15 +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; - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); bool success = dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, CollectLineNumbers, &context); + accessor.DebugInfoOffset(), CollectLineNumbers, &context); if (!success) { return ERR(ABSENT_INFORMATION); } @@ -565,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; } @@ -613,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); } @@ -653,9 +658,10 @@ class CommonLocalVariableClosure : public art::Closure { }; GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, + 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 7db0566a2e..94408ba186 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -169,6 +169,7 @@ class JvmtiMonitor { } size_t old_count = count_; + DCHECK_GT(old_count, 0u); count_ = 0; owner_.store(nullptr, std::memory_order_relaxed); @@ -176,12 +177,19 @@ class JvmtiMonitor { { std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock); how_to_wait(lk); - lk.release(); // Do not unlock the mutex. + // Here we release the mutex. We will get it back below. We first need to do a suspend-check + // without holding it however. This is done in the MonitorEnter function. + // TODO We could do this more efficiently. + // We hold the mutex_ but the overall monitor is not owned at this point. + CHECK(owner_.load(std::memory_order_relaxed) == nullptr); + DCHECK_EQ(0u, count_); } - DCHECK(owner_.load(std::memory_order_relaxed) == nullptr); - owner_.store(self, std::memory_order_relaxed); - DCHECK_EQ(0u, count_); + // Reaquire the mutex/monitor, also go to sleep if we were suspended. + MonitorEnter(self); + CHECK(owner_.load(std::memory_order_relaxed) == self); + DCHECK_EQ(1u, count_); + // Reset the count. count_ = old_count; return true; diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 5b125f6990..6194d1e42c 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -33,19 +33,19 @@ #include <limits> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #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" -#include "dex_file.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" #include "events-inl.h" #include "gc/allocation_listener.h" #include "gc/heap.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 528563bd07..b537e1b01c 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -40,7 +40,7 @@ #include "art_method.h" #include "base/array_ref.h" #include "class_linker.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "gc_root-inl.h" #include "globals.h" #include "jni_env_ext-inl.h" diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index fe12a25151..9d5f4ea3f9 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -38,8 +38,8 @@ #include "base/enums.h" #include "base/macros.h" #include "class_linker.h" -#include "dex_file.h" -#include "dex_file_loader.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object.h" diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index b43eaa0286..bc77753f8f 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -44,9 +44,10 @@ #include "base/bit_utils.h" #include "base/enums.h" #include "base/mutex.h" -#include "dex_file.h" -#include "dex_file_annotations.h" -#include "dex_file_types.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_types.h" #include "gc_root.h" #include "handle_scope-inl.h" #include "jni_env_ext.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 b7b81ce358..555c5a725b 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 <android-base/logging.h> +#include <android-base/strings.h> + #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" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 1d7f137f2b..3a5fcccf35 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -37,8 +37,8 @@ #include "art_method.h" #include "base/array_ref.h" #include "class_linker.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "events-inl.h" #include "gc_root-inl.h" #include "globals.h" diff --git a/patchoat/Android.bp b/patchoat/Android.bp index d3bc2a754b..0902823644 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 ae82d72c0f..eb648cba18 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 0000000000..86e851c72b --- /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 <dirent.h> +#include <sys/types.h> + +#include <string> +#include <vector> + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "dexopt_test.h" +#include "runtime.h" + +#include <gtest/gtest.h> + +namespace art { + +using android::base::StringPrintf; + +class PatchoatTest : public DexoptTest { + public: + static bool ListDirFilesEndingWith( + const std::string& dir, + const std::string& suffix, + std::vector<std::string>* 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<std::string>& args, const std::string& arg) { + args.push_back("--runtime-arg"); + args.push_back(arg); + } + + bool CompileBootImage(const std::vector<std::string>& extra_args, + const std::string& image_file_name_prefix, + uint32_t base_addr, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector<std::string> argv; + argv.push_back(runtime->GetCompilerExecutable()); + AddRuntimeArg(argv, "-Xms64m"); + AddRuntimeArg(argv, "-Xmx64m"); + std::vector<std::string> 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<std::string> 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<std::string> 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<std::string>& 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<const char*> 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<char* const*>(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<std::string>& 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<File> 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<File> 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<uint8_t>* contents, std::string* error_msg) { + std::unique_ptr<File> 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<uint8_t> 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<uint8_t> 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<std::string> 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<std::string> dex2oat_image_basenames; + std::vector<std::string> 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-<random>-<random>@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<std::string> 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 48b87c9a6d..a750105e72 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -18,7 +18,7 @@ #include <set> #include "boot_image_profile.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "method_reference.h" #include "type_reference.h" diff --git a/profman/boot_image_profile.h b/profman/boot_image_profile.h index d02e408cd5..eb43b7ca7f 100644 --- a/profman/boot_image_profile.h +++ b/profman/boot_image_profile.h @@ -21,7 +21,7 @@ #include <memory> #include <vector> -#include "dex_file.h" +#include "dex/dex_file.h" #include "jit/profile_compilation_info.h" namespace art { diff --git a/profman/profman.cc b/profman/profman.cc index a5a5546323..71f7f9d669 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -32,15 +32,17 @@ #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" #include "base/unix_file/fd_file.h" #include "boot_image_profile.h" #include "bytecode_utils.h" -#include "dex_file.h" -#include "dex_file_loader.h" -#include "dex_file_types.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" #include "jit/profile_compilation_info.h" #include "profile_assistant.h" #include "runtime.h" @@ -725,7 +727,7 @@ class ProfMan FINAL { const DexFile::CodeItem* code_item = dex_file->GetCodeItem(offset); bool found_invoke = false; - for (const DexInstructionPcPair& inst : code_item->Instructions()) { + for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { if (inst->Opcode() == Instruction::INVOKE_VIRTUAL) { if (found_invoke) { LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " diff --git a/runtime/Android.bp b/runtime/Android.bp index a136ccb9d0..2657f4fa86 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", @@ -47,7 +48,6 @@ cc_defaults { "base/timing_logger.cc", "base/unix_file/fd_file.cc", "base/unix_file/random_access_file_utils.cc", - "cdex/compact_dex_file.cc", "cha.cc", "check_jni.cc", "class_linker.cc", @@ -56,13 +56,16 @@ cc_defaults { "common_throws.cc", "compiler_filter.cc", "debugger.cc", - "dex_file.cc", - "dex_file_loader.cc", - "dex_file_annotations.cc", - "dex_file_layout.cc", - "dex_file_tracking_registrar.cc", - "dex_file_verifier.cc", - "dex_instruction.cc", + "dex/compact_dex_file.cc", + "dex/dex_file.cc", + "dex/dex_file_annotations.cc", + "dex/dex_file_exception_helpers.cc", + "dex/dex_file_layout.cc", + "dex/dex_file_loader.cc", + "dex/dex_file_tracking_registrar.cc", + "dex/dex_file_verifier.cc", + "dex/dex_instruction.cc", + "dex/standard_dex_file.cc", "dex_to_dex_decompiler.cc", "elf_file.cc", "exec_utils.cc", @@ -212,7 +215,6 @@ cc_defaults { "signal_catcher.cc", "stack.cc", "stack_map.cc", - "standard_dex_file.cc", "thread.cc", "thread_list.cc", "thread_pool.cc", @@ -452,10 +454,10 @@ gensrcs { "debugger.h", "base/unix_file/fd_file.h", "class_status.h", - "dex_file.h", - "dex_file_layout.h", - "dex_instruction.h", - "dex_instruction_utils.h", + "dex/dex_file.h", + "dex/dex_file_layout.h", + "dex/dex_instruction.h", + "dex/dex_instruction_utils.h", "gc_root.h", "gc/allocator_type.h", "gc/allocator/rosalloc.h", @@ -469,6 +471,7 @@ gensrcs { "instrumentation.h", "indirect_reference_table.h", "invoke_type.h", + "jdwp_provider.h", "jdwp/jdwp.h", "jdwp/jdwp_constants.h", "lock_word.h", @@ -563,16 +566,16 @@ art_cc_test { "base/transform_iterator_test.cc", "base/variant_map_test.cc", "base/unix_file/fd_file_test.cc", - "cdex/compact_dex_file_test.cc", "cha_test.cc", "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", - "dex_instruction_test.cc", + "dex/code_item_accessors_test.cc", + "dex/compact_dex_file_test.cc", + "dex/dex_file_test.cc", + "dex/dex_file_verifier_test.cc", + "dex/dex_instruction_test.cc", "entrypoints/math_entrypoints_test.cc", "entrypoints/quick/quick_trampoline_entrypoints_test.cc", "entrypoints_order_test.cc", @@ -601,6 +604,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/aot_class_linker.cc b/runtime/aot_class_linker.cc index a62cbec1be..93e02eff69 100644 --- a/runtime/aot_class_linker.cc +++ b/runtime/aot_class_linker.cc @@ -44,7 +44,7 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // Don't initialize klass if it's superclass is not initialized, because superclass might abort // the transaction and rolled back after klass's change is commited. if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) { - if (klass->GetSuperClass()->GetStatus() == mirror::Class::kStatusInitializing) { + if (klass->GetSuperClass()->GetStatus() == ClassStatus::kInitializing) { runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve " + klass->PrettyTypeOf() + " because it's superclass is not initialized."); return false; @@ -79,11 +79,11 @@ verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self, ClassStatus old_status = callbacks->GetPreviousClassState( ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex())); // Was it verified? Report no failure. - if (old_status >= ClassStatus::kStatusVerified) { + if (old_status >= ClassStatus::kVerified) { return verifier::FailureKind::kNoFailure; } // Does it need to be verified at runtime? Report soft failure. - if (old_status >= ClassStatus::kStatusRetryVerificationAtRuntime) { + if (old_status >= ClassStatus::kRetryVerificationAtRuntime) { // Error messages from here are only reported through -verbose:class. It is not worth it to // create a message. return verifier::FailureKind::kSoftFailure; diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h index fa9aa46d4d..b9802967fe 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 <android-base/logging.h> + #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 ef2b34236f..315bf957cc 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 b789fc7481..801254fd30 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -25,10 +25,9 @@ #include <fstream> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "base/logging.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> #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 6ec9c48b92..c09baea72a 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1026,8 +1026,8 @@ ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitia ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode -// Note: Functions `art{Get,Set}<Kind>{Static,Instance>FromCompiledCode` are -// defined by macros in runtime/entrypoints/quick/quick_field_entrypoints.cc. +// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are +// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. /* * Called by managed code to resolve a static field and load a non-wide value. diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc index ff4f81be0f..18585c7973 100644 --- a/runtime/arch/arm/thread_arm.cc +++ b/runtime/arch/arm/thread_arm.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include <android-base/logging.h> + #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 36aded07c4..e64cfb86ea 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 <android-base/logging.h> + #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 d535c7e3c6..d282c8cfc0 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 d830ccffbb..9e9cb16008 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -19,10 +19,10 @@ #include <fstream> #include <sstream> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> -#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 47efeb9200..96a1cadab9 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1638,8 +1638,8 @@ ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitia ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode -// Note: Functions `art{Get,Set}<Kind>{Static,Instance>FromCompiledCode` are -// defined by macros in runtime/entrypoints/quick/quick_field_entrypoints.cc. +// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are +// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 diff --git a/runtime/arch/arm64/thread_arm64.cc b/runtime/arch/arm64/thread_arm64.cc index 3483b704ea..19c4a6ac85 100644 --- a/runtime/arch/arm64/thread_arm64.cc +++ b/runtime/arch/arm64/thread_arm64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include <android-base/logging.h> + #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 ab04b1eaa7..8e8dde4c4c 100644 --- a/runtime/arch/code_offset.h +++ b/runtime/arch/code_offset.h @@ -19,8 +19,10 @@ #include <iosfwd> +#include <android-base/logging.h> + #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 67e2f358c8..1e3275cc00 100644 --- a/runtime/arch/instruction_set_features_test.cc +++ b/runtime/arch/instruction_set_features_test.cc @@ -19,12 +19,11 @@ #include <gtest/gtest.h> #ifdef ART_TARGET_ANDROID -#include "android-base/properties.h" +#include <android-base/properties.h> #endif -#include "android-base/stringprintf.h" - -#include "base/logging.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> namespace art { diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h index 7dcff630d1..7e073b288a 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 <android-base/logging.h> + #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 dca3382664..209f36705a 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 6dce54e5c5..f82dc08cb2 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 6d4145bc98..952ed250d2 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -19,10 +19,9 @@ #include <fstream> #include <sstream> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include <android-base/stringprintf.h> +#include <android-base/strings.h> -#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 ee539edf3a..76bc639277 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 <android-base/logging.h> + #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 fc77a641b3..b2f7e10f52 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1637,6 +1637,9 @@ END \name /* * Called by managed code to resolve a static/instance field and load/store a value. + * + * Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are + * defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. */ ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 57af150b33..c7f9a3e74e 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #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 0a9ab7aacd..0be7a7f4cb 100644 --- a/runtime/arch/mips/thread_mips.cc +++ b/runtime/arch/mips/thread_mips.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include <android-base/logging.h> + #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 89fbf8ffc3..b2a6138471 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 <android-base/logging.h> + #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 bdce520937..ba6fff05ad 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 3fb83d9232..63f4f6cb8c 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1565,6 +1565,9 @@ END \name /* * Called by managed code to resolve a static/instance field and load/store a value. + * + * Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are + * defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. */ ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_IF_NO_EXCEPTION diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index 30de2cc009..d3a24b6202 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #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 3ce5e50d57..c1c390beeb 100644 --- a/runtime/arch/mips64/thread_mips64.cc +++ b/runtime/arch/mips64/thread_mips64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include <android-base/logging.h> + #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 303dfe361c..0ebb22bd6d 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 <android-base/logging.h> + #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 527332fe9a..e6a91247cb 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 ea5a90d8ee..98462512da 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -19,11 +19,11 @@ #include <fstream> #include <sstream> -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> #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 a46ceeba12..93cb6656dc 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1709,6 +1709,9 @@ DEFINE_FUNCTION art_quick_lushr ret END_FUNCTION art_quick_lushr +// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are +// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. + ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index 23027ed7d7..ded3520c76 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #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 f8e2845983..d242693f81 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 <android-base/logging.h> + #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 463e5a279f..85f972309b 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1594,6 +1594,9 @@ UNIMPLEMENTED art_quick_lshl UNIMPLEMENTED art_quick_lshr UNIMPLEMENTED art_quick_lushr +// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are +// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc. + THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCompiledCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCompiledCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCode, RETURN_IF_EAX_ZERO diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index dda1d5f569..4f2243170e 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -19,7 +19,8 @@ #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/macros.h" #include "globals.h" diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 4a328e8d60..99634a067b 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -19,9 +19,10 @@ #include "art_field.h" -#include "base/logging.h" +#include <android-base/logging.h> + #include "class_linker.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" #include "jvalue.h" @@ -299,23 +300,17 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetTypeAsPrimitiveType() != Primitive::kPrimNot; } -inline ObjPtr<mirror::Class> ArtField::LookupType() { +inline ObjPtr<mirror::Class> ArtField::LookupResolvedType() { ScopedAssertNoThreadSuspension ants(__FUNCTION__); const uint32_t field_index = GetDexFieldIndex(); ObjPtr<mirror::Class> declaring_class = GetDeclaringClass(); if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - ObjPtr<mirror::DexCache> 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<mirror::Class> 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<mirror::Class> type = Runtime::Current()->GetClassLinker()->LookupResolvedType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr<mirror::Class> ArtField::ResolveType() { @@ -324,15 +319,9 @@ inline ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::Class> 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<mirror::String> ArtField::GetStringName(Thread* self, bool resolve auto dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); ObjPtr<mirror::DexCache> 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<mirror::String> 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 bc728f4476..dbba2b0918 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -45,17 +45,17 @@ void ArtField::SetOffset(MemberOffset num_bytes) { ObjPtr<mirror::Class> ArtField::ProxyFindSystemClass(const char* descriptor) { DCHECK(GetDeclaringClass()->IsProxyClass()); - return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupClass( + Thread::Current(), descriptor, /* class_loader */ nullptr); + DCHECK(klass != nullptr); + return klass; } ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr<mirror::DexCache> 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 866bf0bc70..46b013da7e 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -19,7 +19,7 @@ #include <jni.h> -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "gc_root.h" #include "modifiers.h" #include "obj_ptr.h" @@ -205,7 +205,7 @@ class ArtField FINAL { bool IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> LookupType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> LookupResolvedType() REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::Class> ResolveType() REQUIRES_SHARED(Locks::mutator_lock_); size_t FieldSize() REQUIRES_SHARED(Locks::mutator_lock_); @@ -234,7 +234,6 @@ class ArtField FINAL { ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::String> ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 31abf94889..bdebe2d9e9 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -21,13 +21,12 @@ #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" -#include "dex_file_types.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_types.h" #include "gc_root-inl.h" #include "invoke_type.h" #include "jit/profiling_info.h" @@ -81,9 +80,8 @@ inline bool ArtMethod::CASDeclaringClass(mirror::Class* expected_class, mirror::Class* desired_class) { GcRoot<mirror::Class> expected_root(expected_class); GcRoot<mirror::Class> desired_root(desired_class); - return reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&declaring_class_)-> - CompareExchangeStrongSequentiallyConsistent( - expected_root, desired_root); + auto atomic_root_class = reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&declaring_class_); + return atomic_root_class->CompareAndSetStrongSequentiallyConsistent(expected_root, desired_root); } inline uint16_t ArtMethod::GetMethodIndex() { @@ -107,23 +105,16 @@ inline uint32_t ArtMethod::GetDexMethodIndex() { inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); - ObjPtr<mirror::Class> 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<mirror::Class> type = + Runtime::Current()->GetClassLinker()->LookupResolvedType(type_idx, this); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr<mirror::Class> ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) { - ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); - ObjPtr<mirror::Class> 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<mirror::Class> type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); + return type; } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -306,9 +297,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() { @@ -469,16 +458,8 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi } } -inline IterationRange<DexInstructionIterator> ArtMethod::DexInstructions() { - CodeItemInstructionAccessor accessor(this); - return { accessor.begin(), - accessor.end() }; -} - -inline IterationRange<DexInstructionIterator> ArtMethod::NullableDexInstructions() { - CodeItemInstructionAccessor accessor(CodeItemInstructionAccessor::CreateNullable(this)); - return { accessor.begin(), - accessor.end() }; +inline CodeItemInstructionAccessor ArtMethod::DexInstructions() { + return CodeItemInstructionAccessor(this); } } // namespace art diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 43a51391b9..44a5dde485 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -25,8 +25,9 @@ #include "base/stringpiece.h" #include "class_linker-inl.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_instruction.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_instruction.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "interpreter/interpreter.h" @@ -134,15 +135,14 @@ uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { return dex_file->GetIndexForClassDef(*class_def); } -mirror::String* ArtMethod::GetNameAsString(Thread* self) { +ObjPtr<mirror::String> ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); Handle<mirror::DexCache> 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() { @@ -264,7 +264,6 @@ uint32_t ArtMethod::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfil uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, uint32_t dex_pc, bool* has_no_move_exception) { - const DexFile::CodeItem* code_item = GetCodeItem(); // Set aside the exception while we resolve its type. Thread* self = Thread::Current(); StackHandleScope<1> hs(self); @@ -273,7 +272,8 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // Default to handler not found. uint32_t found_dex_pc = dex::kDexNoIndex; // Iterate over the catch handlers associated with dex_pc. - for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) { + CodeItemDataAccessor accessor(this); + for (CatchHandlerIterator it(accessor, dex_pc); it.HasNext(); it.Next()) { dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case if (!iter_type_idx.IsValid()) { @@ -298,9 +298,8 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> 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 = accessor.InstructionAt(found_dex_pc); + *has_no_move_exception = (first_catch_instr.Opcode() != Instruction::MOVE_EXCEPTION); } // Put the exception back. if (exception != nullptr) { @@ -550,8 +549,8 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> 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<mirror::Class> type = cl->ResolveType(type_idx, this); if (type == nullptr) { Thread::Current()->AssertPendingException(); return false; diff --git a/runtime/art_method.h b/runtime/art_method.h index 0a592e0528..c4a586ed92 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -19,13 +19,17 @@ #include <cstddef> +#include <android-base/logging.h> + #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" #include "base/iteration_range.h" -#include "base/logging.h" -#include "dex_file.h" -#include "dex_instruction_iterator.h" +#include "base/macros.h" +#include "base/runtime_debug.h" +#include "dex/code_item_accessors.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction_iterator.h" #include "gc_root.h" #include "modifiers.h" #include "obj_ptr.h" @@ -575,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<mirror::String> GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); @@ -713,13 +717,9 @@ class ArtMethod FINAL { "ptr_sized_fields_.entry_point_from_quick_compiled_code_"); } - // Returns the dex instructions of the code item for the art method. Must not be called on null - // code items. - ALWAYS_INLINE IterationRange<DexInstructionIterator> DexInstructions() - REQUIRES_SHARED(Locks::mutator_lock_); - - // Handles a null code item by returning iterators that have a null address. - ALWAYS_INLINE IterationRange<DexInstructionIterator> NullableDexInstructions() + // 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: diff --git a/runtime/atomic.h b/runtime/atomic.h index d8621cc2e6..0e2f056d3a 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -22,8 +22,9 @@ #include <limits> #include <vector> +#include <android-base/logging.h> + #include "arch/instruction_set.h" -#include "base/logging.h" #include "base/macros.h" namespace art { @@ -216,70 +217,109 @@ class PACKED(sizeof(T)) Atomic : public std::atomic<T> { } // Store to memory without ordering or synchronization constraints. - void StoreRelaxed(T desired) { - this->store(desired, std::memory_order_relaxed); + void StoreRelaxed(T desired_value) { + this->store(desired_value, std::memory_order_relaxed); } // Word tearing allowed, but may race. - void StoreJavaData(T desired) { - this->store(desired, std::memory_order_relaxed); + void StoreJavaData(T desired_value) { + this->store(desired_value, std::memory_order_relaxed); } // Store to memory with release ordering. - void StoreRelease(T desired) { - this->store(desired, std::memory_order_release); + void StoreRelease(T desired_value) { + this->store(desired_value, std::memory_order_release); } // Store to memory with a total ordering. - void StoreSequentiallyConsistent(T desired) { - this->store(desired, std::memory_order_seq_cst); + void StoreSequentiallyConsistent(T desired_value) { + this->store(desired_value, std::memory_order_seq_cst); } - // Atomically replace the value with desired value. + // Atomically replace the value with desired_value. T ExchangeRelaxed(T desired_value) { return this->exchange(desired_value, std::memory_order_relaxed); } - // Atomically replace the value with desired value if it matches the expected value. + // Atomically replace the value with desired_value. + T ExchangeSequentiallyConsistent(T desired_value) { + return this->exchange(desired_value, std::memory_order_seq_cst); + } + + // Atomically replace the value with desired_value. + T ExchangeAcquire(T desired_value) { + return this->exchange(desired_value, std::memory_order_acquire); + } + + // Atomically replace the value with desired_value. + T ExchangeRelease(T desired_value) { + return this->exchange(desired_value, std::memory_order_release); + } + + // Atomically replace the value with desired_value if it matches the expected_value. + // Participates in total ordering of atomic operations. Returns true on success, false otherwise. + // If the value does not match, updates the expected_value argument with the value that was + // atomically read for the failed comparison. + bool CompareAndExchangeStrongSequentiallyConsistent(T* expected_value, T desired_value) { + return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_seq_cst); + } + + // Atomically replace the value with desired_value if it matches the expected_value. + // Participates in total ordering of atomic operations. Returns true on success, false otherwise. + // If the value does not match, updates the expected_value argument with the value that was + // atomically read for the failed comparison. + bool CompareAndExchangeStrongAcquire(T* expected_value, T desired_value) { + return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_acquire); + } + + // Atomically replace the value with desired_value if it matches the expected_value. + // Participates in total ordering of atomic operations. Returns true on success, false otherwise. + // If the value does not match, updates the expected_value argument with the value that was + // atomically read for the failed comparison. + bool CompareAndExchangeStrongRelease(T* expected_value, T desired_value) { + return this->compare_exchange_strong(*expected_value, desired_value, std::memory_order_release); + } + + // Atomically replace the value with desired_value if it matches the expected_value. // Participates in total ordering of atomic operations. - bool CompareExchangeStrongSequentiallyConsistent(T expected_value, T desired_value) { + bool CompareAndSetStrongSequentiallyConsistent(T expected_value, T desired_value) { return this->compare_exchange_strong(expected_value, desired_value, std::memory_order_seq_cst); } // The same, except it may fail spuriously. - bool CompareExchangeWeakSequentiallyConsistent(T expected_value, T desired_value) { + bool CompareAndSetWeakSequentiallyConsistent(T expected_value, T desired_value) { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_seq_cst); } - // Atomically replace the value with desired value if it matches the expected value. Doesn't + // Atomically replace the value with desired_value if it matches the expected_value. Doesn't // imply ordering or synchronization constraints. - bool CompareExchangeStrongRelaxed(T expected_value, T desired_value) { + bool CompareAndSetStrongRelaxed(T expected_value, T desired_value) { return this->compare_exchange_strong(expected_value, desired_value, std::memory_order_relaxed); } - // Atomically replace the value with desired value if it matches the expected value. Prior writes + // Atomically replace the value with desired_value if it matches the expected_value. Prior writes // to other memory locations become visible to the threads that do a consume or an acquire on the // same location. - bool CompareExchangeStrongRelease(T expected_value, T desired_value) { + bool CompareAndSetStrongRelease(T expected_value, T desired_value) { return this->compare_exchange_strong(expected_value, desired_value, std::memory_order_release); } // The same, except it may fail spuriously. - bool CompareExchangeWeakRelaxed(T expected_value, T desired_value) { + bool CompareAndSetWeakRelaxed(T expected_value, T desired_value) { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_relaxed); } - // Atomically replace the value with desired value if it matches the expected value. Prior writes + // Atomically replace the value with desired_value if it matches the expected_value. Prior writes // made to other memory locations by the thread that did the release become visible in this // thread. - bool CompareExchangeWeakAcquire(T expected_value, T desired_value) { + bool CompareAndSetWeakAcquire(T expected_value, T desired_value) { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_acquire); } - // Atomically replace the value with desired value if it matches the expected value. prior writes + // Atomically replace the value with desired_value if it matches the expected_value. Prior writes // to other memory locations become visible to the threads that do a consume or an acquire on the // same location. - bool CompareExchangeWeakRelease(T expected_value, T desired_value) { + bool CompareAndSetWeakRelease(T expected_value, T desired_value) { return this->compare_exchange_weak(expected_value, desired_value, std::memory_order_release); } @@ -291,6 +331,14 @@ class PACKED(sizeof(T)) Atomic : public std::atomic<T> { return this->fetch_add(value, std::memory_order_relaxed); // Return old_value. } + T FetchAndAddAcquire(const T value) { + return this->fetch_add(value, std::memory_order_acquire); // Return old_value. + } + + T FetchAndAddRelease(const T value) { + return this->fetch_add(value, std::memory_order_acquire); // Return old_value. + } + T FetchAndSubSequentiallyConsistent(const T value) { return this->fetch_sub(value, std::memory_order_seq_cst); // Return old value. } @@ -299,12 +347,40 @@ class PACKED(sizeof(T)) Atomic : public std::atomic<T> { return this->fetch_sub(value, std::memory_order_relaxed); // Return old value. } - T FetchAndOrSequentiallyConsistent(const T value) { + T FetchAndBitwiseAndSequentiallyConsistent(const T value) { + return this->fetch_and(value, std::memory_order_seq_cst); // Return old_value. + } + + T FetchAndBitwiseAndAcquire(const T value) { + return this->fetch_and(value, std::memory_order_acquire); // Return old_value. + } + + T FetchAndBitwiseAndRelease(const T value) { + return this->fetch_and(value, std::memory_order_release); // Return old_value. + } + + T FetchAndBitwiseOrSequentiallyConsistent(const T value) { return this->fetch_or(value, std::memory_order_seq_cst); // Return old_value. } - T FetchAndAndSequentiallyConsistent(const T value) { - return this->fetch_and(value, std::memory_order_seq_cst); // Return old_value. + T FetchAndBitwiseOrAcquire(const T value) { + return this->fetch_or(value, std::memory_order_acquire); // Return old_value. + } + + T FetchAndBitwiseOrRelease(const T value) { + return this->fetch_or(value, std::memory_order_release); // Return old_value. + } + + T FetchAndBitwiseXorSequentiallyConsistent(const T value) { + return this->fetch_xor(value, std::memory_order_seq_cst); // Return old_value. + } + + T FetchAndBitwiseXorAcquire(const T value) { + return this->fetch_xor(value, std::memory_order_acquire); // Return old_value. + } + + T FetchAndBitwiseXorRelease(const T value) { + return this->fetch_xor(value, std::memory_order_release); // Return old_value. } volatile T* Address() { diff --git a/runtime/barrier.cc b/runtime/barrier.cc index 9bcda35a9d..4329a5a245 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -16,7 +16,9 @@ #include "barrier.h" -#include "base/logging.h" +#include <android-base/logging.h> + +#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 0000000000..8906c96ea7 --- /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 <atomic> + +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<unsigned int> gAborting; + +} // namespace art + +#endif // ART_RUNTIME_BASE_ABORTING_H_ diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc index bb006389fa..2da88c3830 100644 --- a/runtime/base/allocator.cc +++ b/runtime/base/allocator.cc @@ -19,8 +19,9 @@ #include <inttypes.h> #include <stdlib.h> +#include <android-base/logging.h> + #include "atomic.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h index 99cdb49984..3cedb66abe 100644 --- a/runtime/base/allocator.h +++ b/runtime/base/allocator.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_BASE_ALLOCATOR_H_ #define ART_RUNTIME_BASE_ALLOCATOR_H_ +#include <type_traits> + #include "atomic.h" #include "base/macros.h" #include "base/mutex.h" -#include "base/type_static_if.h" namespace art { @@ -147,9 +148,9 @@ class TrackingAllocatorImpl : public std::allocator<T> { template<class T, AllocatorTag kTag> // C++ doesn't allow template typedefs. This is a workaround template typedef which is // TrackingAllocatorImpl<T> if kEnableTrackingAllocator is true, std::allocator<T> otherwise. -using TrackingAllocator = typename TypeStaticIf<kEnableTrackingAllocator, - TrackingAllocatorImpl<T, kTag>, - std::allocator<T>>::type; +using TrackingAllocator = typename std::conditional<kEnableTrackingAllocator, + TrackingAllocatorImpl<T, kTag>, + std::allocator<T>>::type; } // namespace art diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 2e35f8ac4f..cc413c5ab9 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -23,7 +23,8 @@ #include <iomanip> #include <numeric> -#include "logging.h" +#include <android-base/logging.h> + #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 a327cb0a8b..9e03658aef 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -20,11 +20,11 @@ #include <stddef.h> #include <stdint.h> -#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_object.h b/runtime/base/arena_object.h index ed00babd62..06884c23d4 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 <android-base/logging.h> + +#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 630a036f3d..ef86512cf7 100644 --- a/runtime/base/array_ref.h +++ b/runtime/base/array_ref.h @@ -20,7 +20,7 @@ #include <type_traits> #include <vector> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index a80ca28d2e..86007d6a35 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 <android-base/logging.h> + #include "globals.h" -#include "logging.h" namespace art { diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index a2164f335d..bfbe8eaf71 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] * @@ -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_utils.h b/runtime/base/bit_utils.h index 5d836545e9..34cddbff6a 100644 --- a/runtime/base/bit_utils.h +++ b/runtime/base/bit_utils.h @@ -20,7 +20,8 @@ #include <limits> #include <type_traits> -#include "base/logging.h" +#include <android-base/logging.h> + #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 8514de6b75..2d3d0508cc 100644 --- a/runtime/base/bit_utils_iterator.h +++ b/runtime/base/bit_utils_iterator.h @@ -21,9 +21,10 @@ #include <limits> #include <type_traits> +#include <android-base/logging.h> + #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 0e67f77e19..e67d4e25eb 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 <android-base/logging.h> + +#include "base/bit_utils.h" namespace art { diff --git a/runtime/base/bounded_fifo.h b/runtime/base/bounded_fifo.h index 7bcd382022..1520770fe6 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 <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/casts.h b/runtime/base/casts.h index 92c493ace7..3c6b2be355 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -24,8 +24,9 @@ #include <limits> #include <type_traits> -#include "base/logging.h" -#include "base/macros.h" +#include <android-base/logging.h> + +#include "stl_util_identity.h" namespace art { @@ -98,7 +99,7 @@ inline Dest bit_cast(const Source& source) { // A version of static_cast that DCHECKs that the value can be precisely represented // when converting to Dest. template <typename Dest, typename Source> -inline Dest dchecked_integral_cast(const Source source) { +constexpr Dest dchecked_integral_cast(Source source) { DCHECK( // Check that the value is within the lower limit of Dest. (static_cast<intmax_t>(std::numeric_limits<Dest>::min()) <= @@ -114,6 +115,33 @@ inline Dest dchecked_integral_cast(const Source source) { return static_cast<Dest>(source); } +// A version of dchecked_integral_cast casting between an integral type and an enum type. +// When casting to an enum type, the cast does not check if the value corresponds to an enumerator. +// When casting from an enum type, the target type can be omitted and the enum's underlying type +// shall be used. + +template <typename Dest, typename Source> +constexpr +typename std::enable_if<!std::is_enum<Source>::value, Dest>::type +enum_cast(Source value) { + return static_cast<Dest>( + dchecked_integral_cast<typename std::underlying_type<Dest>::type>(value)); +} + +template <typename Dest = void, typename Source> +constexpr +typename std::enable_if<std::is_enum<Source>::value, + typename std::conditional<std::is_same<Dest, void>::value, + std::underlying_type<Source>, + Identity<Dest>>::type>::type::type +enum_cast(Source value) { + using return_type = typename std::conditional<std::is_same<Dest, void>::value, + std::underlying_type<Source>, + Identity<Dest>>::type::type; + return dchecked_integral_cast<return_type>( + static_cast<typename std::underlying_type<Source>::type>(value)); +} + // A version of reinterpret_cast<>() between pointers and int64_t/uint64_t // that goes through uintptr_t to avoid treating the pointer as "signed." diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h index 77f0ea2b7c..7236ac301a 100644 --- a/runtime/base/dchecked_vector.h +++ b/runtime/base/dchecked_vector.h @@ -21,7 +21,7 @@ #include <type_traits> #include <vector> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/base/debug_stack.h b/runtime/base/debug_stack.h index 886065db30..1331e10a02 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 <android-base/logging.h> +#include <android-base/macros.h> + #include "globals.h" namespace art { diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc index dffb9b43a1..e668699a0b 100644 --- a/runtime/base/file_magic.cc +++ b/runtime/base/file_magic.cc @@ -20,11 +20,11 @@ #include <sys/stat.h> #include <sys/types.h> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> -#include "base/logging.h" #include "base/unix_file/fd_file.h" -#include "dex_file.h" +#include "dex/dex_file.h" namespace art { diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 323a06519d..63b4ac56d0 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -47,9 +47,9 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "dex_instruction.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_instruction.h" #include "oat_quick_method_header.h" #include "os.h" #include "scoped_thread_state_change-inl.h" @@ -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 007f3b443d..e4555ad3cb 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -21,13 +21,14 @@ #include <string> +#include <android-base/logging.h> + #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 c743342a98..47e6d93346 100644 --- a/runtime/base/hash_set.h +++ b/runtime/base/hash_set.h @@ -25,8 +25,10 @@ #include <type_traits> #include <utility> +#include <android-base/logging.h> + #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 be2092040d..3ce0140c84 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -24,6 +24,8 @@ #include "histogram.h" +#include <android-base/logging.h> + #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 e0c921e408..7544a9c918 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -19,7 +19,7 @@ #include <string> #include <vector> -#include "base/logging.h" +#include <android-base/macros.h> namespace art { diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 4776357fdf..90eb74c75c 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -20,7 +20,8 @@ #include <limits> #include <sstream> -#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<bool*>& GetFlagPtrs() { - static std::vector<bool*> 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<unsigned int> gAborting(0); diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 15f935395e..c562bdf59f 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<unsigned int> 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 d380b9eccc..404e080b03 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 6cd7d60253..512e5ce651 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -59,6 +59,10 @@ template<typename T> 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 587b092ab7..01adbf17e2 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -164,7 +164,7 @@ inline void ReaderWriterMutex::SharedLock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (LIKELY(cur_state >= 0)) { // Add as an extra reader. - done = state_.CompareExchangeWeakAcquire(cur_state, cur_state + 1); + done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); } else { HandleSharedLockContention(self, cur_state); } @@ -188,10 +188,10 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (LIKELY(cur_state > 0)) { // Reduce state by 1 and impose lock release load/store ordering. - // Note, the relaxed loads below musn't reorder before the CompareExchange. + // Note, the relaxed loads below musn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing // a status bit into the state on contention. - done = state_.CompareExchangeWeakSequentiallyConsistent(cur_state, cur_state - 1); + done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, cur_state - 1); if (done && (cur_state - 1) == 0) { // Weak CAS may fail spuriously. if (num_pending_writers_.LoadRelaxed() > 0 || num_pending_readers_.LoadRelaxed() > 0) { diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 3adebe7e00..9f17ad051c 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.CompareAndSetWeakAcquire(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_.CompareAndSetWeakAcquire(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: @@ -293,7 +314,7 @@ void BaseMutex::RecordContention(uint64_t blocked_tid, do { slot = data->cur_content_log_entry.LoadRelaxed(); new_slot = (slot + 1) % kContentionLogSize; - } while (!data->cur_content_log_entry.CompareExchangeWeakRelaxed(slot, new_slot)); + } while (!data->cur_content_log_entry.CompareAndSetWeakRelaxed(slot, new_slot)); log[new_slot].blocked_tid = blocked_tid; log[new_slot].owner_tid = owner_tid; log[new_slot].count.StoreRelaxed(1); @@ -417,7 +438,7 @@ void Mutex::ExclusiveLock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (LIKELY(cur_state == 0)) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. - done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, 1 /* new state */); + done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); } else { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); @@ -463,7 +484,7 @@ bool Mutex::ExclusiveTryLock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (cur_state == 0) { // Change state from 0 to 1 and impose load/store ordering appropriate for lock acquisition. - done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, 1 /* new state */); + done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, 1 /* new state */); } else { return false; } @@ -522,10 +543,10 @@ void Mutex::ExclusiveUnlock(Thread* self) { // We're no longer the owner. exclusive_owner_.StoreRelaxed(0); // Change state to 0 and impose load/store ordering appropriate for lock release. - // Note, the relaxed loads below mustn't reorder before the CompareExchange. + // Note, the relaxed loads below mustn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing // a status bit into the state on contention. - done = state_.CompareExchangeWeakSequentiallyConsistent(cur_state, 0 /* new state */); + done = state_.CompareAndSetWeakSequentiallyConsistent(cur_state, 0 /* new state */); if (LIKELY(done)) { // Spurious fail? // Wake a contender. if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { @@ -618,7 +639,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (LIKELY(cur_state == 0)) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. - done = state_.CompareExchangeWeakAcquire(0 /* cur_state*/, -1 /* new state */); + done = state_.CompareAndSetWeakAcquire(0 /* cur_state*/, -1 /* new state */); } else { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); @@ -659,10 +680,10 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { // We're no longer the owner. exclusive_owner_.StoreRelaxed(0); // Change state from -1 to 0 and impose load/store ordering appropriate for lock release. - // Note, the relaxed loads below musn't reorder before the CompareExchange. + // Note, the relaxed loads below musn't reorder before the CompareAndSet. // TODO: the ordering here is non-trivial as state is split across 3 fields, fix by placing // a status bit into the state on contention. - done = state_.CompareExchangeWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */); + done = state_.CompareAndSetWeakSequentiallyConsistent(-1 /* cur_state*/, 0 /* new state */); if (LIKELY(done)) { // Weak CAS may fail spuriously. // Wake any waiters. if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || @@ -691,7 +712,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 int32_t cur_state = state_.LoadRelaxed(); if (cur_state == 0) { // Change state from 0 to -1 and impose load/store ordering appropriate for lock acquisition. - done = state_.CompareExchangeWeakAcquire(0 /* cur_state */, -1 /* new state */); + done = state_.CompareAndSetWeakAcquire(0 /* cur_state */, -1 /* new state */); } else { // Failed to acquire, hang up. timespec now_abs_ts; @@ -763,7 +784,7 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) { int32_t cur_state = state_.LoadRelaxed(); if (cur_state >= 0) { // Add as an extra reader and impose load/store ordering appropriate for lock acquisition. - done = state_.CompareExchangeWeakAcquire(cur_state, cur_state + 1); + done = state_.CompareAndSetWeakAcquire(cur_state, cur_state + 1); } else { // Owner holds it exclusively. return false; @@ -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 c0cf4872de..7077298ca9 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -24,8 +24,10 @@ #include <iosfwd> #include <string> +#include <android-base/logging.h> + #include "atomic.h" -#include "base/logging.h" +#include "base/aborting.h" #include "base/macros.h" #include "globals.h" diff --git a/runtime/base/runtime_debug.cc b/runtime/base/runtime_debug.cc new file mode 100644 index 0000000000..4f8a8ec9c6 --- /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 <vector> + +#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<bool*>& GetFlagPtrs() { + static std::vector<bool*> 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 0000000000..89a0361fa7 --- /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 8f50fd443b..35e337f0d6 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 <android-base/logging.h> + #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_flock.cc b/runtime/base/scoped_flock.cc index b8df6897e4..514b97bfb1 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -19,9 +19,9 @@ #include <sys/file.h> #include <sys/stat.h> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> -#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 1b933c07f3..db6c819c6c 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -20,9 +20,8 @@ #include <memory> #include <string> -#include "android-base/unique_fd.h" +#include <android-base/unique_fd.h> -#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 b27297241d..02f37652cf 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -21,7 +21,7 @@ #include <set> #include <sstream> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc index 2570bad85d..672431cf9d 100644 --- a/runtime/base/stringpiece.cc +++ b/runtime/base/stringpiece.cc @@ -19,7 +19,7 @@ #include <ostream> #include <utility> -#include "logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index 06db48a576..dc2206e420 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -19,10 +19,12 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include <cutils/trace.h> -#include <utils/Trace.h> +#include <sstream> #include <string> +#include "android-base/stringprintf.h" + namespace art { class ScopedTrace { @@ -30,6 +32,12 @@ class ScopedTrace { explicit ScopedTrace(const char* name) { ATRACE_BEGIN(name); } + template <typename Fn> + 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 919937f5ba..7648a109fa 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 b8d6931a83..23ec3e1aea 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -18,8 +18,9 @@ #include "timing_logger.h" +#include <android-base/logging.h> + #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/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 792c58172e..37f239da23 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -23,7 +23,7 @@ #include <limits> -#include "base/logging.h" +#include <android-base/logging.h> // Includes needed for FdFile::Copy(). #ifdef __linux__ diff --git a/runtime/bytecode_utils.h b/runtime/bytecode_utils.h index 815e0baf27..a7e0abf4e3 100644 --- a/runtime/bytecode_utils.h +++ b/runtime/bytecode_utils.h @@ -18,9 +18,9 @@ #define ART_RUNTIME_BYTECODE_UTILS_H_ #include "base/arena_object.h" -#include "dex_file-inl.h" -#include "dex_file.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_instruction-inl.h" namespace art { diff --git a/runtime/cdex/compact_dex_file.cc b/runtime/cdex/compact_dex_file.cc deleted file mode 100644 index 82ffdb0adb..0000000000 --- a/runtime/cdex/compact_dex_file.cc +++ /dev/null @@ -1,49 +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 "compact_dex_file.h" - -namespace art { - -constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize]; -constexpr uint8_t CompactDexFile::kDexMagicVersion[]; - -void CompactDexFile::WriteMagic(uint8_t* magic) { - std::copy_n(kDexMagic, kDexMagicSize, magic); -} - -void CompactDexFile::WriteCurrentVersion(uint8_t* magic) { - std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize); -} - -bool CompactDexFile::IsMagicValid(const uint8_t* magic) { - return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); -} - -bool CompactDexFile::IsVersionValid(const uint8_t* magic) { - const uint8_t* version = &magic[sizeof(kDexMagic)]; - return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0; -} - -bool CompactDexFile::IsMagicValid() const { - return IsMagicValid(header_->magic_); -} - -bool CompactDexFile::IsVersionValid() const { - return IsVersionValid(header_->magic_); -} - -} // namespace art diff --git a/runtime/cha.cc b/runtime/cha.cc index 6c011e8e39..a53d7e5b25 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 c3dd702446..5549122c34 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -21,15 +21,17 @@ #include <iomanip> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/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" +#include "dex/dex_file-inl.h" #include "gc/space/space.h" #include "java_vm_ext.h" #include "jni_internal.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<mirror::Class> 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<JNIEnvExt*>(env)->self; + Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->self_; AbortF("thread %s using JNIEnv* from thread %s", ToStr<Thread>(*self).c_str(), ToStr<Thread>(*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<Thread>(*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<Thread>(*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<JNIEnvExt*>(env)->vm; + return reinterpret_cast<JNIEnvExt*>(env)->GetVm(); } static const JNINativeInterface* baseEnv(JNIEnv* env) { - return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions; + return reinterpret_cast<JNIEnvExt*>(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 d9e89159f5..0c29e257a1 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -18,7 +18,8 @@ #define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ #include "art_method-inl.h" -#include "dex_file_types.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file_types.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" #include "stack.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 d6f003027b..cd6e8d59e8 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<mirror::Class> ClassLinker::LookupResolvedType( - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { - ObjPtr<mirror::Class> 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<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer) { + if (kObjPtrPoisoning) { + StackHandleScope<1> hs(Thread::Current()); + HandleWrapperObjPtr<mirror::Class> 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<mirror::Class> resolved_type = + referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (resolved_type == nullptr) { + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> 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<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr<mirror::Class> 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<mirror::Class> resolved_type = + referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> 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<mirror::ClassLoader> class_loader(hs.NewHandle(referring_class->GetClassLoader())); + resolved_type = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved_type; +} + +inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + DCHECK(dex_cache != nullptr); + Thread::PoisonObjectPointersIfDebug(); + ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx); + if (resolved == nullptr) { + resolved = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved; +} + +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> 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<mirror::Class> type = + referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr<mirror::Class> 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<mirror::Class> type = + referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType( + dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, dex_cache, class_loader); } - return resolved_type.Ptr(); + return type; } template <bool kThrowOnError, typename ClassGetter> @@ -113,7 +174,7 @@ inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_c break; } case kDirect: - if (dex_cache->GetDexFile()->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_cache->GetDexFile()->SupportsDefaultMethods()) { break; } FALLTHROUGH_INTENDED; @@ -147,10 +208,9 @@ inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> 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<mirror::Class> 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<kWithoutReadBarrier>()->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<kWithoutReadBarrier>()->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<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile* dex_file = h_dex_cache->GetDexFile(); - resolved_method = ResolveMethod<kResolveMode>(*dex_file, - method_idx, + resolved_method = ResolveMethod<kResolveMode>(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<mirror::DexCache> 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<kWithoutReadBarrier>()->GetResolvedField( + field_idx, image_pointer_size_); if (field == nullptr) { - field = LookupResolvedField(field_idx, dex_cache, referrer->GetClassLoader(), is_static); + ObjPtr<mirror::ClassLoader> 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<mirror::Class> 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<kWithoutReadBarrier>()->GetResolvedField( + field_idx, image_pointer_size_); if (UNLIKELY(resolved_field == nullptr)) { StackHandleScope<2> hs(Thread::Current()); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> 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<mirror::ClassLoader> 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 ccf431969a..877654247c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -50,8 +50,9 @@ #include "class_table-inl.h" #include "compiler_callbacks.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_file_loader.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "experimental_flags.h" @@ -444,7 +445,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b CHECK(java_lang_Object != nullptr); // backfill Object as the super class of Class. java_lang_Class->SetSuperClass(java_lang_Object.Get()); - mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self); + mirror::Class::SetStatus(java_lang_Object, ClassStatus::kLoaded, self); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been @@ -461,10 +462,8 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // // We take the lock here to avoid using NO_THREAD_SAFETY_ANALYSIS. MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - mirror::Class* java_lang_Object_ptr = java_lang_Object.Get(); - SubtypeCheck<mirror::Class*>::EnsureInitialized(java_lang_Object_ptr); - mirror::Class* java_lang_Class_ptr = java_lang_Class.Get(); - SubtypeCheck<mirror::Class*>::EnsureInitialized(java_lang_Class_ptr); + SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(java_lang_Object.Get()); + SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(java_lang_Class.Get()); } // Object[] next to hold class roots. @@ -495,14 +494,14 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); java_lang_String->SetStringClass(); mirror::String::SetClass(java_lang_String.Get()); - mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(java_lang_String, ClassStatus::kResolved, self); // Setup java.lang.ref.Reference. Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_)))); mirror::Reference::SetClass(java_lang_ref_Reference.Get()); java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); - mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kResolved, self); // Create storage for root classes, save away our work so far (requires descriptors). class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( @@ -555,7 +554,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); java_lang_DexCache->SetDexCacheClass(); java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); - mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kResolved, self); // Setup dalvik.system.ClassExt @@ -563,7 +562,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b AllocClass(self, java_lang_Class.Get(), mirror::ClassExt::ClassSize(image_pointer_size_)))); SetClassRoot(kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); mirror::ClassExt::SetClass(dalvik_system_ClassExt.Get()); - mirror::Class::SetStatus(dalvik_system_ClassExt, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kResolved, self); // Set up array classes for string, field, method Handle<mirror::Class> object_array_string(hs.NewHandle( @@ -611,15 +610,15 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b } // Object, String, ClassExt and DexCache need to be rerun through FindSystemClass to finish init - mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(java_lang_Object, ClassStatus::kNotReady, self); CheckSystemClass(self, java_lang_Object, "Ljava/lang/Object;"); CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); - mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(java_lang_String, ClassStatus::kNotReady, self); CheckSystemClass(self, java_lang_String, "Ljava/lang/String;"); - mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kNotReady, self); CheckSystemClass(self, java_lang_DexCache, "Ljava/lang/DexCache;"); CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); - mirror::Class::SetStatus(dalvik_system_ClassExt, mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kNotReady, self); CheckSystemClass(self, dalvik_system_ClassExt, "Ldalvik/system/ClassExt;"); CHECK_EQ(dalvik_system_ClassExt->GetObjectSize(), mirror::ClassExt::InstanceSize()); @@ -773,7 +772,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class - mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self); + mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kNotReady, self); CheckSystemClass(self, java_lang_ref_Reference, "Ljava/lang/ref/Reference;"); CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); CHECK_EQ(java_lang_ref_Reference->GetClassSize(), @@ -1871,8 +1870,7 @@ bool ClassLinker::AddImageSpace( ScopedTrace trace("Recalculate app image SubtypeCheck bitstrings"); MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); for (const ClassTable::TableSlot& root : temp_set) { - mirror::Class* root_klass = root.Read(); - SubtypeCheck<mirror::Class*>::EnsureInitialized(root_klass); + SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(root.Read()); } } } @@ -2311,7 +2309,7 @@ mirror::Class* ClassLinker::EnsureResolved(Thread* self, // Check for circular dependencies between classes, the lock is required for SetStatus. if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { ThrowClassCircularityError(h_class.Get()); - mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(h_class, ClassStatus::kErrorUnresolved, self); return nullptr; } } @@ -2773,7 +2771,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // An exception occured during load, set status to erroneous while holding klass' lock in case // notification is necessary. if (!klass->IsErroneous()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); } return nullptr; } @@ -2783,7 +2781,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, if (!LoadSuperAndInterfaces(klass, *new_dex_file)) { // Loading failed. if (!klass->IsErroneous()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); } return nullptr; } @@ -2802,7 +2800,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { // Linking failed. if (!klass->IsErroneous()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); } return nullptr; } @@ -3089,7 +3087,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader) { CHECK(klass != nullptr); CHECK(klass->GetDexCache() != nullptr); - CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus()); + CHECK_EQ(ClassStatus::kNotReady, klass->GetStatus()); const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); CHECK(descriptor != nullptr); @@ -3099,7 +3097,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetAccessFlags(access_flags); klass->SetClassLoader(class_loader); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); - mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, nullptr); + mirror::Class::SetStatus(klass, ClassStatus::kIdx, nullptr); klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); klass->SetDexTypeIndex(dex_class_def.class_idx_); @@ -3391,7 +3389,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)) { @@ -3625,7 +3623,7 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr<mirror::Class> primi h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); h_class->SetPrimitiveType(type); h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); - mirror::Class::SetStatus(h_class, mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(h_class, ClassStatus::kInitialized, self); const char* descriptor = Primitive::Descriptor(type); ObjPtr<mirror::Class> existing = InsertClass(descriptor, h_class.Get(), @@ -3740,11 +3738,11 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto } else { new_class->SetClassFlags(mirror::kClassFlagObjectArray); } - mirror::Class::SetStatus(new_class, mirror::Class::kStatusLoaded, self); + mirror::Class::SetStatus(new_class, ClassStatus::kLoaded, self); new_class->PopulateEmbeddedVTable(image_pointer_size_); ImTable* object_imt = java_lang_Object->GetImt(image_pointer_size_); new_class->SetImt(object_imt, image_pointer_size_); - mirror::Class::SetStatus(new_class, mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(new_class, ClassStatus::kInitialized, self); // don't need to set new_class->SetObjectSize(..) // because Object::SizeOf delegates to Array::SizeOf @@ -4002,7 +4000,7 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, } // Need to grab the lock to change status. ObjectLock<mirror::Class> super_lock(self, klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); return false; } @@ -4024,9 +4022,9 @@ verifier::FailureKind ClassLinker::VerifyClass( ObjectLock<mirror::Class> lock(self, klass); // Is somebody verifying this now? - mirror::Class::Status old_status = klass->GetStatus(); - while (old_status == mirror::Class::kStatusVerifying || - old_status == mirror::Class::kStatusVerifyingAtRuntime) { + ClassStatus old_status = klass->GetStatus(); + while (old_status == ClassStatus::kVerifying || + old_status == ClassStatus::kVerifyingAtRuntime) { lock.WaitIgnoringInterrupts(); // WaitIgnoringInterrupts can still receive an interrupt and return early, in this // case we may see the same status again. b/62912904. This is why the check is @@ -4057,18 +4055,18 @@ verifier::FailureKind ClassLinker::VerifyClass( return verifier::FailureKind::kSoftFailure; } - if (klass->GetStatus() == mirror::Class::kStatusResolved) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifying, self); + if (klass->GetStatus() == ClassStatus::kResolved) { + mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self); } else { - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime) + CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime) << klass->PrettyClass(); CHECK(!Runtime::Current()->IsAotCompiler()); - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifyingAtRuntime, self); + mirror::Class::SetStatus(klass, ClassStatus::kVerifyingAtRuntime, self); } // Skip verification if disabled. if (!Runtime::Current()->IsVerificationEnabled()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); EnsureSkipAccessChecksMethods(klass, image_pointer_size_); return verifier::FailureKind::kNoFailure; } @@ -4130,7 +4128,7 @@ verifier::FailureKind ClassLinker::VerifyClass( // Try to use verification information from the oat file, otherwise do runtime verification. const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); - mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady); + ClassStatus oat_file_class_status(ClassStatus::kNotReady); bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status); VLOG(class_linker) << "Class preverified status for class " @@ -4167,10 +4165,10 @@ verifier::FailureKind ClassLinker::VerifyClass( // Even though there were no verifier failures we need to respect whether the super-class and // super-default-interfaces were verified or requiring runtime reverification. if (supertype == nullptr || supertype->IsVerified()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); } else { - CHECK_EQ(supertype->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); - mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self); + CHECK_EQ(supertype->GetStatus(), ClassStatus::kRetryVerificationAtRuntime); + mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); // Pretend a soft failure occurred so that we don't consider the class verified below. verifier_failure = verifier::FailureKind::kSoftFailure; } @@ -4180,9 +4178,9 @@ verifier::FailureKind ClassLinker::VerifyClass( // failures at runtime will be handled by slow paths in the generated // code. Set status accordingly. if (Runtime::Current()->IsAotCompiler()) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self); + mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); } else { - mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); // As this is a fake verified status, make sure the methods are _not_ marked // kAccSkipAccessChecks later. klass->SetVerificationAttempted(); @@ -4194,7 +4192,7 @@ verifier::FailureKind ClassLinker::VerifyClass( << " because: " << error_msg; self->AssertNoPendingException(); ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); } if (preverified || verifier_failure == verifier::FailureKind::kNoFailure) { // Class is verified so we don't need to do any access check on its methods. @@ -4236,7 +4234,7 @@ verifier::FailureKind ClassLinker::PerformClassVerification(Thread* self, bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, ObjPtr<mirror::Class> klass, - mirror::Class::Status& oat_file_class_status) { + ClassStatus& oat_file_class_status) { // If we're compiling, we can only verify the class using the oat file if // we are not compiling the image or if the class we're verifying is not part of // the app. In other words, we will only check for preverification of bootclasspath @@ -4262,15 +4260,15 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, uint16_t class_def_index = klass->GetDexClassDefIndex(); oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus(); - if (oat_file_class_status >= mirror::Class::kStatusVerified) { + if (oat_file_class_status >= ClassStatus::kVerified) { return true; } // If we only verified a subset of the classes at compile time, we can end up with classes that // were resolved by the verifier. - if (oat_file_class_status == mirror::Class::kStatusResolved) { + if (oat_file_class_status == ClassStatus::kResolved) { return false; } - if (oat_file_class_status == mirror::Class::kStatusRetryVerificationAtRuntime) { + if (oat_file_class_status == ClassStatus::kRetryVerificationAtRuntime) { // Compile time verification failed with a soft error. Compile time verification can fail // because we have incomplete type information. Consider the following: // class ... { @@ -4295,7 +4293,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, // in the class. These errors are unrecoverable. return false; } - if (oat_file_class_status == mirror::Class::kStatusNotReady) { + if (oat_file_class_status == ClassStatus::kNotReady) { // Status is uninitialized if we couldn't determine the status at compile time, for example, // not loading the class. // TODO: when the verifier doesn't rely on Class-es failing to resolve/load the type hierarchy @@ -4317,15 +4315,14 @@ void ClassLinker::ResolveClassExceptionHandlerTypes(Handle<mirror::Class> 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); @@ -4369,7 +4366,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& temp_klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); // Object has an empty iftable, copy it for that reason. temp_klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); - mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusIdx, self); + mirror::Class::SetStatus(temp_klass, ClassStatus::kIdx, self); std::string descriptor(GetDescriptorForProxy(temp_klass.Get())); const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str()); @@ -4437,7 +4434,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& // The super class is java.lang.reflect.Proxy temp_klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy)); // Now effectively in the loaded state. - mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusLoaded, self); + mirror::Class::SetStatus(temp_klass, ClassStatus::kLoaded, self); self->AssertNoPendingException(); // At this point the class is loaded. Publish a ClassLoad event. @@ -4453,7 +4450,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& Handle<mirror::ObjectArray<mirror::Class>> h_interfaces( hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces))); if (!LinkClass(self, descriptor.c_str(), temp_klass, h_interfaces, &klass)) { - mirror::Class::SetStatus(temp_klass, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(temp_klass, ClassStatus::kErrorUnresolved, self); return nullptr; } } @@ -4474,7 +4471,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& { // Lock on klass is released. Lock new class object. ObjectLock<mirror::Class> initialization_lock(self, klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self); } // sanity checks @@ -4515,7 +4512,7 @@ std::string ClassLinker::GetDescriptorForProxy(ObjPtr<mirror::Class> proxy_class void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> 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 <init>(InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. @@ -4690,7 +4687,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, VlogClassInitializationFailure(klass); } else { CHECK(Runtime::Current()->IsAotCompiler()); - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); + CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime); } return false; } else { @@ -4706,12 +4703,12 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, } } - // If the class is kStatusInitializing, either this thread is + // If the class is ClassStatus::kInitializing, either this thread is // initializing higher up the stack or another thread has beat us // to initializing and we need to wait. Either way, this // invocation of InitializeClass will not be responsible for // running <clinit> and will return. - if (klass->GetStatus() == mirror::Class::kStatusInitializing) { + if (klass->GetStatus() == ClassStatus::kInitializing) { // Could have got an exception during verification. if (self->IsExceptionPending()) { VlogClassInitializationFailure(klass); @@ -4736,20 +4733,20 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class) : OatFile::OatClass::Invalid(); - if (oat_class.GetStatus() < mirror::Class::kStatusSuperclassValidated && + if (oat_class.GetStatus() < ClassStatus::kSuperclassValidated && !ValidateSuperClassDescriptors(klass)) { - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); return false; } self->AllowThreadSuspension(); - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << klass->PrettyClass() + CHECK_EQ(klass->GetStatus(), ClassStatus::kVerified) << klass->PrettyClass() << " self.tid=" << self->GetTid() << " clinit.tid=" << klass->GetClinitThreadId(); // From here out other threads may observe that we're initializing and so changes of state // require the a notification. klass->SetClinitThreadId(self->GetTid()); - mirror::Class::SetStatus(klass, mirror::Class::kStatusInitializing, self); + mirror::Class::SetStatus(klass, ClassStatus::kInitializing, self); t0 = NanoTime(); } @@ -4776,7 +4773,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, << (self->GetException() != nullptr ? self->GetException()->Dump() : ""); ObjectLock<mirror::Class> lock(self, klass); // Initialization failed because the super-class is erroneous. - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); return false; } } @@ -4807,7 +4804,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, if (!iface_initialized) { ObjectLock<mirror::Class> lock(self, klass); // Initialization failed because one of our interfaces with default methods is erroneous. - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); return false; } } @@ -4818,7 +4815,6 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> 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<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader())); Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); @@ -4836,11 +4832,11 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> 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()) { @@ -4848,7 +4844,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> 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<true>(field); } else { @@ -4880,7 +4876,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, if (self->IsExceptionPending()) { WrapExceptionInInitializer(klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); success = false; } else if (Runtime::Current()->IsTransactionAborted()) { // The exception thrown when the transaction aborted has been caught and cleared @@ -4889,7 +4885,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, << mirror::Class::PrettyDescriptor(klass.Get()) << " without exception while transaction was aborted: re-throw it now."; Runtime::Current()->ThrowTransactionAbortError(self); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); success = false; } else { RuntimeStats* global_stats = Runtime::Current()->GetStats(); @@ -4899,7 +4895,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, global_stats->class_init_time_ns += (t1 - t0); thread_stats->class_init_time_ns += (t1 - t0); // Set the class as initialized except if failed to initialize static fields. - mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized, self); + mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self); if (VLOG_IS_ON(class_linker)) { std::string temp; LOG(INFO) << "Initialized class " << klass->GetDescriptor(&temp) << " from " << @@ -4973,14 +4969,14 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, // we were not using WaitIgnoringInterrupts), bail out. if (self->IsExceptionPending()) { WrapExceptionInInitializer(klass); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); return false; } // Spurious wakeup? Go back to waiting. - if (klass->GetStatus() == mirror::Class::kStatusInitializing) { + if (klass->GetStatus() == ClassStatus::kInitializing) { continue; } - if (klass->GetStatus() == mirror::Class::kStatusVerified && + if (klass->GetStatus() == ClassStatus::kVerified && Runtime::Current()->IsAotCompiler()) { // Compile time initialization failed. return false; @@ -5221,8 +5217,7 @@ bool ClassLinker::EnsureInitialized(Thread* self, // or Overflowed (can be used as a source for IsSubClass check). { MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); - ObjPtr<mirror::Class> c_ptr(c.Get()); - SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c_ptr); + SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c.Get()); // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck is already initialized. } const bool success = InitializeClass(self, c, can_init_fields, can_init_parents); @@ -5270,7 +5265,7 @@ void ClassLinker::RegisterClassLoader(ObjPtr<mirror::ClassLoader> 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); @@ -5314,7 +5309,7 @@ bool ClassLinker::LinkClass(Thread* self, Handle<mirror::Class> klass, Handle<mirror::ObjectArray<mirror::Class>> interfaces, MutableHandle<mirror::Class>* h_new_class_out) { - CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); + CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { return false; @@ -5334,7 +5329,7 @@ bool ClassLinker::LinkClass(Thread* self, return false; } CreateReferenceInstanceOffsets(klass); - CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); + CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus()); ImTable* imt = nullptr; if (klass->ShouldHaveImt()) { @@ -5387,7 +5382,7 @@ bool ClassLinker::LinkClass(Thread* self, // This will notify waiters on klass that saw the not yet resolved // class in the class_table_ during EnsureResolved. - mirror::Class::SetStatus(klass, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kResolved, self); h_new_class_out->Assign(klass.Get()); } else { CHECK(!klass->IsResolved()); @@ -5403,7 +5398,7 @@ bool ClassLinker::LinkClass(Thread* self, klass->SetIFieldsPtrUnchecked(nullptr); if (UNLIKELY(h_new_class == nullptr)) { self->AssertPendingOOMException(); - mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); return false; } @@ -5437,12 +5432,12 @@ bool ClassLinker::LinkClass(Thread* self, // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. - mirror::Class::SetStatus(klass, mirror::Class::kStatusRetired, self); + mirror::Class::SetStatus(klass, ClassStatus::kRetired, self); - CHECK_EQ(h_new_class->GetStatus(), mirror::Class::kStatusResolving); + CHECK_EQ(h_new_class->GetStatus(), ClassStatus::kResolving); // This will notify waiters on new_class that saw the not yet resolved // class in the class_table_ during EnsureResolved. - mirror::Class::SetStatus(h_new_class, mirror::Class::kStatusResolved, self); + mirror::Class::SetStatus(h_new_class, ClassStatus::kResolved, self); // Return the new class. h_new_class_out->Assign(h_new_class.Get()); } @@ -5450,7 +5445,7 @@ bool ClassLinker::LinkClass(Thread* self, } bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) { - CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus()); + CHECK_EQ(ClassStatus::kIdx, klass->GetStatus()); const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); dex::TypeIndex super_class_idx = class_def.superclass_idx_; if (super_class_idx.IsValid()) { @@ -5467,7 +5462,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF return false; } - ObjPtr<mirror::Class> super_class = ResolveType(dex_file, super_class_idx, klass.Get()); + ObjPtr<mirror::Class> super_class = ResolveType(super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5486,7 +5481,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF if (interfaces != nullptr) { for (size_t i = 0; i < interfaces->Size(); i++) { dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; - ObjPtr<mirror::Class> interface = ResolveType(dex_file, idx, klass.Get()); + ObjPtr<mirror::Class> interface = ResolveType(idx, klass.Get()); if (interface == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5503,7 +5498,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF } } // Mark the class as loaded. - mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, nullptr); + mirror::Class::SetStatus(klass, ClassStatus::kLoaded, nullptr); return true; } @@ -7727,32 +7722,32 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, - Handle<mirror::DexCache> dex_cache) { +ObjPtr<mirror::String> ClassLinker::ResolveString(dex::StringIndex string_idx, + Handle<mirror::DexCache> dex_cache) { DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::String> 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<mirror::String> 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<mirror::DexCache> dex_cache) { +ObjPtr<mirror::String> ClassLinker::LookupString(dex::StringIndex string_idx, + ObjPtr<mirror::DexCache> dex_cache) { DCHECK(dex_cache != nullptr); ObjPtr<mirror::String> 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<mirror::String> string = @@ -7760,87 +7755,168 @@ mirror::String* ClassLinker::LookupString(const DexFile& dex_file, if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } - return string.Ptr(); + return string; } -ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { - ObjPtr<mirror::Class> 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<mirror::Class> ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> 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<mirror::Class> 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<mirror::Class> referrer) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); - return ResolveType(dex_file, type_idx, dex_cache, class_loader); +ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + Thread* self = Thread::Current(); + const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx); + ObjPtr<mirror::Class> 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<mirror::Throwable> 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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::Throwable> 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<mirror::ClassLoader> loader, const char* class_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); + ObjPtr<mirror::Class> path_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); + ObjPtr<mirror::Class> dex_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); + ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::Object> 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<mirror::Object> dex_elements_obj = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + if (dex_elements_obj != nullptr) { + ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements = + dex_elements_obj->AsObjectArray<mirror::Object>(); + oss << "("; + const char* path_separator = ""; + for (int32_t i = 0; i != dex_elements->GetLength(); ++i) { + ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); + ObjPtr<mirror::Object> dex_file = + (element != nullptr) ? dex_file_field->GetObject(element) : nullptr; + ObjPtr<mirror::LongArray> 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<const DexFile*>( + static_cast<uintptr_t>(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 <ClassLinker::ResolveMode kResolveMode> -ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, @@ -7858,17 +7934,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<mirror::Class> 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; @@ -7943,8 +8026,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<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); @@ -7955,9 +8037,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<mirror::Class> klass = - ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); + ObjPtr<mirror::Class> klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; @@ -7979,7 +8060,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); ObjPtr<mirror::Class> 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. @@ -8008,8 +8089,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<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, bool is_static) { @@ -8019,9 +8099,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<mirror::Class> klass = ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader); + ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8050,8 +8131,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<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(dex_cache != nullptr); @@ -8060,9 +8140,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<mirror::Class> klass(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader)); + ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8079,11 +8160,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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) { +ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( + Thread* self, + uint32_t proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK(dex_cache != nullptr); @@ -8095,9 +8176,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<mirror::Class> 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; @@ -8123,7 +8205,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(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; @@ -8141,14 +8223,13 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, return type.Get(); } -mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, - ArtMethod* referrer) { +ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(Thread* self, + uint32_t proto_idx, + ArtMethod* referrer) { StackHandleScope<2> hs(self); - const DexFile* dex_file = referrer->GetDexFile(); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> 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( @@ -8326,8 +8407,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)) { @@ -8410,7 +8490,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<mirror::Class> klass = ResolveType(type_idx, dex_cache, class_loader); if (nullptr == klass) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8443,9 +8523,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<mirror::MethodHandle> 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); @@ -8626,9 +8706,9 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { } jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, - const std::vector<const DexFile*>& dex_files, - jclass loader_class, - jobject parent_loader) { + const std::vector<const DexFile*>& dex_files, + jclass loader_class, + jobject parent_loader) { CHECK(self->GetJniEnv()->IsSameObject(loader_class, WellKnownClasses::dalvik_system_PathClassLoader) || self->GetJniEnv()->IsSameObject(loader_class, @@ -8659,10 +8739,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; @@ -8704,6 +8784,29 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, DCHECK(h_dex_path_list != nullptr); // Set elements. dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get()); + // Create an empty List for the "nativeLibraryDirectories," required for native tests. + // Note: this code is uncommon(oatdump)/testing-only, so don't add further WellKnownClasses + // elements. + { + ArtField* native_lib_dirs = dex_elements_field->GetDeclaringClass()-> + FindDeclaredInstanceField("nativeLibraryDirectories", "Ljava/util/List;"); + DCHECK(native_lib_dirs != nullptr); + ObjPtr<mirror::Class> list_class = FindSystemClass(self, "Ljava/util/ArrayList;"); + DCHECK(list_class != nullptr); + { + StackHandleScope<1> h_list_scope(self); + Handle<mirror::Class> h_list_class(h_list_scope.NewHandle<mirror::Class>(list_class)); + bool list_init = EnsureInitialized(self, h_list_class, true, true); + DCHECK(list_init); + list_class = h_list_class.Get(); + } + ObjPtr<mirror::Object> list_object = list_class->AllocObject(self); + // Note: we leave the object uninitialized. This must never leak into any non-testing code, but + // is fine for testing. While it violates a Java-code invariant (the elementData field is + // normally never null), as long as one does not try to add elements, this will still + // work. + native_lib_dirs->SetObject<false>(h_dex_path_list.Get(), list_object); + } // Create the class loader.. Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class)); @@ -8928,14 +9031,12 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type); template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a4c4f3d9ab..3e3425f5ac 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,9 +27,9 @@ #include "base/enums.h" #include "base/macros.h" #include "base/mutex.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "dex_cache_resolved_classes.h" -#include "dex_file.h" -#include "dex_file_types.h" #include "gc_root.h" #include "handle.h" #include "jni.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<mirror::DexCache> dex_cache) + // Resolve a String with the given index from the DexFile associated with the given DexCache, + // storing the result in the DexCache. + ObjPtr<mirror::String> ResolveString(dex::StringIndex string_idx, + Handle<mirror::DexCache> 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<mirror::DexCache> dex_cache) + // 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<mirror::String> LookupString(dex::StringIndex string_idx, + ObjPtr<mirror::DexCache> 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<mirror::Class> 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<mirror::Class> ResolveType(dex::TypeIndex type_idx, ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::Class> LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> 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<mirror::Class> ResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> 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<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer) REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> 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<mirror::Class> 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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> 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<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> 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<mirror::ClassLoader> 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 <ResolveMode kResolveMode> - ArtMethod* ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> 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<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> 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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, bool is_static) + Handle<mirror::ClassLoader> 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<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> 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<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> 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<mirror::MethodType> ResolveMethodType(Thread* self, + uint32_t proto_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> 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<mirror::MethodType> 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<mirror::MethodHandle> 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. @@ -478,7 +478,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_); bool VerifyClassUsingOatFile(const DexFile& dex_file, ObjPtr<mirror::Class> klass, - mirror::Class::Status& oat_file_class_status) + ClassStatus& oat_file_class_status) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); void ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass) @@ -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<mirror::Class> DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Implementation of ResolveType() called when the type was not found in the dex cache. + ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> 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 892a850997..80ef6544e8 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -26,7 +26,8 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "common_runtime_test.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" +#include "dex/standard_dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" #include "experimental_flags.h" #include "gc/heap.h" @@ -50,7 +51,6 @@ #include "mirror/string-inl.h" #include "mirror/var_handle.h" #include "scoped_thread_state_change-inl.h" -#include "standard_dex_file.h" #include "thread-current-inl.h" namespace art { @@ -86,7 +86,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(primitive->GetSuperClass() == nullptr); EXPECT_FALSE(primitive->HasSuperClass()); EXPECT_TRUE(primitive->GetClassLoader() == nullptr); - EXPECT_EQ(mirror::Class::kStatusInitialized, primitive->GetStatus()); + EXPECT_EQ(ClassStatus::kInitialized, primitive->GetStatus()); EXPECT_FALSE(primitive->IsErroneous()); EXPECT_TRUE(primitive->IsLoaded()); EXPECT_TRUE(primitive->IsResolved()); @@ -125,7 +125,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr); EXPECT_FALSE(JavaLangObject->HasSuperClass()); EXPECT_TRUE(JavaLangObject->GetClassLoader() == nullptr); - EXPECT_EQ(mirror::Class::kStatusInitialized, JavaLangObject->GetStatus()); + EXPECT_EQ(ClassStatus::kInitialized, JavaLangObject->GetStatus()); EXPECT_FALSE(JavaLangObject->IsErroneous()); EXPECT_TRUE(JavaLangObject->IsLoaded()); EXPECT_TRUE(JavaLangObject->IsResolved()); @@ -200,7 +200,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(array->HasSuperClass()); ASSERT_TRUE(array->GetComponentType() != nullptr); ASSERT_GT(strlen(array->GetComponentType()->GetDescriptor(&temp)), 0U); - EXPECT_EQ(mirror::Class::kStatusInitialized, array->GetStatus()); + EXPECT_EQ(ClassStatus::kInitialized, array->GetStatus()); EXPECT_FALSE(array->IsErroneous()); EXPECT_TRUE(array->IsLoaded()); EXPECT_TRUE(array->IsResolved()); @@ -795,6 +795,12 @@ struct FieldVarHandleOffsets : public CheckOffsets<mirror::FieldVarHandle> { } }; +struct ArrayElementVarHandleOffsets : public CheckOffsets<mirror::ArrayElementVarHandle> { + ArrayElementVarHandleOffsets() : CheckOffsets<mirror::ArrayElementVarHandle>( + false, "Ljava/lang/invoke/ArrayElementVarHandle;") { + } +}; + struct ByteArrayViewVarHandleOffsets : public CheckOffsets<mirror::ByteArrayViewVarHandle> { ByteArrayViewVarHandleOffsets() : CheckOffsets<mirror::ByteArrayViewVarHandle>( false, "Ljava/lang/invoke/ByteArrayViewVarHandle;") { @@ -838,6 +844,7 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(CallSiteOffsets().Check()); EXPECT_TRUE(VarHandleOffsets().Check()); EXPECT_TRUE(FieldVarHandleOffsets().Check()); + EXPECT_TRUE(ArrayElementVarHandleOffsets().Check()); EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check()); EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check()); } @@ -912,7 +919,7 @@ TEST_F(ClassLinkerTest, FindClass) { EXPECT_TRUE(MyClass->GetSuperClass() == JavaLangObject); EXPECT_TRUE(MyClass->HasSuperClass()); EXPECT_EQ(class_loader.Get(), MyClass->GetClassLoader()); - EXPECT_EQ(mirror::Class::kStatusResolved, MyClass->GetStatus()); + EXPECT_EQ(ClassStatus::kResolved, MyClass->GetStatus()); EXPECT_FALSE(MyClass->IsErroneous()); EXPECT_TRUE(MyClass->IsLoaded()); EXPECT_TRUE(MyClass->IsResolved()); @@ -954,15 +961,14 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> 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 +989,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<mirror::Class>(nullptr)); // Resolve the array class we want to test. ObjPtr<mirror::Class> array_klass @@ -991,13 +997,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { ASSERT_OBJ_PTR_NE(array_klass, ObjPtr<mirror::Class>(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 +1018,14 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr)); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; Handle<mirror::DexCache> 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 +1037,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 +1309,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<mirror::Class> 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<mirror::Class> init = ResolveVerifyAndClinit(type_idx, + getS0, + soa.Self(), + /* can_run_clinit */ true, + /* verify_access */ false); EXPECT_TRUE(init != nullptr); EXPECT_TRUE(init->IsInitialized()); } @@ -1541,11 +1554,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Its RType = Ljava/lang/String; // Its PTypes = { Ljava/lang/String; } Handle<mirror::MethodType> 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 +1568,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Resolve the method type again and assert that we get back the same value. Handle<mirror::MethodType> 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 +1581,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { ASSERT_FALSE(method2->IsDirect()); const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle<mirror::MethodType> 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 54e75588b9..3ec5335a80 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -21,8 +21,8 @@ #include "base/stl_util.h" #include "class_linker.h" #include "class_loader_utils.h" -#include "dex_file.h" -#include "dex_file_loader.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" #include "handle_scope-inl.h" #include "jni_internal.h" #include "oat_file_assistant.h" diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index fc3446c3f9..bc726354a8 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -23,7 +23,7 @@ #include "base/stl_util.h" #include "class_linker.h" #include "common_runtime_test.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "handle_scope-inl.h" #include "mirror/class.h" #include "mirror/class_loader.h" diff --git a/runtime/class_reference.h b/runtime/class_reference.h index 2ef9ab8959..e8e668e169 100644 --- a/runtime/class_reference.h +++ b/runtime/class_reference.h @@ -20,7 +20,7 @@ #include <stdint.h> #include <utility> -#include "dex_file_reference.h" +#include "dex/dex_file_reference.h" namespace art { diff --git a/runtime/class_status.h b/runtime/class_status.h index 7f2ef6a09d..ada286338b 100644 --- a/runtime/class_status.h +++ b/runtime/class_status.h @@ -24,70 +24,70 @@ namespace art { // Class Status // -// kStatusRetired: Class that's temporarily used till class linking time +// kRetired: Class that's temporarily used till class linking time // has its (vtable) size figured out and has been cloned to one with the // right size which will be the one used later. The old one is retired and // will be gc'ed once all refs to the class point to the newly // cloned version. // -// kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need +// kErrorUnresolved, kErrorResolved: Class is erroneous. We need // to distinguish between classes that have been resolved and classes that // have not. This is important because the const-class instruction needs to // return a previously resolved class even if its subsequent initialization // failed. We also need this to decide whether to wrap a previous // initialization failure in ClassDefNotFound error or not. // -// kStatusNotReady: If a Class cannot be found in the class table by +// kNotReady: If a Class cannot be found in the class table by // FindClass, it allocates an new one with AllocClass in the -// kStatusNotReady and calls LoadClass. Note if it does find a -// class, it may not be kStatusResolved and it will try to push it -// forward toward kStatusResolved. +// kNotReady and calls LoadClass. Note if it does find a +// class, it may not be kResolved and it will try to push it +// forward toward kResolved. // -// kStatusIdx: LoadClass populates with Class with information from -// the DexFile, moving the status to kStatusIdx, indicating that the +// kIdx: LoadClass populates with Class with information from +// the DexFile, moving the status to kIdx, indicating that the // Class value in super_class_ has not been populated. The new Class // can then be inserted into the classes table. // -// kStatusLoaded: After taking a lock on Class, the ClassLinker will -// attempt to move a kStatusIdx class forward to kStatusLoaded by +// kLoaded: After taking a lock on Class, the ClassLinker will +// attempt to move a kIdx class forward to kLoaded by // using ResolveClass to initialize the super_class_ and ensuring the // interfaces are resolved. // -// kStatusResolving: Class is just cloned with the right size from +// kResolving: Class is just cloned with the right size from // temporary class that's acting as a placeholder for linking. The old // class will be retired. New class is set to this status first before // moving on to being resolved. // -// kStatusResolved: Still holding the lock on Class, the ClassLinker +// kResolved: Still holding the lock on Class, the ClassLinker // shows linking is complete and fields of the Class populated by making -// it kStatusResolved. Java allows circularities of the form where a super +// it kResolved. Java allows circularities of the form where a super // class has a field that is of the type of the sub class. We need to be able // to fully resolve super classes while resolving types for fields. // -// kStatusRetryVerificationAtRuntime: The verifier sets a class to +// kRetryVerificationAtRuntime: The verifier sets a class to // this state if it encounters a soft failure at compile time. This // often happens when there are unresolved classes in other dex // files, and this status marks a class as needing to be verified // again at runtime. // // TODO: Explain the other states -enum ClassStatus : int8_t { - kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead. - kStatusErrorResolved = -2, - kStatusErrorUnresolved = -1, - kStatusNotReady = 0, - kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. - kStatusLoaded = 2, // DEX idx values resolved. - kStatusResolving = 3, // Just cloned from temporary class object. - kStatusResolved = 4, // Part of linking. - kStatusVerifying = 5, // In the process of being verified. - kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime. - kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime. - kStatusVerified = 8, // Logically part of linking; done pre-init. - kStatusSuperclassValidated = 9, // Superclass validation part of init done. - kStatusInitializing = 10, // Class init in progress. - kStatusInitialized = 11, // Ready to go. - kStatusMax = 12, +enum class ClassStatus : uint8_t { + kNotReady = 0, // Zero-initialized Class object starts in this state. + kRetired = 1, // Retired, should not be used. Use the newly cloned one instead. + kErrorResolved = 2, + kErrorUnresolved = 3, + kIdx = 4, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. + kLoaded = 5, // DEX idx values resolved. + kResolving = 6, // Just cloned from temporary class object. + kResolved = 7, // Part of linking. + kVerifying = 8, // In the process of being verified. + kRetryVerificationAtRuntime = 9, // Compile time verification failed, retry at runtime. + kVerifyingAtRuntime = 10, // Retrying verification at runtime. + kVerified = 11, // Logically part of linking; done pre-init. + kSuperclassValidated = 12, // Superclass validation part of init done. + kInitializing = 13, // Class init in progress. + kInitialized = 14, // Ready to go. + kLast = kInitialized }; std::ostream& operator<<(std::ostream& os, const ClassStatus& rhs); diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 1280466a91..718e93a97d 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -95,7 +95,7 @@ inline mirror::Class* ClassTable::TableSlot::Read() const { if (kReadBarrierOption != kWithoutReadBarrier && before_ptr != after_ptr) { // If another thread raced and updated the reference, do not store the read barrier updated // one. - data_.CompareExchangeStrongRelease(before, Encode(after_ptr, MaskHash(before))); + data_.CompareAndSetStrongRelease(before, Encode(after_ptr, MaskHash(before))); } return after_ptr.Ptr(); } @@ -110,7 +110,7 @@ inline void ClassTable::TableSlot::VisitRoot(const Visitor& visitor) const { if (before_ptr != after_ptr) { // If another thread raced and updated the reference, do not store the read barrier updated // one. - data_.CompareExchangeStrongRelease(before, Encode(after_ptr, MaskHash(before))); + data_.CompareAndSetStrongRelease(before, Encode(after_ptr, MaskHash(before))); } } diff --git a/runtime/class_table.cc b/runtime/class_table.cc index c45bbe5334..e313ec5dd7 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -79,7 +79,7 @@ mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* kl mirror::Class* const existing = existing_it->Read(); CHECK_NE(existing, klass) << descriptor; CHECK(!existing->IsResolved()) << descriptor; - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor; + CHECK_EQ(klass->GetStatus(), ClassStatus::kResolving) << descriptor; CHECK(!klass->IsTemp()) << descriptor; VerifyObject(klass); // Update the element in the hash set with the new class. This is safe to do since the descriptor diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc index 18c2b827fe..fdf6ad1fea 100644 --- a/runtime/class_table_test.cc +++ b/runtime/class_table_test.cc @@ -20,7 +20,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" #include "common_runtime_test.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" #include "handle_scope-inl.h" diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 267735fe95..1db25c49f6 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 "dex/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 ef1647caf3..96d660fd64 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -30,12 +30,13 @@ #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" #include "compiler_callbacks.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "gc/heap.h" #include "gc_root-inl.h" #include "gtest/gtest.h" diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 5be8d5b55c..1c73240eea 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -22,8 +22,11 @@ #include <string> +#include <android-base/logging.h> + #include "arch/instruction_set.h" #include "base/mutex.h" +#include "dex/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 cd52bb6551..92d86519dc 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -18,14 +18,14 @@ #include <sstream> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" #include "invoke_type.h" #include "mirror/class-inl.h" #include "mirror/method_type.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/compiler_callbacks.h b/runtime/compiler_callbacks.h index 9041df94b9..4560bca922 100644 --- a/runtime/compiler_callbacks.h +++ b/runtime/compiler_callbacks.h @@ -55,7 +55,7 @@ class CompilerCallbacks { // Return the class status of a previous stage of the compilation. This can be used, for example, // when class unloading is enabled during multidex compilation. virtual ClassStatus GetPreviousClassState(ClassReference ref ATTRIBUTE_UNUSED) { - return ClassStatus::kStatusNotReady; + return ClassStatus::kNotReady; } virtual void SetDoesClassUnloading(bool does_class_unloading ATTRIBUTE_UNUSED, diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 1dcd935eea..842cd7330c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -33,13 +33,14 @@ #include "base/time_utils.h" #include "class_linker-inl.h" #include "class_linker.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" -#include "dex_file_types.h" -#include "dex_instruction.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction.h" #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" @@ -278,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_) { @@ -328,6 +326,7 @@ bool Dbg::gDisposed = false; ObjectRegistry* Dbg::gRegistry = nullptr; DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback; DebuggerDdmCallback Dbg::gDebugDdmCallback; +InternalDebuggerControlCallback Dbg::gDebuggerControlCallback; // Deoptimization support. std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_; @@ -364,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<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -736,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() { @@ -934,7 +948,7 @@ JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id, JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids, std::vector<uint64_t>* counts) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); VariableSizedHandleScope hs(Thread::Current()); std::vector<Handle<mirror::Class>> classes; counts->clear(); @@ -955,7 +969,7 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, std::vector<JDWP::ObjectId>* 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<mirror::Class> c = DecodeClass(class_id, &error); if (c == nullptr) { @@ -963,7 +977,11 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, } VariableSizedHandleScope hs(Thread::Current()); std::vector<Handle<mirror::Object>> 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())); } @@ -973,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<JDWP::ObjectId>* referring_objects) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); JDWP::JdwpError error; ObjPtr<mirror::Object> o = gRegistry->Get<mirror::Object*>(object_id, &error); if (o == nullptr) { @@ -1515,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 { @@ -1546,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 " @@ -1558,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; } @@ -1657,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); @@ -1680,10 +1698,10 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan context.numItems = 0; context.pReply = pReply; - if (code_item != nullptr) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems); @@ -1723,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. @@ -1738,12 +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) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugLocalInfo( - code_item, debug_info_offset, 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); @@ -1769,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<const uint8_t*>(code_item->insns_); + CodeItemDataAccessor accessor(m); + size_t byte_count = accessor.InsnsSizeInCodeUnits() * 2; + const uint8_t* begin = reinterpret_cast<const uint8_t*>(accessor.Insns()); const uint8_t* end = begin + byte_count; for (const uint8_t* p = begin; p != end; ++p) { bytecodes->push_back(*p); @@ -2954,9 +2976,8 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec Handle<mirror::Throwable> 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); @@ -3832,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) { @@ -3860,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); } } @@ -3869,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; }; @@ -3888,11 +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); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, 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. @@ -4372,15 +4392,20 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, replyData.get(), offset, length); - if (length == 0 || replyData.get() == nullptr) { - return false; - } - out_data->resize(length); env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(out_data->data())); + + if (env->ExceptionCheck()) { + LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", + type); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + return true; } @@ -4414,7 +4439,7 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep std::vector<uint8_t> out_data; uint32_t out_type = 0; request->Skip(request_length); - if (!DdmHandleChunk(env, type, data, &out_type, &out_data)) { + if (!DdmHandleChunk(env, type, data, &out_type, &out_data) || out_data.empty()) { return false; } const uint32_t kDdmHeaderSize = 8; diff --git a/runtime/debugger.h b/runtime/debugger.h index d5bad8dc67..74018137a0 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -53,18 +53,22 @@ class ScopedObjectAccessUnchecked; class StackVisitor; class Thread; +struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { + 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<const uint8_t>& data) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; -struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { - bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) - 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. */ @@ -251,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(); @@ -789,6 +794,7 @@ class Dbg { 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/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h new file mode 100644 index 0000000000..2fdf262b7d --- /dev/null +++ b/runtime/dex/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_DEX_CODE_ITEM_ACCESSORS_INL_H_ +#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_ + +#include "code_item_accessors-no_art-inl.h" + +#include "art_method-inl.h" +#include "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_DEX_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h index d04849d09a..016923d035 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/dex/code_item_accessors-no_art-inl.h @@ -14,16 +14,16 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ -#define ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ +#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ +#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ #include "code_item_accessors.h" -#include "art_method-inl.h" -#include "cdex/compact_dex_file.h" +#include "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) { @@ -37,14 +37,16 @@ inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& c } inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); + const DexFile::CodeItem* code_item) { + if (code_item != nullptr) { + DCHECK(dex_file->HasAddress(code_item)); + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); + } } } @@ -54,9 +56,6 @@ inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( Init(dex_file, code_item); } -inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method) - : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {} - inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { return DexInstructionIterator(insns_, 0u); } @@ -65,17 +64,12 @@ inline DexInstructionIterator CodeItemInstructionAccessor::end() const { return DexInstructionIterator(insns_, insns_size_in_code_units_); } -inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable( - ArtMethod* method) { - DCHECK(method != nullptr); - CodeItemInstructionAccessor ret; - const DexFile::CodeItem* code_item = method->GetCodeItem(); - if (code_item != nullptr) { - ret.Init(method->GetDexFile(), code_item); - } else { - DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; - } - return ret; +inline IterationRange<DexInstructionIterator> 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) { @@ -96,13 +90,14 @@ inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_ite inline void CodeItemDataAccessor::Init(const DexFile* dex_file, const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); + if (code_item != nullptr) { + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); + } } } @@ -111,26 +106,6 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, Init(dex_file, code_item); } -inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) - : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} - -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable( - const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - CodeItemDataAccessor ret; - if (code_item != nullptr) { - ret.Init(dex_file, code_item); - } else { - DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; - } - return ret; -} - -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { - DCHECK(method != nullptr); - return CreateNullable(method->GetDexFile(), method->GetCodeItem()); -} - inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const { const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); return { @@ -150,6 +125,42 @@ inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_de 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<const CompactDexFile::CodeItem&>(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast<const StandardDexFile::CodeItem&>(*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<typename NewLocalCallback> +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_INL_H_ +#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/dex/code_item_accessors.h index aa1305acad..65cc0bf996 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/dex/code_item_accessors.h @@ -16,11 +16,11 @@ // 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_ +#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ +#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ #include "base/mutex.h" -#include "cdex/compact_dex_file.h" +#include "compact_dex_file.h" #include "dex_file.h" #include "dex_instruction_iterator.h" #include "standard_dex_file.h" @@ -42,6 +42,8 @@ class CodeItemInstructionAccessor { ALWAYS_INLINE DexInstructionIterator end() const; + IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const; + uint32_t InsnsSizeInCodeUnits() const { return insns_size_in_code_units_; } @@ -52,6 +54,7 @@ class CodeItemInstructionAccessor { // 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); } @@ -60,10 +63,6 @@ class CodeItemInstructionAccessor { return Insns() != nullptr; } - // CreateNullable allows ArtMethods that have a null code item. - ALWAYS_INLINE static CodeItemInstructionAccessor CreateNullable(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_); - protected: CodeItemInstructionAccessor() = default; @@ -109,14 +108,6 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; - // CreateNullable allows ArtMethods that have a null code item. - ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_); - - ALWAYS_INLINE static CodeItemDataAccessor CreateNullable( - const DexFile* dex_file, - const DexFile::CodeItem* code_item); - protected: CodeItemDataAccessor() = default; @@ -132,6 +123,48 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { 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<typename NewLocalCallback> + 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_ +#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/code_item_accessors_test.cc b/runtime/dex/code_item_accessors_test.cc index ef5d246a54..57a5573d8d 100644 --- a/runtime/code_item_accessors_test.cc +++ b/runtime/dex/code_item_accessors_test.cc @@ -86,17 +86,17 @@ TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) { EXPECT_EQ(data_accessor.TriesSize(), kTriesSize); }; - uint8_t buffer1[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {}; - CompactDexFile::CodeItem* dex_code_item = reinterpret_cast<CompactDexFile::CodeItem*>(buffer1); + StandardDexFile::CodeItem* dex_code_item = + reinterpret_cast<StandardDexFile::CodeItem*>(const_cast<uint8_t*>(standard_dex->Begin())); 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_); + verify_code_item(standard_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<CompactDexFile::CodeItem*>(buffer2); + CompactDexFile::CodeItem* cdex_code_item = + reinterpret_cast<CompactDexFile::CodeItem*>(const_cast<uint8_t*>(compact_dex->Begin())); cdex_code_item->registers_size_ = kRegisterSize; cdex_code_item->ins_size_ = kInsSize; cdex_code_item->outs_size_ = kOutsSize; diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc new file mode 100644 index 0000000000..8f90e098bb --- /dev/null +++ b/runtime/dex/compact_dex_file.cc @@ -0,0 +1,90 @@ +/* + * 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 "compact_dex_file.h" + +#include "dex_file-inl.h" +#include "leb128.h" + +namespace art { + +constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize]; +constexpr uint8_t CompactDexFile::kDexMagicVersion[]; + +void CompactDexFile::WriteMagic(uint8_t* magic) { + std::copy_n(kDexMagic, kDexMagicSize, magic); +} + +void CompactDexFile::WriteCurrentVersion(uint8_t* magic) { + std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize); +} + +bool CompactDexFile::IsMagicValid(const uint8_t* magic) { + return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); +} + +bool CompactDexFile::IsVersionValid(const uint8_t* magic) { + const uint8_t* version = &magic[sizeof(kDexMagic)]; + return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0; +} + +bool CompactDexFile::IsMagicValid() const { + return IsMagicValid(header_->magic_); +} + +bool CompactDexFile::IsVersionValid() const { + return IsVersionValid(header_->magic_); +} + +bool CompactDexFile::SupportsDefaultMethods() const { + return (GetHeader().GetFeatureFlags() & + static_cast<uint32_t>(FeatureFlags::kDefaultMethods)) != 0; +} + +uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { + // TODO: Clean up this temporary code duplication with StandardDexFile. Eventually the + // implementations will differ. + DCHECK(HasAddress(&item)); + const CodeItem& code_item = down_cast<const CodeItem&>(item); + uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&code_item); + uint32_t insns_size = code_item.insns_size_in_code_units_; + uint32_t tries_size = code_item.tries_size_; + const uint8_t* handler_data = GetCatchHandlerData( + DexInstructionIterator(code_item.insns_, code_item.insns_size_in_code_units_), + code_item.tries_size_, + 0); + + if (tries_size == 0 || handler_data == nullptr) { + uintptr_t insns_end = reinterpret_cast<uintptr_t>(&code_item.insns_[insns_size]); + return insns_end - code_item_start; + } else { + // Get the start of the handler data. + uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data); + // Manually read each handler. + for (uint32_t i = 0; i < handlers_size; ++i) { + int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; + if (uleb128_count <= 0) { + uleb128_count = -uleb128_count + 1; + } + for (int32_t j = 0; j < uleb128_count; ++j) { + DecodeUnsignedLeb128(&handler_data); + } + } + return reinterpret_cast<uintptr_t>(handler_data) - code_item_start; + } +} + +} // namespace art diff --git a/runtime/cdex/compact_dex_file.h b/runtime/dex/compact_dex_file.h index f17f8cf68a..280c6f70cc 100644 --- a/runtime/cdex/compact_dex_file.h +++ b/runtime/dex/compact_dex_file.h @@ -14,9 +14,10 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_CDEX_COMPACT_DEX_FILE_H_ -#define ART_RUNTIME_CDEX_COMPACT_DEX_FILE_H_ +#ifndef ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ +#include "base/casts.h" #include "dex_file.h" namespace art { @@ -27,13 +28,26 @@ class CompactDexFile : public DexFile { static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; + enum class FeatureFlags : uint32_t { + kDefaultMethods = 0x1, + }; + class Header : public DexFile::Header { - // Same for now. + public: + uint32_t GetFeatureFlags() const { + return feature_flags_; + } + + private: + uint32_t feature_flags_ = 0u; + + friend class CompactDexWriter; }; struct CodeItem : public DexFile::CodeItem { private: // TODO: Insert compact dex specific fields here. + friend class CompactDexFile; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -51,6 +65,14 @@ class CompactDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; + const Header& GetHeader() const { + return down_cast<const Header&>(DexFile::GetHeader()); + } + + virtual bool SupportsDefaultMethods() const OVERRIDE; + + uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; + private: // Not supported yet. CompactDexFile(const uint8_t* base, @@ -75,4 +97,4 @@ class CompactDexFile : public DexFile { } // namespace art -#endif // ART_RUNTIME_CDEX_COMPACT_DEX_FILE_H_ +#endif // ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_ diff --git a/runtime/cdex/compact_dex_file_test.cc b/runtime/dex/compact_dex_file_test.cc index b43b35d69a..d665dc994b 100644 --- a/runtime/cdex/compact_dex_file_test.cc +++ b/runtime/dex/compact_dex_file_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "cdex/compact_dex_file.h" -#include "dex_file_loader.h" #include "common_runtime_test.h" +#include "compact_dex_file.h" +#include "dex_file_loader.h" namespace art { diff --git a/runtime/dex/compact_dex_level.h b/runtime/dex/compact_dex_level.h new file mode 100644 index 0000000000..de9ca3c783 --- /dev/null +++ b/runtime/dex/compact_dex_level.h @@ -0,0 +1,50 @@ +/* + * 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_DEX_COMPACT_DEX_LEVEL_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_ + +#include <string> + +#include "base/macros.h" +#include "dex_file.h" + +namespace art { + +// Optimization level for compact dex generation. +enum class CompactDexLevel { + // Level none means not generated. + kCompactDexLevelNone, + // Level fast means optimizations that don't take many resources to perform. + 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_DEX_COMPACT_DEX_LEVEL_H_ diff --git a/runtime/dex_file-inl.h b/runtime/dex/dex_file-inl.h index 18809683cc..9b56328a71 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex/dex_file-inl.h @@ -14,14 +14,13 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_INL_H_ -#define ART_RUNTIME_DEX_FILE_INL_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_INL_H_ +#define ART_RUNTIME_DEX_DEX_FILE_INL_H_ #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stringpiece.h" -#include "cdex/compact_dex_file.h" +#include "compact_dex_file.h" #include "dex_file.h" #include "invoke_type.h" #include "leb128.h" @@ -134,10 +133,6 @@ inline const char* DexFile::GetShorty(uint32_t proto_idx) const { return StringDataByIdx(proto_id.shorty_idx_); } -inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { - return GetTryItems(code_item.Instructions().end(), offset); -} - inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset) { return reinterpret_cast<const TryItem*> @@ -385,13 +380,16 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, } template<typename NewLocalCallback> -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<const char*> arg_descriptors; @@ -399,15 +397,15 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, for (; it.HasNext(); it.Next()) { arg_descriptors.push_back(it.GetDescriptor()); } - return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset), + 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)); }, @@ -488,13 +486,9 @@ bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, } template<typename DexDebugNewPosition> -bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, +bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const { - if (code_item == nullptr) { - return false; - } return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); @@ -513,6 +507,15 @@ inline const StandardDexFile* DexFile::AsStandardDexFile() const { return down_cast<const StandardDexFile*>(this); } +// Get the base of the encoded data for the given DexCode. +inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& code_item_end, + uint32_t tries_size, + uint32_t offset) { + const uint8_t* handler_data = + reinterpret_cast<const uint8_t*>(GetTryItems(code_item_end, tries_size)); + return handler_data + offset; +} + } // namespace art -#endif // ART_RUNTIME_DEX_FILE_INL_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_INL_H_ diff --git a/runtime/dex_file.cc b/runtime/dex/dex_file.cc index af79207834..16325b83f6 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex/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<dex::TypeIndex>::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 { @@ -214,32 +217,6 @@ uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def, UNREACHABLE(); } -uint32_t DexFile::GetCodeItemSize(const DexFile::CodeItem& code_item) { - uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&code_item); - uint32_t insns_size = code_item.insns_size_in_code_units_; - uint32_t tries_size = code_item.tries_size_; - const uint8_t* handler_data = GetCatchHandlerData(code_item, 0); - - if (tries_size == 0 || handler_data == nullptr) { - uintptr_t insns_end = reinterpret_cast<uintptr_t>(&code_item.insns_[insns_size]); - return insns_end - code_item_start; - } else { - // Get the start of the handler data. - uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data); - // Manually read each handler. - for (uint32_t i = 0; i < handlers_size; ++i) { - int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; - if (uleb128_count <= 0) { - uleb128_count = -uleb128_count + 1; - } - for (int32_t j = 0; j < uleb128_count; ++j) { - DecodeUnsignedLeb128(&handler_data); - } - } - return reinterpret_cast<uintptr_t>(handler_data) - code_item_start; - } -} - const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass, const DexFile::StringId& name, const DexFile::TypeId& type) const { @@ -508,11 +485,6 @@ int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint return -1; } -int32_t DexFile::FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address) { - 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) { LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context); @@ -799,85 +771,6 @@ void EncodedArrayValueIterator::Next() { ptr_ += width; } -CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) { - handler_.address_ = -1; - int32_t offset = -1; - - // Short-circuit the overwhelmingly common cases. - switch (code_item.tries_size_) { - case 0: - break; - case 1: { - const DexFile::TryItem* tries = DexFile::GetTryItems(code_item, 0); - uint32_t start = tries->start_addr_; - if (address >= start) { - uint32_t end = start + tries->insn_count_; - if (address < end) { - offset = tries->handler_off_; - } - } - break; - } - default: - offset = DexFile::FindCatchHandlerOffset(code_item, address); - } - Init(code_item, offset); -} - -CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, - const DexFile::TryItem& try_item) { - handler_.address_ = -1; - Init(code_item, try_item.handler_off_); -} - -void CatchHandlerIterator::Init(const DexFile::CodeItem& code_item, - int32_t offset) { - if (offset >= 0) { - Init(DexFile::GetCatchHandlerData(code_item, offset)); - } else { - // Not found, initialize as empty - current_data_ = nullptr; - remaining_count_ = -1; - catch_all_ = false; - DCHECK(!HasNext()); - } -} - -void CatchHandlerIterator::Init(const uint8_t* handler_data) { - current_data_ = handler_data; - remaining_count_ = DecodeSignedLeb128(¤t_data_); - - // If remaining_count_ is non-positive, then it is the negative of - // the number of catch types, and the catches are followed by a - // catch-all handler. - if (remaining_count_ <= 0) { - catch_all_ = true; - remaining_count_ = -remaining_count_; - } else { - catch_all_ = false; - } - Next(); -} - -void CatchHandlerIterator::Next() { - if (remaining_count_ > 0) { - handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(¤t_data_)); - handler_.address_ = DecodeUnsignedLeb128(¤t_data_); - remaining_count_--; - return; - } - - if (catch_all_) { - handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); - handler_.address_ = DecodeUnsignedLeb128(¤t_data_); - catch_all_ = false; - return; - } - - // no more handler - remaining_count_ = -1; -} - namespace dex { std::ostream& operator<<(std::ostream& os, const StringIndex& index) { diff --git a/runtime/dex_file.h b/runtime/dex/dex_file.h index 944a30849f..c2a36ce01a 100644 --- a/runtime/dex_file.h +++ b/runtime/dex/dex_file.h @@ -14,15 +14,17 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_H_ -#define ART_RUNTIME_DEX_FILE_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_H_ +#define ART_RUNTIME_DEX_DEX_FILE_H_ #include <memory> #include <string> #include <vector> +#include <android-base/logging.h> + #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" @@ -67,8 +69,6 @@ class DexFile { static constexpr size_t kDexMagicSize = 4; static constexpr size_t kDexVersionLen = 4; - // First Dex format version supporting default methods. - static const uint32_t kDefaultMethodsVersion = 37; // First Dex format version enforcing class definition ordering rules. static const uint32_t kClassDefinitionOrderEnforcedVersion = 37; @@ -303,21 +303,16 @@ class DexFile { // Raw code_item. struct CodeItem { - IterationRange<DexInstructionIterator> Instructions(uint32_t start_dex_pc = 0u) const { - DCHECK_LE(start_dex_pc, insns_size_in_code_units_); - return { DexInstructionIterator(insns_, start_dex_pc), - DexInstructionIterator(insns_, insns_size_in_code_units_) }; - } - - const Instruction& InstructionAt(uint32_t dex_pc) const { - return *Instruction::At(insns_ + dex_pc); - } - // Used when quickening / unquickening. void SetDebugInfoOffset(uint32_t new_offset) { debug_info_off_ = new_offset; } + uint32_t GetDebugInfoOffset() const { + return debug_info_off_; + } + + protected: 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 @@ -338,6 +333,11 @@ class DexFile { uint16_t insns_[1]; // actual array of bytecode. private: + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; + friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -472,7 +472,7 @@ class DexFile { } // Decode the dex magic version - uint32_t GetVersion() const { + uint32_t GetDexVersion() const { return GetHeader().GetVersion(); } @@ -482,6 +482,9 @@ class DexFile { // Returns true if the byte string after the magic is the correct value. virtual bool IsVersionValid() const = 0; + // Returns true if the dex file supports default methods. + virtual bool SupportsDefaultMethods() const = 0; + // Returns the number of string identifiers in the .dex file. size_t NumStringIds() const { DCHECK(header_ != nullptr) << GetLocation(); @@ -583,7 +586,7 @@ class DexFile { uint32_t FindCodeItemOffset(const DexFile::ClassDef& class_def, uint32_t dex_method_idx) const; - static uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item); + virtual uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) const = 0; // Returns the declaring class descriptor string of a field id. const char* GetFieldDeclaringClassDescriptor(const FieldId& field_id) const { @@ -715,7 +718,7 @@ class DexFile { } CHECK(oat_dex_file_ == nullptr) << "Should only use GetDebugInfoOffset in a non runtime setup"; - return code_item->debug_info_off_; + return code_item->GetDebugInfoOffset(); } const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; @@ -774,26 +777,15 @@ class DexFile { } 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 DexInstructionIterator& code_item_end, uint32_t tries_size, - uint32_t offset) { - const uint8_t* handler_data = - reinterpret_cast<const uint8_t*>(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); - } + uint32_t offset); // Find which try region is associated with the given address (ie dex pc). Returns -1 if none. 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(uint32_t debug_info_off) const { // Check that the offset is in bounds. @@ -954,7 +946,9 @@ class DexFile { NewLocalCallback new_local, void* context); template<typename NewLocalCallback> - 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, @@ -968,8 +962,7 @@ class DexFile { DexDebugNewPosition position_functor, void* context); template<typename DexDebugNewPosition> - bool DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, + bool DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const; @@ -1014,6 +1007,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; @@ -1032,7 +1026,14 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; + bool HasAddress(const void* addr) const { + return Begin() <= addr && addr < Begin() + Size(); + } + protected: + // First Dex format version supporting default methods. + static const uint32_t kDefaultMethodsVersion = 37; + DexFile(const uint8_t* base, size_t size, const std::string& location, @@ -1453,47 +1454,6 @@ class CallSiteArrayValueIterator : public EncodedArrayValueIterator { }; std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code); -class CatchHandlerIterator { - public: - CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address); - - CatchHandlerIterator(const DexFile::CodeItem& code_item, - const DexFile::TryItem& try_item); - - explicit CatchHandlerIterator(const uint8_t* handler_data) { - Init(handler_data); - } - - dex::TypeIndex GetHandlerTypeIndex() const { - return handler_.type_idx_; - } - uint32_t GetHandlerAddress() const { - return handler_.address_; - } - void Next(); - bool HasNext() const { - return remaining_count_ != -1 || catch_all_; - } - // End of this set of catch blocks, convenience method to locate next set of catch blocks - const uint8_t* EndDataPointer() const { - CHECK(!HasNext()); - return current_data_; - } - - private: - void Init(const DexFile::CodeItem& code_item, int32_t offset); - void Init(const uint8_t* handler_data); - - struct CatchHandlerItem { - dex::TypeIndex type_idx_; // type index of the caught exception type - uint32_t address_; // handler address - } handler_; - const uint8_t* current_data_; // the current handler in dex file. - int32_t remaining_count_; // number of handlers not read. - bool catch_all_; // is there a handler that will catch all exceptions in case - // that all typed handler does not match. -}; - } // namespace art -#endif // ART_RUNTIME_DEX_FILE_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_H_ diff --git a/runtime/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index b44bd51643..72b18fb420 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -343,8 +343,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> 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) { @@ -458,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; @@ -474,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())); @@ -501,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())); @@ -540,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())); @@ -569,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()), @@ -783,10 +778,8 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( uint32_t type_index = DecodeUnsignedLeb128(&annotation); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); - mirror::Class* resolved_class; StackHandleScope<2> hs(self); - resolved_class = class_linker->ResolveType( - klass.GetDexFile(), + ObjPtr<mirror::Class> resolved_class = class_linker->ResolveType( dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -1401,7 +1394,6 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> 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())); @@ -1567,21 +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); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); - dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, DexFile::LineNumForPcCb, &context); + dex_file->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), DexFile::LineNumForPcCb, &context); return context.line_num_; } template<bool kTransactionActive> void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) const { DCHECK(dex_cache_ != nullptr); - DCHECK(class_loader_ != nullptr); switch (type_) { case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break; @@ -1594,17 +1583,15 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kDouble: field->SetDouble<kTransactionActive>(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject<kTransactionActive>(field->GetDeclaringClass(), nullptr); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, - dex::StringIndex(jval_.i), - *dex_cache_); + ObjPtr<mirror::String> resolved = linker_->ResolveString(dex::StringIndex(jval_.i), + dex_cache_); field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, - dex::TypeIndex(jval_.i), - *dex_cache_, - *class_loader_); + ObjPtr<mirror::Class> resolved = linker_->ResolveType(dex::TypeIndex(jval_.i), + dex_cache_, + class_loader_); field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h index a934a4f99c..26773729c2 100644 --- a/runtime/dex_file_annotations.h +++ b/runtime/dex/dex_file_annotations.h @@ -14,23 +14,23 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_ANNOTATIONS_H_ -#define ART_RUNTIME_DEX_FILE_ANNOTATIONS_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_ +#define ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_ #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 T> class MutableHandle; namespace annotations { @@ -116,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<mirror::DexCache>* dex_cache, - Handle<mirror::ClassLoader>* class_loader, + RuntimeEncodedStaticFieldValueIterator(Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> 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) { @@ -132,9 +131,9 @@ class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIte void ReadValueToField(ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_); private: - Handle<mirror::DexCache>* const dex_cache_; // Dex cache to resolve literal objects. - Handle<mirror::ClassLoader>* const class_loader_; // ClassLoader to resolve types. - ClassLinker* linker_; // Linker to resolve literal objects. + const Handle<mirror::DexCache> dex_cache_; // Dex cache to resolve literal objects. + const Handle<mirror::ClassLoader> class_loader_; // ClassLoader to resolve types. + ClassLinker* const linker_; // Linker to resolve literal objects. DISALLOW_IMPLICIT_CONSTRUCTORS(RuntimeEncodedStaticFieldValueIterator); }; @@ -142,4 +141,4 @@ class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIte } // namespace art -#endif // ART_RUNTIME_DEX_FILE_ANNOTATIONS_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_ diff --git a/runtime/dex/dex_file_exception_helpers.cc b/runtime/dex/dex_file_exception_helpers.cc new file mode 100644 index 0000000000..ad56eb0a0b --- /dev/null +++ b/runtime/dex/dex_file_exception_helpers.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 "dex_file_exception_helpers.h" + +#include "code_item_accessors-no_art-inl.h" + +namespace art { + +CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address) { + handler_.address_ = -1; + int32_t offset = -1; + + // Short-circuit the overwhelmingly common cases. + switch (accessor.TriesSize()) { + case 0: + break; + case 1: { + const DexFile::TryItem* tries = accessor.TryItems().begin(); + uint32_t start = tries->start_addr_; + if (address >= start) { + uint32_t end = start + tries->insn_count_; + if (address < end) { + offset = tries->handler_off_; + } + } + break; + } + default: { + const DexFile::TryItem* try_item = accessor.FindTryItem(address); + offset = try_item != nullptr ? try_item->handler_off_ : -1; + break; + } + } + Init(accessor, offset); +} + +CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, + const DexFile::TryItem& try_item) { + handler_.address_ = -1; + Init(accessor, try_item.handler_off_); +} + +void CatchHandlerIterator::Init(const CodeItemDataAccessor& accessor, int32_t offset) { + if (offset >= 0) { + Init(accessor.GetCatchHandlerData(offset)); + } else { + // Not found, initialize as empty + current_data_ = nullptr; + remaining_count_ = -1; + catch_all_ = false; + DCHECK(!HasNext()); + } +} + +void CatchHandlerIterator::Init(const uint8_t* handler_data) { + current_data_ = handler_data; + remaining_count_ = DecodeSignedLeb128(¤t_data_); + + // If remaining_count_ is non-positive, then it is the negative of + // the number of catch types, and the catches are followed by a + // catch-all handler. + if (remaining_count_ <= 0) { + catch_all_ = true; + remaining_count_ = -remaining_count_; + } else { + catch_all_ = false; + } + Next(); +} + +void CatchHandlerIterator::Next() { + if (remaining_count_ > 0) { + handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(¤t_data_)); + handler_.address_ = DecodeUnsignedLeb128(¤t_data_); + remaining_count_--; + return; + } + + if (catch_all_) { + handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); + handler_.address_ = DecodeUnsignedLeb128(¤t_data_); + catch_all_ = false; + return; + } + + // no more handler + remaining_count_ = -1; +} + +} // namespace art diff --git a/runtime/dex/dex_file_exception_helpers.h b/runtime/dex/dex_file_exception_helpers.h new file mode 100644 index 0000000000..bd6cb7e747 --- /dev/null +++ b/runtime/dex/dex_file_exception_helpers.h @@ -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. + */ + +#ifndef ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ +#define ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ + +#include "dex_file.h" + +namespace art { + +class CodeItemDataAccessor; + +class CatchHandlerIterator { + public: + CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address); + + CatchHandlerIterator(const CodeItemDataAccessor& accessor, const DexFile::TryItem& try_item); + + explicit CatchHandlerIterator(const uint8_t* handler_data) { + Init(handler_data); + } + + dex::TypeIndex GetHandlerTypeIndex() const { + return handler_.type_idx_; + } + uint32_t GetHandlerAddress() const { + return handler_.address_; + } + void Next(); + bool HasNext() const { + return remaining_count_ != -1 || catch_all_; + } + // End of this set of catch blocks, convenience method to locate next set of catch blocks + const uint8_t* EndDataPointer() const { + CHECK(!HasNext()); + return current_data_; + } + + private: + void Init(const CodeItemDataAccessor& accessor, int32_t offset); + void Init(const uint8_t* handler_data); + + struct CatchHandlerItem { + dex::TypeIndex type_idx_; // type index of the caught exception type + uint32_t address_; // handler address + } handler_; + const uint8_t* current_data_; // the current handler in dex file. + int32_t remaining_count_; // number of handlers not read. + bool catch_all_; // is there a handler that will catch all exceptions in case + // that all typed handler does not match. +}; + +} // namespace art + +#endif // ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_ diff --git a/runtime/dex_file_layout.cc b/runtime/dex/dex_file_layout.cc index 1973440d55..1973440d55 100644 --- a/runtime/dex_file_layout.cc +++ b/runtime/dex/dex_file_layout.cc diff --git a/runtime/dex_file_layout.h b/runtime/dex/dex_file_layout.h index 4c960c3ff5..a7b9051f24 100644 --- a/runtime/dex_file_layout.h +++ b/runtime/dex/dex_file_layout.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_LAYOUT_H_ -#define ART_RUNTIME_DEX_FILE_LAYOUT_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ +#define ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ #include <algorithm> #include <cstdint> #include <iosfwd> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { @@ -121,4 +121,4 @@ std::ostream& operator<<(std::ostream& os, const DexLayoutSections& sections); } // namespace art -#endif // ART_RUNTIME_DEX_FILE_LAYOUT_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_LAYOUT_H_ diff --git a/runtime/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc index bc9276985b..fafd69889d 100644 --- a/runtime/dex_file_loader.cc +++ b/runtime/dex/dex_file_loader.cc @@ -25,7 +25,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" -#include "cdex/compact_dex_file.h" +#include "compact_dex_file.h" #include "dex_file.h" #include "dex_file_verifier.h" #include "standard_dex_file.h" diff --git a/runtime/dex_file_loader.h b/runtime/dex/dex_file_loader.h index 17631234b3..7db8d8e08e 100644 --- a/runtime/dex_file_loader.h +++ b/runtime/dex/dex_file_loader.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_LOADER_H_ -#define ART_RUNTIME_DEX_FILE_LOADER_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ +#define ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ #include <cstdint> #include <memory> @@ -202,4 +202,4 @@ class DexFileLoader { } // namespace art -#endif // ART_RUNTIME_DEX_FILE_LOADER_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_LOADER_H_ diff --git a/runtime/dex_file_reference.h b/runtime/dex/dex_file_reference.h index 01a64257a8..6f882900c6 100644 --- a/runtime/dex_file_reference.h +++ b/runtime/dex/dex_file_reference.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_REFERENCE_H_ -#define ART_RUNTIME_DEX_FILE_REFERENCE_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ +#define ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ #include <cstdint> @@ -49,4 +49,4 @@ inline bool operator==(const DexFileReference& a, const DexFileReference& b) { } // namespace art -#endif // ART_RUNTIME_DEX_FILE_REFERENCE_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_ diff --git a/runtime/dex_file_test.cc b/runtime/dex/dex_file_test.cc index 14c36b4538..87eec571f1 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex/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" @@ -730,8 +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)); - uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, 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/dex_file_tracking_registrar.cc index 874d8ea905..bffca5599a 100644 --- a/runtime/dex_file_tracking_registrar.cc +++ b/runtime/dex/dex_file_tracking_registrar.cc @@ -19,6 +19,8 @@ #include <deque> #include <tuple> +#include <android-base/logging.h> + // 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 { @@ -162,7 +164,7 @@ void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast<const void*>(code_item); - size_t code_item_size = DexFile::GetCodeItemSize(*code_item); + size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); } cdit.Next(); @@ -183,9 +185,12 @@ void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poiso if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast<const void*>(code_item); size_t code_item_start = reinterpret_cast<size_t>(code_item); - size_t code_item_start_end = reinterpret_cast<size_t>(&code_item->insns_[1]); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + size_t code_item_start_end = reinterpret_cast<size_t>(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(); } @@ -203,9 +208,10 @@ void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { - const void* insns_begin = reinterpret_cast<const void*>(&code_item->insns_); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + const void* insns_begin = reinterpret_cast<const void*>(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(); @@ -227,7 +233,7 @@ void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, b const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) { const void* code_item_begin = reinterpret_cast<const void*>(code_item); - size_t code_item_size = DexFile::GetCodeItemSize(*code_item); + size_t code_item_size = dex_file_->GetCodeItemSize(*code_item); range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison)); } cdit.Next(); diff --git a/runtime/dex_file_tracking_registrar.h b/runtime/dex/dex_file_tracking_registrar.h index 5c0e0f50ab..71b8ed7bde 100644 --- a/runtime/dex_file_tracking_registrar.h +++ b/runtime/dex/dex_file_tracking_registrar.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_TRACKING_REGISTRAR_H_ -#define ART_RUNTIME_DEX_FILE_TRACKING_REGISTRAR_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ +#define ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ #include <deque> #include <tuple> @@ -78,4 +78,4 @@ void RegisterDexFile(const DexFile* dex_file); } // namespace dex } // namespace art -#endif // ART_RUNTIME_DEX_FILE_TRACKING_REGISTRAR_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_ diff --git a/runtime/dex_file_types.h b/runtime/dex/dex_file_types.h index acca7c055b..2c508f9c99 100644 --- a/runtime/dex_file_types.h +++ b/runtime/dex/dex_file_types.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_TYPES_H_ -#define ART_RUNTIME_DEX_FILE_TYPES_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ +#define ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ #include <limits> #include <ostream> @@ -114,4 +114,4 @@ template<> struct hash<art::dex::TypeIndex> { } // namespace std -#endif // ART_RUNTIME_DEX_FILE_TYPES_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_TYPES_H_ diff --git a/runtime/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc index edf5650df1..d6f685a595 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex/dex_file_verifier.cc @@ -23,6 +23,7 @@ #include "android-base/stringprintf.h" +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "experimental_flags.h" #include "leb128.h" @@ -385,7 +386,14 @@ bool DexFileVerifier::CheckHeader() { return false; } - if (header_->header_size_ != sizeof(DexFile::Header)) { + bool size_matches = false; + if (dex_file_->IsCompactDexFile()) { + size_matches = header_->header_size_ == sizeof(CompactDexFile::Header); + } else { + size_matches = header_->header_size_ == sizeof(StandardDexFile::Header); + } + + if (!size_matches) { ErrorStringPrintf("Bad header size: %ud", header_->header_size_); return false; } @@ -572,7 +580,8 @@ uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) { bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, uint32_t* handler_offsets, uint32_t handlers_size) { - const uint8_t* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0); + CodeItemDataAccessor accessor(dex_file_, code_item); + const uint8_t* handlers_base = accessor.GetCatchHandlerData(); for (uint32_t i = 0; i < handlers_size; i++) { bool catch_all; @@ -600,7 +609,7 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it } DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr); - if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) { + if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) { ErrorStringPrintf("Invalid handler addr: %x", addr); return false; } @@ -608,7 +617,7 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it if (catch_all) { DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr); - if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) { + if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) { ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr); return false; } @@ -1224,14 +1233,14 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - if (UNLIKELY(code_item->ins_size_ > code_item->registers_size_)) { + CodeItemDataAccessor accessor(dex_file_, code_item); + if (UNLIKELY(accessor.InsSize() > accessor.RegistersSize())) { ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)", - code_item->ins_size_, code_item->registers_size_); + accessor.InsSize(), accessor.RegistersSize()); return false; } - if (UNLIKELY((code_item->outs_size_ > 5) && - (code_item->outs_size_ > code_item->registers_size_))) { + if (UNLIKELY(accessor.OutsSize() > 5 && accessor.OutsSize() > accessor.RegistersSize())) { /* * outs_size can be up to 5, even if registers_size is smaller, since the * short forms of method invocation allow repetitions of a register multiple @@ -1239,18 +1248,18 @@ bool DexFileVerifier::CheckIntraCodeItem() { * need to be represented in-order in the register file. */ ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)", - code_item->outs_size_, code_item->registers_size_); + accessor.OutsSize(), accessor.RegistersSize()); return false; } - const uint16_t* insns = code_item->insns_; - uint32_t insns_size = code_item->insns_size_in_code_units_; + const uint16_t* insns = accessor.Insns(); + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); if (!CheckListSize(insns, insns_size, sizeof(uint16_t), "insns size")) { return false; } // Grab the end of the insns if there are no try_items. - uint32_t try_items_size = code_item->tries_size_; + uint32_t try_items_size = accessor.TriesSize(); if (try_items_size == 0) { ptr_ = reinterpret_cast<const uint8_t*>(&insns[insns_size]); return true; @@ -1262,12 +1271,12 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - const DexFile::TryItem* try_items = DexFile::GetTryItems(*code_item, 0); + const DexFile::TryItem* try_items = accessor.TryItems().begin(); if (!CheckListSize(try_items, try_items_size, sizeof(DexFile::TryItem), "try_items size")) { return false; } - ptr_ = DexFile::GetCatchHandlerData(*code_item, 0); + ptr_ = accessor.GetCatchHandlerData(); DECODE_UNSIGNED_CHECKED_FROM(ptr_, handlers_size); if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) { @@ -3002,7 +3011,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, GetFieldDescriptionOrError(begin_, header_, idx).c_str(), field_access_flags, PrettyJavaAccessFlags(field_access_flags).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. @@ -3017,7 +3026,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, GetFieldDescriptionOrError(begin_, header_, idx).c_str(), field_access_flags, PrettyJavaAccessFlags(field_access_flags).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. @@ -3100,7 +3109,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. @@ -3129,14 +3138,14 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, if ((class_access_flags & kAccInterface) != 0) { // Non-static interface methods must be public or private. uint32_t desired_flags = (kAccPublic | kAccStatic); - if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { desired_flags |= kAccPrivate; } if ((method_access_flags & desired_flags) == 0) { *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. @@ -3161,7 +3170,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. @@ -3195,7 +3204,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { return false; } else { // Allow in older versions, but warn. diff --git a/runtime/dex_file_verifier.h b/runtime/dex/dex_file_verifier.h index 23089fa215..6cb5d4c629 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex/dex_file_verifier.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_FILE_VERIFIER_H_ -#define ART_RUNTIME_DEX_FILE_VERIFIER_H_ +#ifndef ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ +#define ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ #include <unordered_set> @@ -254,4 +254,4 @@ class DexFileVerifier { } // namespace art -#endif // ART_RUNTIME_DEX_FILE_VERIFIER_H_ +#endif // ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_ diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex/dex_file_verifier_test.cc index d4d912cbfb..d4d912cbfb 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex/dex_file_verifier_test.cc diff --git a/runtime/dex_instruction-inl.h b/runtime/dex/dex_instruction-inl.h index f6ed1f03b7..a6b8414e62 100644 --- a/runtime/dex_instruction-inl.h +++ b/runtime/dex/dex_instruction-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_INSTRUCTION_INL_H_ -#define ART_RUNTIME_DEX_INSTRUCTION_INL_H_ +#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ +#define ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ #include "dex_instruction.h" @@ -555,4 +555,4 @@ inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_ } // namespace art -#endif // ART_RUNTIME_DEX_INSTRUCTION_INL_H_ +#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_ diff --git a/runtime/dex_instruction.cc b/runtime/dex/dex_instruction.cc index 6ebe2286e8..6ebe2286e8 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex/dex_instruction.cc diff --git a/runtime/dex_instruction.h b/runtime/dex/dex_instruction.h index 4041820616..8b1a5ce670 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex/dex_instruction.h @@ -14,10 +14,11 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_INSTRUCTION_H_ -#define ART_RUNTIME_DEX_INSTRUCTION_H_ +#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ +#define ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ + +#include <android-base/logging.h> -#include "base/logging.h" #include "base/macros.h" #include "globals.h" @@ -738,4 +739,4 @@ class VarArgsInstructionOperands FINAL : public InstructionOperands { } // namespace art -#endif // ART_RUNTIME_DEX_INSTRUCTION_H_ +#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_H_ diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex/dex_instruction_iterator.h index be583a2533..c1b3118f85 100644 --- a/runtime/dex_instruction_iterator.h +++ b/runtime/dex/dex_instruction_iterator.h @@ -14,13 +14,15 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_ -#define ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_ +#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ +#define ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ #include <iterator> +#include <android-base/logging.h> + +#include "base/macros.h" #include "dex_instruction.h" -#include "base/logging.h" namespace art { @@ -232,4 +234,4 @@ class SafeDexInstructionIterator : public DexInstructionIteratorBase { } // namespace art -#endif // ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_ +#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_ diff --git a/runtime/dex_instruction_list.h b/runtime/dex/dex_instruction_list.h index ef83bdc216..aa63fadb66 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex/dex_instruction_list.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_INSTRUCTION_LIST_H_ -#define ART_RUNTIME_DEX_INSTRUCTION_LIST_H_ +#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ +#define ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ // V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags); #define DEX_INSTRUCTION_LIST(V) \ @@ -304,5 +304,5 @@ V(k4rcc) \ V(k51l) -#endif // ART_RUNTIME_DEX_INSTRUCTION_LIST_H_ -#undef ART_RUNTIME_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint +#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ +#undef ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint diff --git a/runtime/dex_instruction_test.cc b/runtime/dex/dex_instruction_test.cc index c944085b9e..c944085b9e 100644 --- a/runtime/dex_instruction_test.cc +++ b/runtime/dex/dex_instruction_test.cc diff --git a/runtime/dex_instruction_utils.h b/runtime/dex/dex_instruction_utils.h index 72d82442b5..27501927e7 100644 --- a/runtime/dex_instruction_utils.h +++ b/runtime/dex/dex_instruction_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_ -#define ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_ +#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ +#define ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ #include "dex_instruction.h" @@ -216,4 +216,4 @@ constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) { } // namespace art -#endif // ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_ +#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_ diff --git a/runtime/standard_dex_file.cc b/runtime/dex/standard_dex_file.cc index 4c1d3081d8..843508d831 100644 --- a/runtime/standard_dex_file.cc +++ b/runtime/dex/standard_dex_file.cc @@ -16,6 +16,10 @@ #include "standard_dex_file.h" +#include "base/casts.h" +#include "dex_file-inl.h" +#include "leb128.h" + namespace art { const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; @@ -63,4 +67,39 @@ bool StandardDexFile::IsVersionValid() const { return IsVersionValid(header_->magic_); } +bool StandardDexFile::SupportsDefaultMethods() const { + return GetDexVersion() >= DexFile::kDefaultMethodsVersion; +} + +uint32_t StandardDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { + DCHECK(HasAddress(&item)); + const CodeItem& code_item = down_cast<const CodeItem&>(item); + uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&code_item); + uint32_t insns_size = code_item.insns_size_in_code_units_; + uint32_t tries_size = code_item.tries_size_; + const uint8_t* handler_data = GetCatchHandlerData( + DexInstructionIterator(code_item.insns_, code_item.insns_size_in_code_units_), + code_item.tries_size_, + 0); + + if (tries_size == 0 || handler_data == nullptr) { + uintptr_t insns_end = reinterpret_cast<uintptr_t>(&code_item.insns_[insns_size]); + return insns_end - code_item_start; + } else { + // Get the start of the handler data. + uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data); + // Manually read each handler. + for (uint32_t i = 0; i < handlers_size; ++i) { + int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2; + if (uleb128_count <= 0) { + uleb128_count = -uleb128_count + 1; + } + for (int32_t j = 0; j < uleb128_count; ++j) { + DecodeUnsignedLeb128(&handler_data); + } + } + return reinterpret_cast<uintptr_t>(handler_data) - code_item_start; + } +} + } // namespace art diff --git a/runtime/standard_dex_file.h b/runtime/dex/standard_dex_file.h index 5d5359776d..fb2f720920 100644 --- a/runtime/standard_dex_file.h +++ b/runtime/dex/standard_dex_file.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_STANDARD_DEX_FILE_H_ -#define ART_RUNTIME_STANDARD_DEX_FILE_H_ +#ifndef ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ +#define ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ #include <iosfwd> @@ -35,6 +35,7 @@ class StandardDexFile : public DexFile { struct CodeItem : public DexFile::CodeItem { private: // TODO: Insert standard dex specific fields here. + friend class StandardDexFile; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -56,6 +57,10 @@ class StandardDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; + virtual bool SupportsDefaultMethods() const OVERRIDE; + + uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; + private: StandardDexFile(const uint8_t* base, size_t size, @@ -81,4 +86,4 @@ class StandardDexFile : public DexFile { } // namespace art -#endif // ART_RUNTIME_STANDARD_DEX_FILE_H_ +#endif // ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_ diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 5f9b3cf6c4..e459f09e95 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -27,7 +27,7 @@ #include "base/stl_util.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h index 2278b052ed..ca6afc5b92 100644 --- a/runtime/dex_cache_resolved_classes.h +++ b/runtime/dex_cache_resolved_classes.h @@ -21,7 +21,7 @@ #include <unordered_set> #include <vector> -#include "dex_file_types.h" +#include "dex/dex_file_types.h" namespace art { diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index a5ebaded5f..f3f2d52cb4 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -16,11 +16,14 @@ #include "dex_to_dex_decompiler.h" -#include "base/logging.h" +#include <android-base/logging.h> + +#include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" #include "quicken_info.h" namespace art { @@ -28,10 +31,11 @@ namespace optimizer { class DexDecompiler { public: - DexDecompiler(const DexFile::CodeItem& code_item, + DexDecompiler(const DexFile& dex_file, + const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info, bool decompile_return_instruction) - : code_item_(code_item), + : code_item_accessor_(&dex_file, &code_item), quicken_info_(quickened_info.data()), quicken_info_number_of_indices_(QuickenInfoTable::NumberOfIndices(quickened_info.size())), decompile_return_instruction_(decompile_return_instruction) {} @@ -74,7 +78,7 @@ class DexDecompiler { return ret; } - const DexFile::CodeItem& code_item_; + const CodeItemInstructionAccessor code_item_accessor_; const QuickenInfoTable quicken_info_; const size_t quicken_info_number_of_indices_; const bool decompile_return_instruction_; @@ -89,7 +93,7 @@ bool DexDecompiler::Decompile() { // because the RETURN_VOID quickening is not encoded in the quickening data. Because // unquickening is a rare need and not performance sensitive, it is not worth the // added storage to also add the RETURN_VOID quickening in the quickened data. - for (const DexInstructionPcPair& pair : code_item_.Instructions()) { + for (const DexInstructionPcPair& pair : code_item_accessor_) { Instruction* inst = const_cast<Instruction*>(&pair.Inst()); switch (inst->Opcode()) { @@ -192,13 +196,14 @@ bool DexDecompiler::Decompile() { return true; } -bool ArtDecompileDEX(const DexFile::CodeItem& code_item, +bool ArtDecompileDEX(const DexFile& dex_file, + const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info, bool decompile_return_instruction) { if (quickened_info.size() == 0 && !decompile_return_instruction) { return true; } - DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction); + DexDecompiler decompiler(dex_file, code_item, quickened_info, decompile_return_instruction); return decompiler.Decompile(); } diff --git a/runtime/dex_to_dex_decompiler.h b/runtime/dex_to_dex_decompiler.h index d7cb1641e1..93711d17db 100644 --- a/runtime/dex_to_dex_decompiler.h +++ b/runtime/dex_to_dex_decompiler.h @@ -18,7 +18,7 @@ #define ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_ #include "base/array_ref.h" -#include "dex_file.h" +#include "dex/dex_file.h" namespace art { namespace optimizer { @@ -29,7 +29,8 @@ namespace optimizer { // to non-const has too many repercussions on the code base. We make it // consistent with DexToDexCompiler, but we should really change it to // DexFile::CodeItem*. -bool ArtDecompileDEX(const DexFile::CodeItem& code_item, +bool ArtDecompileDEX(const DexFile& dex_file, + const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_data, bool decompile_return_instruction); diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 3c8243a6c5..d93d76793f 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -218,10 +218,11 @@ void DexoptTest::ReserveImageSpace() { std::unique_ptr<BacktraceMap> 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 afe4eeb059..d057ff3b1a 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 418d937b12..0cac8e8d02 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -19,11 +19,11 @@ #include <sys/cdefs.h> +#include <android-base/logging.h> + // 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 8253739427..3048f45f30 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -24,7 +24,7 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "common_throws.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "entrypoints/quick/callee_save_frame.h" #include "handle_scope-inl.h" #include "imt_conflict_table.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<mirror::Class> 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<mirror::DexCache> h_dex_cache(hs.NewHandle(method->GetDexCache())); Handle<mirror::ClassLoader> 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<mirror::Class> 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<mirror::Class> 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<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> 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<mirror::Class> 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<mirror::Class> 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<mirror::String> ResolveString(ClassLinker* class_linker, + dex::StringIndex string_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::String> string = referrer->GetDexCache()->GetResolvedString(string_idx); if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle<mirror::DexCache> 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<mirror::String> ResolveStringFromCode(ArtMethod* referrer, + dex::StringIndex string_idx) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::String> string = referrer->GetDexCache()->GetResolvedString(string_idx); if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle<mirror::DexCache> 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 f3450da306..ffa138d5b1 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -21,7 +21,7 @@ #include "base/enums.h" #include "base/mutex.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index cda70ea265..eb32153b16 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -23,8 +23,8 @@ #include "base/callee_save_type.h" #include "base/macros.h" #include "base/mutex.h" -#include "dex_file_types.h" -#include "dex_instruction.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction.h" #include "gc/allocator_type.h" #include "handle.h" #include "jvalue.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<mirror::Class> 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<mirror::String> 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 7ec360a93c..780e221129 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include <android-base/logging.h> + #include "art_method-inl.h" -#include "base/logging.h" #include "entrypoints/entrypoint_utils.h" #include "java_vm_ext.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index b8d96af3fb..1ab67ec2b9 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -19,7 +19,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "callee_save_frame.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8acaa90053..8c90800463 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 5f40711753..c782c9c949 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 f756312983..cfb427f1ac 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -19,8 +19,8 @@ #include "callee_save_frame.h" #include "class_linker-inl.h" #include "class_table-inl.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" #include "mirror/class-inl.h" @@ -41,6 +41,12 @@ static void StoreObjectInBss(ArtMethod* outer_method, static_assert(sizeof(GcRoot<mirror::String>) == sizeof(GcRoot<mirror::Object>), "Size check."); DCHECK_NE(bss_offset, IndexBssMappingLookup::npos); DCHECK_ALIGNED(bss_offset, sizeof(GcRoot<mirror::Object>)); + 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<mirror::Object>* slot = reinterpret_cast<GcRoot<mirror::Object>*>( const_cast<uint8_t*>(oat_file->BssBegin() + bss_offset)); DCHECK_GE(slot, oat_file->GetBssGcRoots().data()); @@ -132,15 +138,15 @@ 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, - /* can_run_clinit */ true, - /* verify_access */ false); + ObjPtr<mirror::Class> 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) @@ -150,15 +156,15 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), - caller, - self, - /* can_run_clinit */ false, - /* verify_access */ false); + ObjPtr<mirror::Class> 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) @@ -168,13 +174,13 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), - caller, - self, - /* can_run_clinit */ false, - /* verify_access */ true); + ObjPtr<mirror::Class> 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; + return result.Ptr(); } extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) @@ -183,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)); + ObjPtr<mirror::String> 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_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index b13103df03..62cc9dee27 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -20,7 +20,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "callee_save_frame.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc_root-inl.h" #include "mirror/class-inl.h" @@ -68,6 +68,11 @@ static ArtMethod* GetReferrer(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_ return GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveRefsOnly).caller; } +// Macro used to define this set of functions: +// +// art{Get,Set}<Kind>{Static,Instance}FromCode +// art{Get,Set}<Kind>{Static,Instance}FromCompiledCode +// #define ART_GET_FIELD_FROM_CODE(Kind, PrimitiveType, RetType, SetType, \ PrimitiveOrObject, IsObject, Ptr) \ extern "C" RetType artGet ## Kind ## StaticFromCode(uint32_t field_idx, \ @@ -216,15 +221,100 @@ static ArtMethod* GetReferrer(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_ field_idx, obj, new_value, GetReferrer(self), self); \ } +// Define these functions: +// +// artGetByteStaticFromCode +// artGetByteInstanceFromCode +// artSetByteStaticFromCode +// artSetByteInstanceFromCode +// artGetByteStaticFromCompiledCode +// artGetByteInstanceFromCompiledCode +// artSetByteStaticFromCompiledCode +// artSetByteInstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(Byte, int8_t, ssize_t, uint32_t, Primitive, false, ) + +// Define these functions: +// +// artGetBooleanStaticFromCode +// artGetBooleanInstanceFromCode +// artSetBooleanStaticFromCode +// artSetBooleanInstanceFromCode +// artGetBooleanStaticFromCompiledCode +// artGetBooleanInstanceFromCompiledCode +// artSetBooleanStaticFromCompiledCode +// artSetBooleanInstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(Boolean, int8_t, size_t, uint32_t, Primitive, false, ) + +// Define these functions: +// +// artGetShortStaticFromCode +// artGetShortInstanceFromCode +// artSetShortStaticFromCode +// artSetShortInstanceFromCode +// artGetShortStaticFromCompiledCode +// artGetShortInstanceFromCompiledCode +// artSetShortStaticFromCompiledCode +// artSetShortInstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(Short, int16_t, ssize_t, uint16_t, Primitive, false, ) + +// Define these functions: +// +// artGetCharStaticFromCode +// artGetCharInstanceFromCode +// artSetCharStaticFromCode +// artSetCharInstanceFromCode +// artGetCharStaticFromCompiledCode +// artGetCharInstanceFromCompiledCode +// artSetCharStaticFromCompiledCode +// artSetCharInstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(Char, int16_t, size_t, uint16_t, Primitive, false, ) + +// Define these functions: +// +// artGet32StaticFromCode +// artGet32InstanceFromCode +// artSet32StaticFromCode +// artSet32InstanceFromCode +// artGet32StaticFromCompiledCode +// artGet32InstanceFromCompiledCode +// artSet32StaticFromCompiledCode +// artSet32InstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(32, int32_t, size_t, uint32_t, Primitive, false, ) + +// Define these functions: +// +// artGet64StaticFromCode +// artGet64InstanceFromCode +// artSet64StaticFromCode +// artSet64InstanceFromCode +// artGet64StaticFromCompiledCode +// artGet64InstanceFromCompiledCode +// artSet64StaticFromCompiledCode +// artSet64InstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(64, int64_t, uint64_t, uint64_t, Primitive, false, ) + +// Define these functions: +// +// artGetObjStaticFromCode +// artGetObjInstanceFromCode +// artSetObjStaticFromCode +// artSetObjInstanceFromCode +// artGetObjStaticFromCompiledCode +// artGetObjInstanceFromCompiledCode +// artSetObjStaticFromCompiledCode +// artSetObjInstanceFromCompiledCode +// ART_GET_FIELD_FROM_CODE(Obj, mirror::HeapReference<mirror::Object>, mirror::Object*, mirror::Object*, Object, true, .Ptr()) +#undef ART_GET_FIELD_FROM_CODE + // To cut on the number of entrypoints, we have shared entries for // byte/boolean and char/short for setting an instance or static field. We just diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc index d4bc1c76b1..d22f180c7a 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<const Instruction::ArrayDataPayload*>(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 29a62c86ee..3c41a8c3b5 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 <android-base/logging.h> + #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" @@ -50,8 +51,8 @@ extern void ReadBarrierJni(mirror::CompressedReference<mirror::Object>* handle_o extern uint32_t JniMethodFastStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); DCHECK(env != nullptr); - uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); if (kIsDebugBuild) { ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); @@ -65,8 +66,8 @@ 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<uint32_t>(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast<uint32_t>(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? @@ -114,11 +115,11 @@ ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) { 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<IRTSegmentState>(saved_local_ref_cookie); + env->SetLocalSegmentState(env->GetLocalRefCookie()); + env->SetLocalRefCookie(bit_cast<IRTSegmentState>(saved_local_ref_cookie)); self->PopHandleScope(); } @@ -155,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<mirror::Object> h_obj(hs.NewHandleWrapper(&o)); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a76cddf5e..f727690c11 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -20,9 +20,9 @@ #include "callee_save_frame.h" #include "common_throws.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" @@ -784,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; @@ -795,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(); @@ -823,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. @@ -1121,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) { @@ -1250,17 +1249,8 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(caller->GetDeclaringClass()->GetDexCache())); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader())); - // TODO Maybe put this into a mirror::Class function. ObjPtr<mirror::Class> 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 { @@ -2459,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) @@ -2576,13 +2564,11 @@ extern "C" uintptr_t artInvokePolymorphic( // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC. ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - const DexFile::CodeItem* code = caller_method->GetCodeItem(); - const Instruction& inst = code->InstructionAt(dex_pc); + const Instruction& inst = caller_method->DexInstructions().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); diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 6a4e5b5f01..49c2a15e86 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -21,9 +21,10 @@ #include <sys/ucontext.h> #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" +#include "dex/dex_file_types.h" #include "mirror/class.h" #include "mirror/object_reference.h" #include "oat_quick_method_header.h" diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 3d0e8172b6..72eb8274c8 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -23,8 +23,9 @@ #include <memory> #include <string> +#include <android-base/logging.h> + #include "atomic.h" -#include "base/logging.h" #include "base/macros.h" #include "mem_map.h" #include "stack_reference.h" @@ -108,7 +109,7 @@ class AtomicStack { // Stack overflow. return false; } - } while (!back_index_.CompareExchangeWeakRelaxed(index, new_index)); + } while (!back_index_.CompareAndSetWeakRelaxed(index, new_index)); *start_address = begin_ + index; *end_address = begin_ + new_index; if (kIsDebugBuild) { @@ -240,7 +241,7 @@ class AtomicStack { // Stack overflow. return false; } - } while (!back_index_.CompareExchangeWeakRelaxed(index, index + 1)); + } while (!back_index_.CompareAndSetWeakRelaxed(index, index + 1)); begin_[index].Assign(value); return true; } diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h index cd3923abbe..bf153f56d8 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -21,9 +21,10 @@ #include <memory> +#include <android-base/logging.h> + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { @@ -42,8 +43,7 @@ inline bool Bitmap::AtomicTestAndSetBit(uintptr_t bit_index) { DCHECK(TestBit(bit_index)); return true; } - } while (!atomic_entry->CompareExchangeWeakSequentiallyConsistent(old_word, - old_word | word_mask)); + } while (!atomic_entry->CompareAndSetWeakSequentiallyConsistent(old_word, old_word | word_mask)); DCHECK(TestBit(bit_index)); return false; } diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 6ff53597e4..adca5c835e 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 <android-base/logging.h> + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" -#include "card_table.h" #include "mem_map.h" #include "space_bitmap.h" @@ -31,7 +33,7 @@ namespace accounting { static inline bool byte_cas(uint8_t old_value, uint8_t new_value, uint8_t* address) { #if defined(__i386__) || defined(__x86_64__) Atomic<uint8_t>* byte_atomic = reinterpret_cast<Atomic<uint8_t>*>(address); - return byte_atomic->CompareExchangeWeakRelaxed(old_value, new_value); + return byte_atomic->CompareAndSetWeakRelaxed(old_value, new_value); #else // Little endian means most significant byte is on the left. const size_t shift_in_bytes = reinterpret_cast<uintptr_t>(address) % sizeof(uintptr_t); @@ -45,7 +47,7 @@ static inline bool byte_cas(uint8_t old_value, uint8_t new_value, uint8_t* addre ~(static_cast<uintptr_t>(0xFF) << shift_in_bits); const uintptr_t old_word = cur_word | (static_cast<uintptr_t>(old_value) << shift_in_bits); const uintptr_t new_word = cur_word | (static_cast<uintptr_t>(new_value) << shift_in_bits); - return word_atomic->CompareExchangeWeakRelaxed(old_word, new_word); + return word_atomic->CompareAndSetWeakRelaxed(old_word, new_word); #endif } @@ -193,7 +195,7 @@ inline void CardTable::ModifyCardsAtomic(uint8_t* scan_begin, new_bytes[i] = visitor(expected_bytes[i]); } Atomic<uintptr_t>* atomic_word = reinterpret_cast<Atomic<uintptr_t>*>(word_cur); - if (LIKELY(atomic_word->CompareExchangeWeakRelaxed(expected_word, new_word))) { + if (LIKELY(atomic_word->CompareAndSetWeakRelaxed(expected_word, new_word))) { for (size_t i = 0; i < sizeof(uintptr_t); ++i) { const uint8_t expected_byte = expected_bytes[i]; const uint8_t new_byte = new_bytes[i]; diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 01b5896650..934e57a61b 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -18,7 +18,6 @@ #include <sys/mman.h> -#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 4237e7ee3f..c997f8dbfc 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 <android-base/logging.h> + #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 1b3d0dadae..0dd05cd6f0 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -18,6 +18,7 @@ #include <memory> +#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 b37dd965fc..df9ee8c219 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -21,9 +21,10 @@ #include <memory> +#include <android-base/logging.h> + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { @@ -46,7 +47,7 @@ inline bool SpaceBitmap<kAlignment>::AtomicTestAndSet(const mirror::Object* obj) DCHECK(Test(obj)); return true; } - } while (!atomic_entry->CompareExchangeWeakRelaxed(old_word, old_word | mask)); + } while (!atomic_entry->CompareAndSetWeakRelaxed(old_word, old_word | mask)); DCHECK(Test(obj)); return false; } diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 280c0b1d69..237ee80ba0 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -19,7 +19,7 @@ #include "android-base/stringprintf.h" #include "art_field-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 2257b81e09..2ee4239e8a 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 ef916f8745..65062208d6 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -16,8 +16,9 @@ #include "dlmalloc.h" +#include <android-base/logging.h> + #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/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index b742ac4a7c..928abe873e 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 2c90773b8f..6e5cf0ede8 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -26,9 +26,10 @@ #include <unordered_set> #include <vector> +#include <android-base/logging.h> + #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 7beff960cc..cf837161e0 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -88,6 +88,7 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, from_space_num_bytes_at_first_pause_(0), mark_stack_mode_(kMarkStackModeOff), weak_ref_access_enabled_(true), + max_peak_num_non_free_regions_(0), skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock), measure_read_barrier_slow_path_(measure_read_barrier_slow_path), mark_from_read_barrier_measurements_(false), @@ -760,7 +761,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); @@ -1753,6 +1755,8 @@ void ConcurrentCopying::ReclaimPhase() { cumulative_bytes_moved_.FetchAndAddRelaxed(to_bytes); uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent(); cumulative_objects_moved_.FetchAndAddRelaxed(to_objects); + max_peak_num_non_free_regions_ = std::max(max_peak_num_non_free_regions_, + region_space_->GetNumNonFreeRegions()); if (kEnableFromSpaceAccountingCheck) { CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects); CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes); @@ -2078,7 +2082,7 @@ inline void ConcurrentCopying::VisitRoots( // It was updated by the mutator. break; } - } while (!addr->CompareExchangeWeakRelaxed(expected_ref, new_ref)); + } while (!addr->CompareAndSetWeakRelaxed(expected_ref, new_ref)); } } @@ -2097,7 +2101,7 @@ inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Obje // It was updated by the mutator. break; } - } while (!addr->CompareExchangeWeakRelaxed(expected_ref, new_ref)); + } while (!addr->CompareAndSetWeakRelaxed(expected_ref, new_ref)); } } @@ -2690,6 +2694,13 @@ void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { } os << "Cumulative bytes moved " << cumulative_bytes_moved_.LoadRelaxed() << "\n"; os << "Cumulative objects moved " << cumulative_objects_moved_.LoadRelaxed() << "\n"; + + os << "Peak regions allocated " + << max_peak_num_non_free_regions_ << " (" + << PrettySize(max_peak_num_non_free_regions_ * space::RegionSpace::kRegionSize) + << ") / " << region_space_->GetNumRegions() << " (" + << PrettySize(region_space_->GetNumRegions() * space::RegionSpace::kRegionSize) + << ")\n"; } } // namespace collector diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 8b4b58e7b1..939e7fc8a4 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -308,6 +308,11 @@ class ConcurrentCopying : public GarbageCollector { Atomic<uint64_t> cumulative_bytes_moved_; Atomic<uint64_t> cumulative_objects_moved_; + // Maintain the maximum of number of non-free regions collected just before + // reclaim in each GC cycle. At this moment in cycle, highest number of + // regions are in non-free. + size_t max_peak_num_non_free_regions_; + // The skipped blocks are memory blocks/chucks that were copies of // objects that were unused due to lost races (cas failures) at // object copy/forward pointer install. They are reused. diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index c5a341fc80..fa34270d95 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 1024050409..3b5961899f 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -19,6 +19,7 @@ #include <tuple> #include <vector> +#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 aef98dee58..34cc129ce8 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 <android-base/logging.h> + +#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 c6caf4b08e..fdfe949265 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 9fb37b6138..3150781a5a 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -22,7 +22,7 @@ #include <sstream> #include <vector> -#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 871208037a..d88fcdcc95 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 <android-base/logging.h> + +#include "base/macros.h" #include "globals.h" #include <ostream> diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 2047646413..52dd104ac8 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 9f6266612a..9edba96ddd 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" @@ -37,7 +38,7 @@ #include "common_throws.h" #include "cutils/sched_policy.h" #include "debugger.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.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) { @@ -1127,7 +1127,7 @@ static inline AllocationListener* GetAndOverwriteAllocationListener( AllocationListener* old; do { old = storage->LoadSequentiallyConsistent(); - } while (!storage->CompareExchangeStrongSequentiallyConsistent(old, new_value)); + } while (!storage->CompareAndSetStrongSequentiallyConsistent(old, new_value)); return old; } @@ -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<mirror::Class> h_class, + bool use_is_assignable_from) REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* instance_class = obj->GetClass(); + CHECK(instance_class != nullptr); + ObjPtr<mirror::Class> 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<Handle<mirror::Class>>& 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<mirror::Class> 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<Handle<mirror::Class>>& classes, void Heap::GetInstances(VariableSizedHandleScope& scope, Handle<mirror::Class> h_class, + bool use_is_assignable_from, int32_t max_count, std::vector<Handle<mirror::Object>>& 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<size_t>(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 { @@ -3594,7 +3601,7 @@ void Heap::ClearConcurrentGCRequest() { void Heap::RequestConcurrentGC(Thread* self, GcCause cause, bool force_full) { if (CanAddHeapTask(self) && - concurrent_gc_pending_.CompareExchangeStrongSequentiallyConsistent(false, true)) { + concurrent_gc_pending_.CompareAndSetStrongSequentiallyConsistent(false, true)) { task_processor_->AddTask(self, new ConcurrentGCTask(NanoTime(), // Start straight away. cause, force_full)); @@ -3839,7 +3846,7 @@ void Heap::RegisterNativeFree(JNIEnv*, size_t bytes) { do { allocated = new_native_bytes_allocated_.LoadRelaxed(); new_freed_bytes = std::min(allocated, bytes); - } while (!new_native_bytes_allocated_.CompareExchangeWeakRelaxed(allocated, + } while (!new_native_bytes_allocated_.CompareAndSetWeakRelaxed(allocated, allocated - new_freed_bytes)); if (new_freed_bytes < bytes) { old_native_bytes_allocated_.FetchAndSubRelaxed(bytes - new_freed_bytes); @@ -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 4d7424c7ef..7dcf82f415 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -22,11 +22,14 @@ #include <unordered_set> #include <vector> +#include <android-base/logging.h> + #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<mirror::Class> c, + bool use_is_assignable_from, int32_t max_count, std::vector<Handle<mirror::Object>>& 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<space::ContinuousSpace*> continuous_spaces_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index d58d09c794..c59642fe4e 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/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h index 1509bb027d..9ebb131ad1 100644 --- a/runtime/gc/space/bump_pointer_space-inl.h +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -74,7 +74,7 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t if (UNLIKELY(new_end > growth_end_)) { return nullptr; } - } while (!end_.CompareExchangeWeakSequentiallyConsistent(old_end, new_end)); + } while (!end_.CompareAndSetWeakSequentiallyConsistent(old_end, new_end)); return reinterpret_cast<mirror::Object*>(old_end); } diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 576a35c52d..a3eef90e3a 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 74813b4dd1..251d94ca25 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -36,7 +36,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/accounting/space_bitmap-inl.h" #include "image-inl.h" @@ -1587,7 +1587,9 @@ std::unique_ptr<ImageSpace> 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 a0ecb95ac7..6ce81e9209 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/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 45f4f82448..d2efb102e9 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -20,7 +20,9 @@ #include <memory> -#include "base/logging.h" +#include <android-base/logging.h> + +#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 dcb783782f..17274b508d 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/region_space-inl.h b/runtime/gc/space/region_space-inl.h index a3b53b4cad..ea2168fe9c 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -101,7 +101,7 @@ inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* byte if (UNLIKELY(new_top > end_)) { return nullptr; } - } while (!top_.CompareExchangeWeakRelaxed(old_top, new_top)); + } while (!top_.CompareAndSetWeakRelaxed(old_top, new_top)); objects_allocated_.FetchAndAddRelaxed(1); DCHECK_LE(Top(), end_); DCHECK_LT(old_top, end_); diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 77d76fb93b..c9c9136c27 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -138,6 +138,13 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { uint64_t GetObjectsAllocatedInUnevacFromSpace() REQUIRES(!region_lock_) { return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>(); } + // It is OK to do a racy read here as it's only for performance dump. + size_t GetNumNonFreeRegions() const { + return num_non_free_regions_; + } + size_t GetNumRegions() const { + return num_regions_; + } bool CanMoveObjects() const OVERRIDE { return true; diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 5d1f191e4d..3a685cb82d 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 74ce273abf..2c6afa7eb8 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 <android-base/logging.h> + +#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/verification.cc b/runtime/gc/verification.cc index 3cd04a61e9..d99b37762f 100644 --- a/runtime/gc/verification.cc +++ b/runtime/gc/verification.cc @@ -86,7 +86,7 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> 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/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index e1582120dc..46630dbeef 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -54,8 +54,6 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET), DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object::ClassOffset().Int32Value()))) #define MIRROR_OBJECT_LOCK_WORD_OFFSET 4 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_LOCK_WORD_OFFSET), (static_cast<int32_t>(art::mirror::Object::MonitorOffset().Int32Value()))) -#define MIRROR_CLASS_STATUS_INITIALIZED 0xb -DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized)))) #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable)))) #define ACCESS_FLAGS_CLASS_IS_INTERFACE 0x200 diff --git a/runtime/handle.h b/runtime/handle.h index ccff575495..18e503d1de 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_HANDLE_H_ #define ART_RUNTIME_HANDLE_H_ +#include <android-base/logging.h> + #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 f248a118e9..28a230291d 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -19,8 +19,9 @@ #include <stack> +#include <android-base/logging.h> + #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 6a1a8c7271..ea8a68405f 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -36,17 +36,19 @@ #include <set> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "common_throws.h" #include "debugger.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/heap_bitmap.h" #include "gc/allocation_record.h" #include "gc/heap-visit-objects-inl.h" diff --git a/runtime/image.cc b/runtime/image.cc index 8f35d8474c..b9d955c08c 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', '5', '1', '\0' }; // @FastNative access flags. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '2', '\0' }; // 4-bit ClassStatus. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index 3844186a9b..159a308fb3 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/imtable-inl.h b/runtime/imtable-inl.h index cb85fa6e56..6237cca9e4 100644 --- a/runtime/imtable-inl.h +++ b/runtime/imtable-inl.h @@ -20,7 +20,7 @@ #include "imtable.h" #include "art_method-inl.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "utf.h" namespace art { diff --git a/runtime/indenter.h b/runtime/indenter.h index 69b973252d..6361dd2092 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -20,7 +20,8 @@ #include <ostream> #include <streambuf> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/macros.h" namespace art { diff --git a/runtime/index_bss_mapping.h b/runtime/index_bss_mapping.h index d9f4e663a7..dcbc05c195 100644 --- a/runtime/index_bss_mapping.h +++ b/runtime/index_bss_mapping.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_INDEX_BSS_MAPPING_H_ #define ART_RUNTIME_INDEX_BSS_MAPPING_H_ +#include <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" namespace art { diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 2c8ec47492..51878312d9 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<jobject>(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 6675099523..00184e2ed0 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -23,8 +23,10 @@ #include <limits> #include <string> +#include <android-base/logging.h> + #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 49f202182d..4524448916 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -25,9 +25,9 @@ #include "base/callee_save_type.h" #include "class_linker.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction-inl.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -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/instrumentation_test.cc b/runtime/instrumentation_test.cc index 89baa3504f..836bbe711f 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -21,7 +21,7 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "common_throws.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" #include "jni_internal.h" diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 01b7d4e457..54db87297d 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -20,7 +20,7 @@ #include "common_dex_operations.h" #include "common_throws.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "interpreter_common.h" #include "interpreter_mterp_impl.h" #include "interpreter_switch_impl.h" @@ -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<false, true>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register, false); } else if (UNLIKELY(!Runtime::Current()->IsStarted())) { - return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register, false); } else { while (true) { // Mterp does not support all instrumentation/debugging. if (MterpShouldSwitchInterpreters() != 0) { - return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<false, false>(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<false, false>(self, code_item, shadow_frame, + result_register = ExecuteSwitchImpl<false, false>(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<false, true>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<false, false>(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<true, true>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<true, false>(self, accessor, shadow_frame, result_register, false); } } else { DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind); if (transaction_active) { - return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl<true, false>(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 df8568edcd..0d43b9090a 100644 --- a/runtime/interpreter/interpreter.h +++ b/runtime/interpreter/interpreter.h @@ -18,7 +18,7 @@ #define ART_RUNTIME_INTERPRETER_INTERPRETER_H_ #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "obj_ptr.h" namespace art { @@ -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 d2d017e118..475f93803d 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -20,7 +20,7 @@ #include "base/enums.h" #include "debugger.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "intrinsics_enum.h" #include "jit/jit.h" @@ -1062,7 +1062,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, // The second parameter is the name to lookup. { dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i)); - ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache); + ObjPtr<mirror::String> name = class_linker->ResolveString(name_idx, dex_cache); if (name.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1073,12 +1073,8 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, // The third parameter is the method type associated with the name. uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i); - Handle<mirror::MethodType> - method_type(hs.NewHandle(class_linker->ResolveMethodType(self, - *dex_file, - method_type_idx, - dex_cache, - class_loader))); + Handle<mirror::MethodType> method_type(hs.NewHandle( + class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader))); if (method_type.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1113,7 +1109,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, case EncodedArrayValueIterator::ValueType::kMethodType: { uint32_t idx = static_cast<uint32_t>(jvalue.i); ObjPtr<mirror::MethodType> 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; @@ -1136,7 +1132,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kString: { dex::StringIndex idx(static_cast<uint32_t>(jvalue.i)); - ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache); + ObjPtr<mirror::String> ref = class_linker->ResolveString(idx, dex_cache); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1147,8 +1143,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kType: { dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i)); - ObjPtr<mirror::Class> ref = - class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader); + ObjPtr<mirror::Class> ref = class_linker->ResolveType(idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1325,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 @@ -1339,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. @@ -1347,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()); @@ -1372,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 @@ -1504,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 f097bc71b9..8180222e22 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -26,18 +26,19 @@ #include <iostream> #include <sstream> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include "art_field-inl.h" #include "art_method-inl.h" #include "base/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" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "handle_scope-inl.h" #include "jit/jit.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<mirror::MethodHandle> 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<mirror::MethodType> 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<mirror::String> ResolveString(Thread* self, if (UNLIKELY(string_ptr == nullptr)) { StackHandleScope<1> hs(self); Handle<mirror::DexCache> 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_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 37593bc728..99a4f763c9 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -16,7 +16,7 @@ #include "interpreter/interpreter_intrinsics.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "intrinsics_enum.h" #include "interpreter/interpreter_common.h" diff --git a/runtime/interpreter/interpreter_mterp_impl.h b/runtime/interpreter/interpreter_mterp_impl.h index 7aa5a34bd4..d8a764f855 100644 --- a/runtime/interpreter/interpreter_mterp_impl.h +++ b/runtime/interpreter/interpreter_mterp_impl.h @@ -19,7 +19,7 @@ #include "base/macros.h" #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "jvalue.h" #include "obj_ptr.h" diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 094f08664e..6f9cad848d 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -17,7 +17,7 @@ #include "interpreter_switch_impl.h" #include "base/enums.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" @@ -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<bool do_access_check, bool transaction_active> -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<true, false>(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl<true, false>(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template HOT_ATTR -JValue ExecuteSwitchImpl<false, false>(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl<false, false>(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl<true, true>(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl<true, true>(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl<false, true>(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl<false, true>(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 267df2e219..50db337f03 100644 --- a/runtime/interpreter/interpreter_switch_impl.h +++ b/runtime/interpreter/interpreter_switch_impl.h @@ -19,12 +19,13 @@ #include "base/macros.h" #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "jvalue.h" #include "obj_ptr.h" namespace art { +class CodeItemDataAccessor; class ShadowFrame; class Thread; @@ -32,7 +33,7 @@ namespace interpreter { template<bool do_access_check, bool transaction_active> 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/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index 3908cb506e..41b5d5650d 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 diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S index 6e1ba1c882..1c784ef188 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 1ccaa6443f..0f7a6f1116 100644 --- a/runtime/interpreter/mterp/mips/header.S +++ b/runtime/interpreter/mterp/mips/header.S @@ -32,7 +32,7 @@ */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.S" +#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/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 92dd19ed2f..9c7645af9e 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<mirror::Class> 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<mirror::Class> 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<true>(self, allocator_type); } else { - obj = AllocObjectFromCode<true>(c, + obj = AllocObjectFromCode<true>(c.Ptr(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); } diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 9535e254e7..1687afa58a 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -39,7 +39,7 @@ */ #include "asm_support.h" -#include "interpreter/mterp/cfi_asm_support.S" +#include "interpreter/mterp/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ @@ -766,6 +766,7 @@ */ ExecuteMterpImpl: + .cfi_startproc .set noreorder .cpload t9 .set reorder @@ -12844,5 +12845,6 @@ MterpProfileActive: STACK_LOAD_FULL() jalr zero, ra + .cfi_endproc .end ExecuteMterpImpl diff --git a/runtime/interpreter/shadow_frame.cc b/runtime/interpreter/shadow_frame.cc index ab154cf767..fe7e3e0a9b 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/shadow_frame.h b/runtime/interpreter/shadow_frame.h index be2c943427..d5451ffded 100644 --- a/runtime/interpreter/shadow_frame.h +++ b/runtime/interpreter/shadow_frame.h @@ -23,7 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "lock_count_data.h" #include "read_barrier.h" #include "stack_reference.h" diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 31e7986770..d1436fa9cf 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -26,12 +26,12 @@ #include <locale> #include <unordered_map> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #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 bc9ead8360..b38c685d9b 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -19,12 +19,13 @@ #include "interpreter.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "jvalue.h" 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/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 9db5f88dab..0986005e4b 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -24,7 +24,7 @@ #include "base/memory_tool.h" #include "class_linker.h" #include "common_runtime_test.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "handle.h" #include "handle_scope-inl.h" #include "interpreter/interpreter_common.h" diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index f8b82ed313..da4c4b2fa4 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -26,7 +26,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "check_jni.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "fault_handler.h" #include "gc/allocation_record.h" #include "gc/heap.h" @@ -48,6 +48,7 @@ #include "thread-inl.h" #include "thread_list.h" #include "ti/agent.h" +#include "well_known_classes.h" namespace art { @@ -337,7 +338,7 @@ class Libraries { } else { VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling..."; JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym); - jni_on_unload(self->GetJniEnv()->vm, nullptr); + jni_on_unload(self->GetJniEnv()->GetVm(), nullptr); } delete library; } @@ -853,7 +854,6 @@ void JavaVMExt::UnloadNativeLibraries() { bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, - jstring library_path, std::string* error_msg) { error_msg->clear(); @@ -950,6 +950,9 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, // class unloading. Libraries will only be unloaded when the reference count (incremented by // dlopen) becomes zero from dlclose. + // Retrieve the library path from the classloader, if necessary. + ScopedLocalRef<jstring> library_path(env, GetLibrarySearchPath(env, class_loader)); + Locks::mutator_lock_->AssertNotHeld(self); const char* path_str = path.empty() ? nullptr : path.c_str(); bool needs_native_bridge = false; @@ -957,7 +960,7 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, runtime_->GetTargetSdkVersion(), path_str, class_loader, - library_path, + library_path.get(), &needs_native_bridge, error_msg); @@ -1052,17 +1055,17 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, static void* FindCodeForNativeMethodInAgents(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { std::string jni_short_name(m->JniShortName()); std::string jni_long_name(m->JniLongName()); - for (const ti::Agent& agent : Runtime::Current()->GetAgents()) { - void* fn = agent.FindSymbol(jni_short_name); + for (const std::unique_ptr<ti::Agent>& agent : Runtime::Current()->GetAgents()) { + void* fn = agent->FindSymbol(jni_short_name); if (fn != nullptr) { VLOG(jni) << "Found implementation for " << m->PrettyMethod() - << " (symbol: " << jni_short_name << ") in " << agent; + << " (symbol: " << jni_short_name << ") in " << *agent; return fn; } - fn = agent.FindSymbol(jni_long_name); + fn = agent->FindSymbol(jni_long_name); if (fn != nullptr) { VLOG(jni) << "Found implementation for " << m->PrettyMethod() - << " (symbol: " << jni_long_name << ") in " << agent; + << " (symbol: " << jni_long_name << ") in " << *agent; return fn; } } @@ -1119,6 +1122,18 @@ void JavaVMExt::VisitRoots(RootVisitor* visitor) { // The weak_globals table is visited by the GC itself (because it mutates the table). } +jstring JavaVMExt::GetLibrarySearchPath(JNIEnv* env, jobject class_loader) { + if (class_loader == nullptr) { + return nullptr; + } + if (!env->IsInstanceOf(class_loader, WellKnownClasses::dalvik_system_BaseDexClassLoader)) { + return nullptr; + } + return reinterpret_cast<jstring>(env->CallObjectMethod( + class_loader, + WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath)); +} + // JNI Invocation interface. extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h index 7c2755fc58..8c81c2565d 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/java_vm_ext.h @@ -101,7 +101,6 @@ class JavaVMExt : public JavaVM { bool LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, - jstring library_path, std::string* error_msg); // Unload native libraries with cleared class loaders. @@ -200,6 +199,11 @@ class JavaVMExt : public JavaVM { static bool IsBadJniVersion(int version); + // Return the library search path for the given classloader, if the classloader is of a + // well-known type. The jobject will be a local reference and is expected to be managed by the + // caller. + static jstring GetLibrarySearchPath(JNIEnv* env, jobject class_loader); + private: // The constructor should not be called directly. It may leave the object in // an erroneous state, and the result needs to be checked. diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index aeeda1e791..b491c3ee5c 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 ede4f9edb7..d68430f3ac 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 41cb64276c..9409b7661f 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 f0b8c918dc..4b4ca0e4a3 100644 --- a/runtime/jdwp/jdwp_expand_buf.cc +++ b/runtime/jdwp/jdwp_expand_buf.cc @@ -23,7 +23,8 @@ #include <stdlib.h> #include <string.h> -#include "base/logging.h" +#include <android-base/logging.h> + #include "jdwp/jdwp.h" #include "jdwp/jdwp_bits.h" diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 618332b7ef..89eef88b88 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 e6c60685cc..63f5dc8b69 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 <port> or <host>:<port> */ + std::string port_string; + jdwp_options->host.clear(); + std::string::size_type colon = value.find(':'); + if (colon != std::string::npos) { + jdwp_options->host = value.substr(0, colon); + port_string = value.substr(colon + 1); + } else { + port_string = value; + } + if (port_string.empty()) { + 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<std::string> 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 0000000000..10c52e8cf8 --- /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 97662f0727..673a942517 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/base/type_static_if.h b/runtime/jdwp_provider.h index a74d79a665..b62e10b4f8 100644 --- a/runtime/base/type_static_if.h +++ b/runtime/jdwp_provider.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,19 +14,23 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_TYPE_STATIC_IF_H_ -#define ART_RUNTIME_BASE_TYPE_STATIC_IF_H_ +#ifndef ART_RUNTIME_JDWP_PROVIDER_H_ +#define ART_RUNTIME_JDWP_PROVIDER_H_ -// A static if which determines whether to return type A or B based on the condition boolean. -template <bool condition, typename A, typename B> -struct TypeStaticIf { - typedef A type; -}; +#include <ios> + +#include "base/macros.h" +#include "base/logging.h" -// Specialization to handle the false case. -template <typename A, typename B> -struct TypeStaticIf<false, A, B> { - typedef B type; +namespace art { + +enum class JdwpProvider { + kNone, + kInternal, + kAdbConnection, }; -#endif // ART_RUNTIME_BASE_TYPE_STATIC_IF_H_ +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 135d9b1f51..4d1c85a1c2 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 <android-base/logging.h> + #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 0d95bc6e64..12bf79d7ca 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; @@ -647,6 +649,10 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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_); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a5c167eee8..659c55a289 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -21,12 +21,13 @@ #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" #include "cha.h" #include "debugger_interface.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" #include "gc/scoped_gc_critical_section.h" diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index bb8e5e5c15..74bf237c31 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -35,13 +35,14 @@ #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" #include "base/systrace.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "jit/profiling_info.h" #include "os.h" #include "safe_map.h" @@ -1582,7 +1583,11 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* 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_compilation_info.h b/runtime/jit/profile_compilation_info.h index 8dbb43fb53..7c30dee0c0 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -25,8 +25,8 @@ #include "base/arena_object.h" #include "bit_memory_region.h" #include "dex_cache_resolved_classes.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "method_reference.h" #include "safe_map.h" #include "type_reference.h" diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index f155d7e163..08042cc890 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -20,7 +20,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "common_runtime_test.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "handle_scope-inl.h" #include "jit/profile_compilation_info.h" #include "linear_alloc.h" diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index acbc6e63a4..8f0ac33594 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -25,13 +25,14 @@ #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" #include "base/time_utils.h" #include "class_table-inl.h" #include "compiler_filter.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "dex_reference_collection.h" #include "gc/collector_type.h" #include "gc/gc_cause.h" diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index e54a0179e1..9126bea7d0 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -17,7 +17,7 @@ #include "profiling_info.h" #include "art_method-inl.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "scoped_thread_state_change-inl.h" @@ -94,8 +94,8 @@ void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { // *after* this thread hits a suspend point. GcRoot<mirror::Class> expected_root(existing); GcRoot<mirror::Class> desired_root(cls); - if (!reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&cache->classes_[i])-> - CompareExchangeStrongSequentiallyConsistent(expected_root, desired_root)) { + auto atomic_root = reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&cache->classes_[i]); + if (!atomic_root->CompareAndSetStrongSequentiallyConsistent(expected_root, desired_root)) { // Some other thread put a class in the cache, continue iteration starting at this // entry in case the entry contains `cls`. --i; diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h index d66df081c6..14f708b18d 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni_env_ext-inl.h @@ -26,7 +26,7 @@ namespace art { template<typename T> inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> 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<mirror::Object> 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 8352657f28..efe43ee0e9 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<jobject>(locals.Add(local_ref_cookie, obj, &error_msg)); + jobject ref = reinterpret_cast<jobject>(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<IndirectRef>(obj)); + locals_.Remove(local_ref_cookie_, reinterpret_cast<IndirectRef>(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<size_t>(capacity)); - stacked_local_ref_cookies.push_back(local_ref_cookie); - local_ref_cookie = locals.GetSegmentState(); + DCHECK_GE(locals_.FreeCapacity(), static_cast<size_t>(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<uintptr_t, jobject> 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::Object> mirror_obj = self->DecodeJObject(obj); + ObjPtr<mirror::Object> mirror_obj = self_->DecodeJObject(obj); for (std::pair<uintptr_t, jobject>& pair : locked_objects_) { - if (self->DecodeJObject(pair.second) == mirror_obj) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("<JNI MonitorExit>", + if (self_->DecodeJObject(pair.second) == mirror_obj) { + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("<JNI MonitorExit>", "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<uintptr_t, jobject>& pair = locked_objects_[locked_objects_.size() - 1]; if (pair.first == current_frame) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("<JNI End>", + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("<JNI End>", "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<uintptr_t, jobject>& 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("<JNI End>", + "Critical lock held when returning to Java on thread %s", + ToStr<Thread>(*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 2f6c5dc92a..0e8fd03057 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<mirror::Object> 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<IRTSegmentState> 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<IRTSegmentState> 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<std::pair<uintptr_t, jobject>> 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 1e55158a34..b8e6ebe8d8 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -28,11 +28,11 @@ #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" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "fault_handler.h" #include "gc/accounting/card_table-inl.h" #include "gc_root.h" @@ -383,7 +383,7 @@ int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobj } static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { - return reinterpret_cast<JNIEnvExt*>(env)->vm; + return reinterpret_cast<JNIEnvExt*>(env)->GetVm(); } #define CHECK_NON_NULL_ARGUMENT(value) \ @@ -545,7 +545,7 @@ class JNI { } static jboolean ExceptionCheck(JNIEnv* env) { - return static_cast<JNIEnvExt*>(env)->self->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; + return static_cast<JNIEnvExt*>(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<JNIEnvExt*>(env)->vm; - Thread* self = down_cast<JNIEnvExt*>(env)->self; + JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->GetVm(); + Thread* self = down_cast<JNIEnvExt*>(env)->self_; vm->DeleteGlobalRef(self, obj); } @@ -635,8 +635,8 @@ class JNI { } static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) { - JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm; - Thread* self = down_cast<JNIEnvExt*>(env)->self; + JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->GetVm(); + Thread* self = down_cast<JNIEnvExt*>(env)->self_; vm->DeleteWeakGlobalRef(self, obj); } @@ -659,7 +659,7 @@ class JNI { // it. b/22119403 ScopedObjectAccess soa(env); auto* ext_env = down_cast<JNIEnvExt*>(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<JNIEnvExt*>(env)->vm->IsCheckJniEnabled(); + bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled(); for (ObjPtr<mirror::Class> current_class = c.Get(); current_class != nullptr; current_class = current_class->GetSuperClass()) { @@ -2399,7 +2399,7 @@ class JNI { ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(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<JNIEnvExt*>(env)->self->IsExceptionPending() ? nullptr : result; + return static_cast<JNIEnvExt*>(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<size_t>(desired_capacity), &error_msg)) { + if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast<size_t>(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 1ecfe7cb76..ad24c94ae9 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<JNIEnvExt*>(env)->locals.Capacity(); + return reinterpret_cast<JNIEnvExt*>(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 31459af3a0..2bfed7f539 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -19,8 +19,10 @@ #include <vector> +#include <android-base/logging.h> + #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 b9aa0b793b..fac1a7597d 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -20,8 +20,9 @@ #include <cstdint> #include <iosfwd> +#include <android-base/logging.h> + #include "base/bit_utils.h" -#include "base/logging.h" #include "read_barrier.h" namespace art { diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 07078ecb13..d1c230fd8f 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -21,7 +21,8 @@ #include <cstring> #include <string> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/macros.h" #include "base/mutex.h" #include "base/bit_utils.h" diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 7f68d2faa0..8abf8a6003 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<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) { - for (BacktraceMap::const_iterator it = iters.first; it != iters.second; ++it) { + std::pair<BacktraceMap::iterator, BacktraceMap::iterator> 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<uint32_t>(it->start), - static_cast<uint32_t>(it->end), - (it->flags & PROT_READ) ? 'r' : '-', - (it->flags & PROT_WRITE) ? 'w' : '-', - (it->flags & PROT_EXEC) ? 'x' : '-', it->name.c_str()); + static_cast<uint32_t>(entry->start), + static_cast<uint32_t>(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<uintptr_t>(it->start), static_cast<uintptr_t>(it->end), - it->name.c_str(), + static_cast<uintptr_t>(entry->start), static_cast<uintptr_t>(entry->end), + entry->name.c_str(), map_info.str().c_str()); return false; } diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc index 13cc5c99bc..862ff73639 100644 --- a/runtime/memory_region.cc +++ b/runtime/memory_region.cc @@ -19,9 +19,6 @@ #include <stdint.h> #include <string.h> -#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 7cf5d49d70..23e0aecbda 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -20,10 +20,11 @@ #include <stdint.h> #include <type_traits> +#include <android-base/logging.h> + #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_handles-inl.h b/runtime/method_handles-inl.h index 08b8ad937a..2bc71f4b5e 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -20,7 +20,7 @@ #include "method_handles.h" #include "common_throws.h" -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" #include "jvalue.h" #include "mirror/class.h" diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 8eb31c1ef8..88f30a8900 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -408,7 +408,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, 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 @@ -419,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 { @@ -496,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, @@ -550,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); @@ -589,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, @@ -1035,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 { @@ -1066,7 +1065,7 @@ static inline bool MethodHandleInvokeExactInternal( 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, diff --git a/runtime/method_handles.h b/runtime/method_handles.h index bc74bf23d2..6ffd1a81fc 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -19,7 +19,7 @@ #include <ostream> -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "handle.h" #include "interpreter/shadow_frame.h" #include "jvalue.h" diff --git a/runtime/method_info.h b/runtime/method_info.h index 5a72125be4..6485af992d 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 <android-base/logging.h> + +#include "base/macros.h" #include "leb128.h" #include "memory_region.h" diff --git a/runtime/method_reference.h b/runtime/method_reference.h index 31f3b8e84e..50b6d6eb68 100644 --- a/runtime/method_reference.h +++ b/runtime/method_reference.h @@ -19,8 +19,8 @@ #include <stdint.h> #include <string> -#include "dex_file.h" -#include "dex_file_reference.h" +#include "dex/dex_file.h" +#include "dex/dex_file_reference.h" namespace art { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 22812454d1..636c84c759 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 <android-base/logging.h> +#include <android-base/stringprintf.h> #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/array.cc b/runtime/mirror/array.cc index 6218dd9c9c..25283bc310 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -20,7 +20,7 @@ #include "class.h" #include "class_linker-inl.h" #include "common_throws.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" #include "object-inl.h" diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index eb54f7fb1f..302a5e622e 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -27,7 +27,7 @@ #include "class_loader.h" #include "common_throws.h" #include "dex_cache.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/heap-inl.h" #include "iftable.h" #include "invoke_type.h" @@ -440,7 +440,6 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> 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<Class> 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<Class> access_to, // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); diff --git a/runtime/mirror/class-refvisitor-inl.h b/runtime/mirror/class-refvisitor-inl.h index 3d52ead0f4..263b7746db 100644 --- a/runtime/mirror/class-refvisitor-inl.h +++ b/runtime/mirror/class-refvisitor-inl.h @@ -32,12 +32,12 @@ template <bool kVisitNativeRoots, inline void Class::VisitReferences(ObjPtr<Class> klass, const Visitor& visitor) { VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass.Ptr(), visitor); // Right after a class is allocated, but not yet loaded - // (kStatusNotReady, see ClassLinker::LoadClass()), GC may find it + // (ClassStatus::kNotReady, see ClassLinker::LoadClass()), GC may find it // and scan it. IsTemp() may call Class::GetAccessFlags() but may // fail in the DCHECK in Class::GetAccessFlags() because the class - // status is kStatusNotReady. To avoid it, rely on IsResolved() + // status is ClassStatus::kNotReady. To avoid it, rely on IsResolved() // only. This is fine because a temp class never goes into the - // kStatusResolved state. + // ClassStatus::kResolved state. if (IsResolved<kVerifyFlags>()) { // Temp classes don't ever populate imt/vtable or static fields and they are not even // allocated with the right size for those. Also, unresolved classes don't have fields diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 892c03912a..8a7defd362 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -20,13 +20,14 @@ #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" #include "class_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" #include "dex_cache.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" #include "subtype_check.h" @@ -53,23 +54,6 @@ using android::base::StringPrintf; GcRoot<Class> Class::java_lang_Class_; -constexpr Class::Status Class::kStatusRetired; -constexpr Class::Status Class::kStatusErrorResolved; -constexpr Class::Status Class::kStatusErrorUnresolved; -constexpr Class::Status Class::kStatusNotReady; -constexpr Class::Status Class::kStatusIdx; -constexpr Class::Status Class::kStatusLoaded; -constexpr Class::Status Class::kStatusResolving; -constexpr Class::Status Class::kStatusResolved; -constexpr Class::Status Class::kStatusVerifying; -constexpr Class::Status Class::kStatusRetryVerificationAtRuntime; -constexpr Class::Status Class::kStatusVerifyingAtRuntime; -constexpr Class::Status Class::kStatusVerified; -constexpr Class::Status Class::kStatusSuperclassValidated; -constexpr Class::Status Class::kStatusInitializing; -constexpr Class::Status Class::kStatusInitialized; -constexpr Class::Status Class::kStatusMax; - void Class::SetClassClass(ObjPtr<Class> java_lang_Class) { CHECK(java_lang_Class_.IsNull()) << java_lang_Class_.Read() @@ -130,19 +114,19 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) { } } -void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { - Status old_status = h_this->GetStatus(); +void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self) { + ClassStatus old_status = h_this->GetStatus(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized(); if (LIKELY(class_linker_initialized)) { if (UNLIKELY(new_status <= old_status && - new_status != kStatusErrorUnresolved && - new_status != kStatusErrorResolved && - new_status != kStatusRetired)) { + new_status != ClassStatus::kErrorUnresolved && + new_status != ClassStatus::kErrorResolved && + new_status != ClassStatus::kRetired)) { LOG(FATAL) << "Unexpected change back of class status for " << h_this->PrettyClass() << " " << old_status << " -> " << new_status; } - if (new_status >= kStatusResolved || old_status >= kStatusResolved) { + if (new_status >= ClassStatus::kResolved || old_status >= ClassStatus::kResolved) { // When classes are being resolved the resolution code should hold the lock. CHECK_EQ(h_this->GetLockOwnerThreadId(), self->GetThreadId()) << "Attempt to change status of class while not holding its lock: " @@ -154,7 +138,7 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { << "Attempt to set as erroneous an already erroneous class " << h_this->PrettyClass() << " old_status: " << old_status << " new_status: " << new_status; - CHECK_EQ(new_status == kStatusErrorResolved, old_status >= kStatusResolved); + CHECK_EQ(new_status == ClassStatus::kErrorResolved, old_status >= ClassStatus::kResolved); if (VLOG_IS_ON(class_linker)) { LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous."; if (self->IsExceptionPending()) { @@ -180,7 +164,7 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { // Setting the object size alloc fast path needs to be after the status write so that if the // alloc path sees a valid object size, we would know that it's initialized as long as it has a // load-acquire/fake dependency. - if (new_status == kStatusInitialized && !h_this->IsVariableSize()) { + if (new_status == ClassStatus::kInitialized && !h_this->IsVariableSize()) { DCHECK_EQ(h_this->GetObjectSizeAllocFastPath(), std::numeric_limits<uint32_t>::max()); // Finalizable objects must always go slow path. if (!h_this->IsFinalizable()) { @@ -198,13 +182,13 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { if (h_this->IsTemp()) { // Class is a temporary one, ensure that waiters for resolution get notified of retirement // so that they can grab the new version of the class from the class linker's table. - CHECK_LT(new_status, kStatusResolved) << h_this->PrettyDescriptor(); - if (new_status == kStatusRetired || new_status == kStatusErrorUnresolved) { + CHECK_LT(new_status, ClassStatus::kResolved) << h_this->PrettyDescriptor(); + if (new_status == ClassStatus::kRetired || new_status == ClassStatus::kErrorUnresolved) { h_this->NotifyAll(self); } } else { - CHECK_NE(new_status, kStatusRetired); - if (old_status >= kStatusResolved || new_status >= kStatusResolved) { + CHECK_NE(new_status, ClassStatus::kRetired); + if (old_status >= ClassStatus::kResolved || new_status >= ClassStatus::kResolved) { h_this->NotifyAll(self); } } @@ -1034,7 +1018,7 @@ ObjPtr<Class> Class::GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint3 return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - ObjPtr<Class> interface = ClassLinker::LookupResolvedType( + ObjPtr<Class> interface = Runtime::Current()->GetClassLinker()->LookupResolvedType( type_idx, klass->GetDexCache(), klass->GetClassLoader()); return interface; } @@ -1046,9 +1030,7 @@ ObjPtr<Class> Class::ResolveDirectInterface(Thread* self, Handle<Class> 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; @@ -1130,7 +1112,7 @@ class ReadBarrierOnNativeRootsVisitor { // Update the field atomically. This may fail if mutator updates before us, but it's ok. auto* atomic_root = reinterpret_cast<Atomic<CompressedReference<Object>>*>(root); - atomic_root->CompareExchangeStrongSequentiallyConsistent( + atomic_root->CompareAndSetStrongSequentiallyConsistent( CompressedReference<Object>::FromMirrorPtr(old_ref.Ptr()), CompressedReference<Object>::FromMirrorPtr(new_ref.Ptr())); } @@ -1155,7 +1137,7 @@ class CopyClassVisitor { StackHandleScope<1> hs(self_); Handle<mirror::Class> h_new_class_obj(hs.NewHandle(obj->AsClass())); Object::CopyObject(h_new_class_obj.Get(), orig_->Get(), copy_bytes_); - Class::SetStatus(h_new_class_obj, Class::kStatusResolving, self_); + Class::SetStatus(h_new_class_obj, ClassStatus::kResolving, self_); h_new_class_obj->PopulateEmbeddedVTable(pointer_size_); h_new_class_obj->SetImt(imt_, pointer_size_); h_new_class_obj->SetClassSize(new_length_); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index c545a9b7d5..55c588930e 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -18,12 +18,13 @@ #define ART_RUNTIME_MIRROR_CLASS_H_ #include "base/bit_utils.h" +#include "base/casts.h" #include "base/enums.h" #include "base/iteration_range.h" #include "class_flags.h" #include "class_status.h" -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "gc/allocator_type.h" #include "gc_root.h" #include "imtable.h" @@ -77,38 +78,16 @@ class MANAGED Class FINAL : public Object { static constexpr uint32_t kPrimitiveTypeSizeShiftShift = 16; static constexpr uint32_t kPrimitiveTypeMask = (1u << kPrimitiveTypeSizeShiftShift) - 1; - // Make ClassStatus available as Class::Status. - using Status = ClassStatus; - - // Required for a minimal change. Fix up and remove in a future change. - static constexpr Status kStatusRetired = Status::kStatusRetired; - static constexpr Status kStatusErrorResolved = Status::kStatusErrorResolved; - static constexpr Status kStatusErrorUnresolved = Status::kStatusErrorUnresolved; - static constexpr Status kStatusNotReady = Status::kStatusNotReady; - static constexpr Status kStatusIdx = Status::kStatusIdx; - static constexpr Status kStatusLoaded = Status::kStatusLoaded; - static constexpr Status kStatusResolving = Status::kStatusResolving; - static constexpr Status kStatusResolved = Status::kStatusResolved; - static constexpr Status kStatusVerifying = Status::kStatusVerifying; - static constexpr Status kStatusRetryVerificationAtRuntime = - Status::kStatusRetryVerificationAtRuntime; - static constexpr Status kStatusVerifyingAtRuntime = Status::kStatusVerifyingAtRuntime; - static constexpr Status kStatusVerified = Status::kStatusVerified; - static constexpr Status kStatusSuperclassValidated = Status::kStatusSuperclassValidated; - static constexpr Status kStatusInitializing = Status::kStatusInitializing; - static constexpr Status kStatusInitialized = Status::kStatusInitialized; - static constexpr Status kStatusMax = Status::kStatusMax; - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - Status GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) { + ClassStatus GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid including "subtype_check_bits_and_status.h" to get the field. // The ClassStatus is always in the least-significant bits of status_. - return static_cast<Status>(static_cast<uint8_t>( - static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) & 0xff)); + return enum_cast<ClassStatus>( + static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) & 0xff); } // This is static because 'this' may be moved by GC. - static void SetStatus(Handle<Class> h_this, Status new_status, Thread* self) + static void SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); static MemberOffset StatusOffset() { @@ -118,24 +97,24 @@ class MANAGED Class FINAL : public Object { // Returns true if the class has been retired. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsRetired() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusRetired; + return GetStatus<kVerifyFlags>() == ClassStatus::kRetired; } // Returns true if the class has failed to link. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsErroneousUnresolved() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusErrorUnresolved; + return GetStatus<kVerifyFlags>() == ClassStatus::kErrorUnresolved; } // Returns true if the class has failed to initialize. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsErroneousResolved() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusErrorResolved; + return GetStatus<kVerifyFlags>() == ClassStatus::kErrorResolved; } // Returns true if the class status indicets that the class has failed to link or initialize. - static bool IsErroneous(Status status) { - return status == kStatusErrorUnresolved || status == kStatusErrorResolved; + static bool IsErroneous(ClassStatus status) { + return status == ClassStatus::kErrorUnresolved || status == ClassStatus::kErrorResolved; } // Returns true if the class has failed to link or initialize. @@ -147,44 +126,44 @@ class MANAGED Class FINAL : public Object { // Returns true if the class has been loaded. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsIdxLoaded() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusIdx; + return GetStatus<kVerifyFlags>() >= ClassStatus::kIdx; } // Returns true if the class has been loaded. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsLoaded() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusLoaded; + return GetStatus<kVerifyFlags>() >= ClassStatus::kLoaded; } // Returns true if the class has been linked. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsResolved() REQUIRES_SHARED(Locks::mutator_lock_) { - Status status = GetStatus<kVerifyFlags>(); - return status >= kStatusResolved || status == kStatusErrorResolved; + ClassStatus status = GetStatus<kVerifyFlags>(); + return status >= ClassStatus::kResolved || status == ClassStatus::kErrorResolved; } // Returns true if the class should be verified at runtime. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool ShouldVerifyAtRuntime() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusRetryVerificationAtRuntime; + return GetStatus<kVerifyFlags>() == ClassStatus::kRetryVerificationAtRuntime; } // Returns true if the class has been verified. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsVerified() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusVerified; + return GetStatus<kVerifyFlags>() >= ClassStatus::kVerified; } // Returns true if the class is initializing. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsInitializing() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() >= kStatusInitializing; + return GetStatus<kVerifyFlags>() >= ClassStatus::kInitializing; } // Returns true if the class is initialized. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsInitialized() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetStatus<kVerifyFlags>() == kStatusInitialized; + return GetStatus<kVerifyFlags>() == ClassStatus::kInitialized; } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> @@ -333,8 +312,10 @@ class MANAGED Class FINAL : public Object { // Returns true if this class is the placeholder and should retire and // be replaced with a class with the right size for embedded imt/vtable. bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) { - Status s = GetStatus(); - return s < Status::kStatusResolving && s != kStatusErrorResolved && ShouldHaveEmbeddedVTable(); + ClassStatus s = GetStatus(); + return s < ClassStatus::kResolving && + s != ClassStatus::kErrorResolved && + ShouldHaveEmbeddedVTable(); } String* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the cached name. diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 32d49bbc10..c18b219f19 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -20,7 +20,7 @@ #include "base/casts.h" #include "base/enums.h" #include "class-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array.h" diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 8b11c1290d..573244ef17 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -19,13 +19,14 @@ #include "dex_cache.h" +#include <android-base/logging.h> + #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 "dex/dex_file.h" #include "gc/heap-inl.h" #include "gc_root.h" #include "mirror/call_site.h" @@ -167,7 +168,7 @@ inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* // The first assignment for a given call site wins. Atomic<GcRoot<mirror::CallSite>>& ref = reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target); - if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) { + if (ref.CompareAndSetStrongSequentiallyConsistent(null_call_site, candidate)) { // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); return call_site; diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 2f63dff3be..eb4db00ccd 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 f75786b521..b46d80cf15 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -19,7 +19,8 @@ #include "array.h" #include "base/bit_utils.h" -#include "dex_file_types.h" +#include "base/mutex.h" +#include "dex/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 8198636b3d..d2bff2c19a 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<mirror::MethodType> method1_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), - dex_file, method1_id.proto_idx_, dex_cache, class_loader)); Handle<mirror::MethodType> 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.h b/runtime/mirror/emulated_stack_frame.h index b6aa949ec3..9bfa4d6098 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_ #define ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_ -#include "dex_instruction.h" +#include "dex/dex_instruction.h" #include "method_type.h" #include "object.h" #include "stack.h" diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index 42b8473ef0..0b4dde1aa8 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -22,6 +22,14 @@ namespace art { namespace mirror { +const char* MethodHandle::GetReturnTypeDescriptor(const char* invoke_method_name) { + if (strcmp(invoke_method_name, "invoke") == 0 || strcmp(invoke_method_name, "invokeExact") == 0) { + return "Ljava/lang/Object;"; + } else { + return nullptr; + } +} + mirror::Class* MethodHandle::StaticClass() { mirror::Class* klass = MethodHandleImpl::StaticClass()->GetSuperClass(); DCHECK(klass->DescriptorEquals("Ljava/lang/invoke/MethodHandle;")); diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index f362d43b01..a1bc976a25 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -81,6 +81,10 @@ class MANAGED MethodHandle : public Object { ALWAYS_INLINE ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_); + // Gets the return type for a named invoke method, or nullptr if the invoke method is not + // supported. + static const char* GetReturnTypeDescriptor(const char* invoke_method_name); + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); protected: diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index f1a86e5353..6e2a07c9e0 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -579,7 +579,7 @@ inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offs uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value); + return atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -597,7 +597,7 @@ inline bool Object::CasFieldWeakAcquire32(MemberOffset field_offset, uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareExchangeWeakAcquire(old_value, new_value); + return atomic_addr->CompareAndSetWeakAcquire(old_value, new_value); } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -615,7 +615,7 @@ inline bool Object::CasFieldWeakRelease32(MemberOffset field_offset, uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareExchangeWeakRelease(old_value, new_value); + return atomic_addr->CompareAndSetWeakRelease(old_value, new_value); } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -633,7 +633,7 @@ inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_of uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value); + return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags, @@ -689,7 +689,7 @@ inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offs } uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr); - return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value); + return atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_value, new_value); } template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> @@ -706,7 +706,7 @@ inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_of } uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr); - return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value); + return atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_value, new_value); } template<class T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, @@ -832,7 +832,7 @@ inline bool Object::CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier( uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetWeakSequentiallyConsistent(old_ref, new_ref); return success; } @@ -873,7 +873,7 @@ inline bool Object::CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrie uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetStrongSequentiallyConsistent(old_ref, new_ref); return success; } @@ -902,7 +902,7 @@ inline bool Object::CasFieldWeakRelaxedObjectWithoutWriteBarrier( uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeWeakRelaxed(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetWeakRelaxed(old_ref, new_ref); return success; } @@ -931,7 +931,7 @@ inline bool Object::CasFieldWeakReleaseObjectWithoutWriteBarrier( uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeWeakRelease(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetWeakRelease(old_ref, new_ref); return success; } diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h index 0a956633d4..d81fff0a22 100644 --- a/runtime/mirror/object-readbarrier-inl.h +++ b/runtime/mirror/object-readbarrier-inl.h @@ -52,7 +52,7 @@ inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset, uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); - return atomic_addr->CompareExchangeWeakRelaxed(old_value, new_value); + return atomic_addr->CompareAndSetWeakRelaxed(old_value, new_value); } inline bool Object::CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) { @@ -217,7 +217,7 @@ inline bool Object::CasFieldStrongRelaxedObjectWithoutWriteBarrier( uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeStrongRelaxed(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetStrongRelaxed(old_ref, new_ref); return success; } @@ -246,7 +246,7 @@ inline bool Object::CasFieldStrongReleaseObjectWithoutWriteBarrier( uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value(); Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); - bool success = atomic_addr->CompareExchangeStrongRelease(old_ref, new_ref); + bool success = atomic_addr->CompareAndSetStrongRelease(old_ref, new_ref); return success; } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 87cc620309..3765d0af59 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -24,7 +24,7 @@ #include "class-inl.h" #include "class.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" #include "handle_scope-inl.h" @@ -174,7 +174,7 @@ uint32_t Object::GenerateIdentityHashCode() { do { expected_value = hash_code_seed.LoadRelaxed(); new_value = expected_value * 1103515245 + 12345; - } while (!hash_code_seed.CompareExchangeWeakRelaxed(expected_value, new_value) || + } while (!hash_code_seed.CompareAndSetWeakRelaxed(expected_value, new_value) || (expected_value & LockWord::kHashMask) == 0); return expected_value & LockWord::kHashMask; } @@ -240,7 +240,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr<Object> CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr<mirror::Class> 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<Object> CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr<mirror::Class> 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/object_reference-inl.h b/runtime/mirror/object_reference-inl.h index 60f3ce153f..295b460a01 100644 --- a/runtime/mirror/object_reference-inl.h +++ b/runtime/mirror/object_reference-inl.h @@ -31,9 +31,8 @@ void ObjectReference<kPoisonReferences, MirrorType>::Assign(ObjPtr<MirrorType> p template<class MirrorType> bool HeapReference<MirrorType>::CasWeakRelaxed(MirrorType* expected_ptr, MirrorType* new_ptr) { - return reference_.CompareExchangeWeakRelaxed( - Compression::Compress(expected_ptr), - Compression::Compress(new_ptr)); + return reference_.CompareAndSetWeakRelaxed(Compression::Compress(expected_ptr), + Compression::Compress(new_ptr)); } } // namespace mirror diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 1a0fc76190..32a99eb753 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -29,7 +29,7 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "common_runtime_test.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 84587c871c..24c75ec0d8 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/mirror/throwable.cc b/runtime/mirror/throwable.cc index 077ad50dcc..a7a6d087e1 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -21,7 +21,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array-inl.h" diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index e7eac1a5ab..3f4a28ce9d 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -26,6 +26,59 @@ namespace mirror { namespace { +struct VarHandleAccessorToAccessModeEntry { + const char* method_name; + VarHandle::AccessMode access_mode; + + // Binary predicate function for finding access_mode by + // method_name. The access_mode field is ignored. + static bool CompareName(const VarHandleAccessorToAccessModeEntry& lhs, + const VarHandleAccessorToAccessModeEntry& rhs) { + return strcmp(lhs.method_name, rhs.method_name) < 0; + } +}; + +// Map of VarHandle accessor method names to access mode values. The list is alpha-sorted to support +// binary search. For the usage scenario - lookups in the verifier - a linear scan would likely +// suffice since we expect VarHandles to be a lesser encountered class. We could use a std::hashmap +// here and this would be easier to maintain if new values are added here. However, this entails +// CPU cycles initializing the structure on every execution and uses O(N) more memory for +// intermediate nodes and makes that memory dirty. Compile-time magic using constexpr is possible +// here, but that's a tax when this code is recompiled. +const VarHandleAccessorToAccessModeEntry kAccessorToAccessMode[VarHandle::kNumberOfAccessModes] = { + { "compareAndExchange", VarHandle::AccessMode::kCompareAndExchange }, + { "compareAndExchangeAcquire", VarHandle::AccessMode::kCompareAndExchangeAcquire }, + { "compareAndExchangeRelease", VarHandle::AccessMode::kCompareAndExchangeRelease }, + { "compareAndSet", VarHandle::AccessMode::kCompareAndSet }, + { "get", VarHandle::AccessMode::kGet }, + { "getAcquire", VarHandle::AccessMode::kGetAcquire }, + { "getAndAdd", VarHandle::AccessMode::kGetAndAdd }, + { "getAndAddAcquire", VarHandle::AccessMode::kGetAndAddAcquire }, + { "getAndAddRelease", VarHandle::AccessMode::kGetAndAddRelease }, + { "getAndBitwiseAnd", VarHandle::AccessMode::kGetAndBitwiseAnd }, + { "getAndBitwiseAndAcquire", VarHandle::AccessMode::kGetAndBitwiseAndAcquire }, + { "getAndBitwiseAndRelease", VarHandle::AccessMode::kGetAndBitwiseAndRelease }, + { "getAndBitwiseOr", VarHandle::AccessMode::kGetAndBitwiseOr }, + { "getAndBitwiseOrAcquire", VarHandle::AccessMode::kGetAndBitwiseOrAcquire }, + { "getAndBitwiseOrRelease", VarHandle::AccessMode::kGetAndBitwiseOrRelease }, + { "getAndBitwiseXor", VarHandle::AccessMode::kGetAndBitwiseXor }, + { "getAndBitwiseXorAcquire", VarHandle::AccessMode::kGetAndBitwiseXorAcquire }, + { "getAndBitwiseXorRelease", VarHandle::AccessMode::kGetAndBitwiseXorRelease }, + { "getAndSet", VarHandle::AccessMode::kGetAndSet }, + { "getAndSetAcquire", VarHandle::AccessMode::kGetAndSetAcquire }, + { "getAndSetRelease", VarHandle::AccessMode::kGetAndSetRelease }, + { "getOpaque", VarHandle::AccessMode::kGetOpaque }, + { "getVolatile", VarHandle::AccessMode::kGetVolatile }, + { "set", VarHandle::AccessMode::kSet }, + { "setOpaque", VarHandle::AccessMode::kSetOpaque }, + { "setRelease", VarHandle::AccessMode::kSetRelease }, + { "setVolatile", VarHandle::AccessMode::kSetVolatile }, + { "weakCompareAndSet", VarHandle::AccessMode::kWeakCompareAndSet }, + { "weakCompareAndSetAcquire", VarHandle::AccessMode::kWeakCompareAndSetAcquire }, + { "weakCompareAndSetPlain", VarHandle::AccessMode::kWeakCompareAndSetPlain }, + { "weakCompareAndSetRelease", VarHandle::AccessMode::kWeakCompareAndSetRelease }, +}; + // Enumeration for describing the parameter and return types of an AccessMode. enum class AccessModeTemplate : uint32_t { kGet, // T Op(C0..CN) @@ -281,6 +334,41 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces return GetMethodTypeForAccessMode(self, this, access_mode); } +const char* VarHandle::GetReturnTypeDescriptor(const char* accessor_name) { + AccessMode access_mode; + if (!GetAccessModeByMethodName(accessor_name, &access_mode)) { + return nullptr; + } + AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); + switch (access_mode_template) { + case AccessModeTemplate::kGet: + case AccessModeTemplate::kCompareAndExchange: + case AccessModeTemplate::kGetAndUpdate: + return "Ljava/lang/Object;"; + case AccessModeTemplate::kCompareAndSet: + return "Z"; + case AccessModeTemplate::kSet: + return "V"; + } +} + +bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode) { + if (method_name == nullptr) { + return false; + } + VarHandleAccessorToAccessModeEntry target = { method_name, /*dummy*/VarHandle::AccessMode::kGet }; + auto last = std::cend(kAccessorToAccessMode); + auto it = std::lower_bound(std::cbegin(kAccessorToAccessMode), + last, + target, + VarHandleAccessorToAccessModeEntry::CompareName); + if (it == last || strcmp(it->method_name, method_name) != 0) { + return false; + } + *access_mode = it->access_mode; + return true; +} + void VarHandle::SetClass(Class* klass) { CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; CHECK(klass != nullptr); diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index a2a5d8c9ff..7b48669bba 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -26,6 +26,7 @@ namespace art { template<class T> class Handle; struct VarHandleOffsets; struct FieldVarHandleOffsets; +struct ArrayElementVarHandleOffsets; struct ByteArrayViewVarHandleOffsets; struct ByteBufferViewVarHandleOffsets; @@ -77,7 +78,9 @@ class MANAGED VarHandle : public Object { kGetAndBitwiseXor, kGetAndBitwiseXorRelease, kGetAndBitwiseXorAcquire, + kLast = kGetAndBitwiseXorAcquire, }; + constexpr static size_t kNumberOfAccessModes = static_cast<size_t>(AccessMode::kLast) + 1u; // Returns true if the AccessMode specified is a supported operation. bool IsAccessModeSupported(AccessMode accessMode) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -104,6 +107,14 @@ class MANAGED VarHandle : public Object { return static_class_.Read(); } + // Gets the return type descriptor for a named accessor method, + // nullptr if accessor_method is not supported. + static const char* GetReturnTypeDescriptor(const char* accessor_method); + + // Returns true and sets access_mode if method_name corresponds to a + // VarHandle access method, such as "setOpaque". Returns false otherwise. + static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode); + static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index 159f80c4fd..e844fd4436 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -987,5 +987,79 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { } } +TEST_F(VarHandleTest, GetMethodTypeForAccessMode) { + VarHandle::AccessMode access_mode; + + // Invalid access mode names + EXPECT_FALSE(VarHandle::GetAccessModeByMethodName(nullptr, &access_mode)); + EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("", &access_mode)); + EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("CompareAndExchange", &access_mode)); + EXPECT_FALSE(VarHandle::GetAccessModeByMethodName("compareAndExchangX", &access_mode)); + + // Valid access mode names + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchange", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchange, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchangeAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchangeAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndExchangeRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kCompareAndExchangeRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("compareAndSet", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kCompareAndSet, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("get", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGet, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAdd", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndAdd, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAddAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndAddAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndAddRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndAddRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAnd", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAnd, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAndAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAndAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseAndRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseAndRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOr", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOr, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOrAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOrAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseOrRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseOrRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXor", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXor, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXorAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXorAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndBitwiseXorRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndBitwiseXorRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSet", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndSet, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSetAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndSetAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getAndSetRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetAndSetRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getOpaque", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetOpaque, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("getVolatile", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kGetVolatile, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("set", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kSet, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setOpaque", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kSetOpaque, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kSetRelease, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("setVolatile", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kSetVolatile, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSet", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSet, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetAcquire", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetAcquire, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetPlain", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetPlain, access_mode); + EXPECT_TRUE(VarHandle::GetAccessModeByMethodName("weakCompareAndSetRelease", &access_mode)); + EXPECT_EQ(VarHandle::AccessMode::kWeakCompareAndSetRelease, access_mode); +} + } // namespace mirror } // namespace art diff --git a/runtime/monitor.cc b/runtime/monitor.cc index d5520d9aca..325591fb53 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -21,14 +21,15 @@ #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" #include "base/time_utils.h" #include "class_linker.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" -#include "dex_instruction-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" +#include "dex/dex_instruction-inl.h" #include "lock_word-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -133,7 +134,7 @@ Monitor::Monitor(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_ int32_t Monitor::GetHashCode() { while (!HasHashCode()) { - if (hash_code_.CompareExchangeWeakRelaxed(0, mirror::Object::GenerateIdentityHashCode())) { + if (hash_code_.CompareAndSetWeakRelaxed(0, mirror::Object::GenerateIdentityHashCode())) { break; } } @@ -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<intptr_t>(pretty_object), - pretty_object->PrettyTypeOf().c_str()); - } else { - // - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>) - // 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. } @@ -1406,11 +1399,10 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O 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 = - Instruction::At(&code_item->insns_[dex_lock_info.dex_pc]); - CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER) + 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<const void*>(monitor_enter_instruction); + << reinterpret_cast<const void*>(&monitor_enter_instruction); } // Iterate through the set of dex registers, as the compiler may not have held all of them diff --git a/runtime/monitor.h b/runtime/monitor.h index b4c0e6f471..f150a8c091 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 0000000000..5c962c3b26 --- /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 <android-base/logging.h> + +#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<MonitorObjectsStackVisitor*>(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 d00f979379..cf5934b6a0 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 c0de374904..a992b5cb5b 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -27,8 +27,8 @@ #include <class_loader_context.h> #include "common_throws.h" #include "compiler_filter.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" #include "jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 2663bea344..6da34bcc60 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<jlongArray>(long_counts); } +static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env, + jclass, + jobjectArray javaClasses, + jboolean includeAssignable) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ObjectArray<mirror::Class>> classes = hs.NewHandle( + soa.Decode<mirror::ObjectArray<mirror::Class>>(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<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr)); + for (size_t i = 0; i < num_classes; ++i) { + h_class.Assign(classes->Get(i)); + + VariableSizedHandleScope hs2(soa.Self()); + std::vector<Handle<mirror::Object>> 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 @@ -500,7 +548,7 @@ static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { return result; } -static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { +static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) { if (agent == nullptr) { ScopedObjectAccess soa(env); ThrowNullPointerException("agent is null"); @@ -522,7 +570,7 @@ static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { filename = chars.c_str(); } - Runtime::Current()->AttachAgent(filename); + Runtime::Current()->AttachAgent(env, filename, classloader); } static JNINativeMethod gMethods[] = { @@ -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;"), @@ -558,7 +607,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), - NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"), + NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 2d1f886896..400518df20 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -32,8 +32,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "class_linker-inl.h" #include "common_throws.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_types.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocator/dlmalloc.h" #include "gc/heap.h" @@ -223,7 +223,7 @@ static jboolean VMRuntime_is64Bit(JNIEnv*, jobject) { } static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) { - return down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; + return down_cast<JNIEnvExt*>(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<mirror::DexCache> dex_cache, } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr); + ObjPtr<mirror::Class> 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<mirror::DexCache> dex_cache, ui } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr); + ObjPtr<mirror::Class> 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_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index a88563da1f..3e8040bfa5 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -16,6 +16,8 @@ #include "dalvik_system_VMStack.h" +#include <type_traits> + #include "nativehelper/jni_macros.h" #include "art_method-inl.h" @@ -32,12 +34,17 @@ namespace art { -static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject peer) +template <typename T, + typename ResultT = + typename std::result_of<T(Thread*, const ScopedFastNativeObjectAccess&)>::type> +static ResultT GetThreadStack(const ScopedFastNativeObjectAccess& soa, + jobject peer, + T fn) REQUIRES_SHARED(Locks::mutator_lock_) { - jobject trace = nullptr; + ResultT trace = nullptr; ObjPtr<mirror::Object> decoded_peer = soa.Decode<mirror::Object>(peer); if (decoded_peer == soa.Self()->GetPeer()) { - trace = soa.Self()->CreateInternalStackTrace<false>(soa); + trace = fn(soa.Self(), soa); } else { // Never allow suspending the heap task thread since it may deadlock if allocations are // required for the stack trace. @@ -59,7 +66,7 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p // Must be runnable to create returned array. { ScopedObjectAccess soa2(soa.Self()); - trace = thread->CreateInternalStackTrace<false>(soa); + trace = fn(thread, soa); } // Restart suspended thread. bool resumed = thread_list->Resume(thread, SuspendReason::kInternal); @@ -75,7 +82,11 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, jobjectArray javaSteArray) { ScopedFastNativeObjectAccess soa(env); - jobject trace = GetThreadStack(soa, javaThread); + auto fn = [](Thread* thread, const ScopedFastNativeObjectAccess& soaa) + REQUIRES_SHARED(Locks::mutator_lock_) -> jobject { + return thread->CreateInternalStackTrace<false>(soaa); + }; + jobject trace = GetThreadStack(soa, javaThread, fn); if (trace == nullptr) { return 0; } @@ -138,7 +149,11 @@ static jclass VMStack_getStackClass2(JNIEnv* env, jclass) { static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject javaThread) { ScopedFastNativeObjectAccess soa(env); - jobject trace = GetThreadStack(soa, javaThread); + auto fn = [](Thread* thread, const ScopedFastNativeObjectAccess& soaa) + REQUIRES_SHARED(Locks::mutator_lock_) -> jobject { + return thread->CreateInternalStackTrace<false>(soaa); + }; + jobject trace = GetThreadStack(soa, javaThread, fn); if (trace == nullptr) { return nullptr; } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a7bee39a81..fd80aaeaf7 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -18,11 +18,14 @@ #include <stdlib.h> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #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 9359ffc7fd..7b999c04af 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -23,8 +23,8 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "common_throws.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -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<mirror::Class> interface(hs.NewHandle<mirror::Class>(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<false>(i, interface.Get()); } diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index a717264bcb..9a52f7002b 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<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast<JNIEnvExt*>(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE; } static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 5130ad50e4..6eebff4aca 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -17,7 +17,7 @@ #include "java_lang_VMClassLoader.h" #include "class_linker.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index 5be317147b..12d400895c 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -20,7 +20,7 @@ #include "class_linker-inl.h" #include "common_throws.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "handle_scope-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index abbb347be6..86124388bc 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -22,7 +22,7 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "class_linker.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index f209f1d73a..e37c14b41c 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -20,7 +20,7 @@ #include "nativehelper/jni_macros.h" #include "art_method-inl.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "handle.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 2e4dd8a599..f990c0421d 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -23,8 +23,8 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "common_throws.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 18ff9c39bf..b604dc0fa1 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -22,7 +22,7 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "class_linker.h" -#include "dex_file_annotations.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index c4ab5d69fc..0b3015bda8 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -21,8 +21,8 @@ #include "art_method-inl.h" #include "common_throws.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" #include "jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index c79f51b51e..8f8fd71727 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,8 +16,9 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" +#include <android-base/logging.h> + #include "base/array_ref.h" -#include "base/logging.h" #include "debugger.h" #include "jni_internal.h" #include "native_util.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index f5057b013a..836637f4f0 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 <android-base/logging.h> + #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<JNIEnvExt*>(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<uint32_t>(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<uint8_t> bytes; - Thread* self = static_cast<JNIEnvExt*>(env)->self; + Thread* self = GetSelf(env); { MutexLock mu(self, *Locks::thread_list_lock_); ThreadList* thread_list = Runtime::Current()->GetThreadList(); diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index cd8315cdf9..7d72805dc6 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -22,9 +22,9 @@ #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 "dex/dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index f166714b79..ec9455289f 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 7db199cd06..8484e2cde7 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 9d2118064d..6d4f18bdb1 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -22,7 +22,7 @@ #include "arch/instruction_set.h" #include "base/macros.h" #include "compiler_filter.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "safe_map.h" namespace art { @@ -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: .bss index mapping change. - static constexpr uint8_t kOatVersion[] = { '1', '3', '5', '\0' }; + // Last oat version changed reason: 4-bit ClassStatus. + static constexpr uint8_t kOatVersion[] = { '1', '3', '6', '\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 c82df7119f..df07a191bc 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -39,11 +39,13 @@ #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" -#include "dex_file_types.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_types.h" +#include "dex/standard_dex_file.h" #include "elf_file.h" #include "elf_utils.h" #include "gc_root.h" @@ -56,7 +58,6 @@ #include "oat_file_manager.h" #include "os.h" #include "runtime.h" -#include "standard_dex_file.h" #include "type_lookup_table.h" #include "utf-inl.h" #include "utils.h" @@ -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.", @@ -1068,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."; } } @@ -1499,14 +1500,10 @@ ArrayRef<GcRoot<mirror::Object>> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return 0; - } - const uint32_t debug_info_off = code_item->debug_info_off_; +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; } @@ -1657,9 +1654,8 @@ OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) con const uint8_t* status_pointer = oat_class_pointer; CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation(); - mirror::Class::Status status = - static_cast<mirror::Class::Status>(*reinterpret_cast<const int16_t*>(status_pointer)); - CHECK_LT(status, mirror::Class::kStatusMax); + ClassStatus status = enum_cast<ClassStatus>(*reinterpret_cast<const int16_t*>(status_pointer)); + CHECK_LE(status, ClassStatus::kLast); const uint8_t* type_pointer = status_pointer + sizeof(uint16_t); CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation(); @@ -1740,7 +1736,7 @@ void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) { } OatFile::OatClass::OatClass(const OatFile* oat_file, - mirror::Class::Status status, + ClassStatus status, OatClassType type, uint32_t bitmap_size, const uint32_t* bitmap_pointer, diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 36a4d7b8fc..02318b68b7 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -24,11 +24,12 @@ #include "base/array_ref.h" #include "base/mutex.h" #include "base/stringpiece.h" +#include "class_status.h" #include "compiler_filter.h" -#include "dex_file.h" -#include "dex_file_layout.h" +#include "dex/dex_file.h" +#include "dex/dex_file_layout.h" #include "index_bss_mapping.h" -#include "mirror/class.h" +#include "mirror/object.h" #include "oat.h" #include "os.h" #include "type_lookup_table.h" @@ -114,9 +115,9 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); - // Return the debug info offset of the code item `item` located in `dex_file`. - static uint32_t GetDebugInfoOffset(const DexFile& dex_file, - const DexFile::CodeItem* item); + // 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(); @@ -196,7 +197,7 @@ class OatFile { class OatClass FINAL { public: - mirror::Class::Status GetStatus() const { + ClassStatus GetStatus() const { return status_; } @@ -224,7 +225,7 @@ class OatFile { // See FindOatClass(). static OatClass Invalid() { return OatClass(/* oat_file */ nullptr, - mirror::Class::kStatusErrorUnresolved, + ClassStatus::kErrorUnresolved, kOatClassNoneCompiled, /* bitmap_size */ 0, /* bitmap_pointer */ nullptr, @@ -233,7 +234,7 @@ class OatFile { private: OatClass(const OatFile* oat_file, - mirror::Class::Status status, + ClassStatus status, OatClassType type, uint32_t bitmap_size, const uint32_t* bitmap_pointer, @@ -241,7 +242,7 @@ class OatFile { const OatFile* const oat_file_; - const mirror::Class::Status status_; + const ClassStatus status_; const OatClassType type_; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index cd18ce102e..240030cf5b 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -24,11 +24,11 @@ #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" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index b86f479eed..29b9bfcf7f 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -26,14 +26,14 @@ #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" #include "class_loader_context.h" -#include "dex_file-inl.h" -#include "dex_file_loader.h" -#include "dex_file_tracking_registrar.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_file_tracking_registrar.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc index aa28fd8c9b..98238e5600 100644 --- a/runtime/oat_quick_method_header.cc +++ b/runtime/oat_quick_method_header.cc @@ -17,7 +17,7 @@ #include "oat_quick_method_header.h" #include "art_method.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/runtime/os_linux.cc b/runtime/os_linux.cc index a463f700d8..1b3e0000da 100644 --- a/runtime/os_linux.cc +++ b/runtime/os_linux.cc @@ -23,7 +23,8 @@ #include <cstddef> #include <memory> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/unix_file/fd_file.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 85af560ce3..3ac3d03e90 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -18,8 +18,10 @@ #include <sstream> +#include <android-base/logging.h> + #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,15 +92,18 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::CheckJni) .Define("-Xjniopts:forcecopy") .IntoKey(M::JniOptsForceCopy) - .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"}) - .WithType<JDWP::JdwpOptions>() + .Define("-XjdwpProvider:_") + .WithType<JdwpProvider>() + .IntoKey(M::JdwpProvider) + .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_", "-XjdwpOptions:_"}) + .WithType<std::string>() .IntoKey(M::JdwpOptions) // TODO Re-enable -agentlib: once I have a good way to transform the values. // .Define("-agentlib:_") // .WithType<std::vector<ti::Agent>>().AppendValues() // .IntoKey(M::AgentLib) .Define("-agentpath:_") - .WithType<std::list<ti::Agent>>().AppendValues() + .WithType<std::list<ti::AgentSpec>>().AppendValues() .IntoKey(M::AgentPath) .Define("-Xms_") .WithType<MemoryKiB>() diff --git a/runtime/plugin.cc b/runtime/plugin.cc index 6aa078771b..7d86f1d5dc 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 f077aaf3fb..909c710a96 100644 --- a/runtime/plugin.h +++ b/runtime/plugin.h @@ -18,7 +18,8 @@ #define ART_RUNTIME_PLUGIN_H_ #include <string> -#include "base/logging.h" + +#include <android-base/logging.h> namespace art { diff --git a/runtime/primitive.h b/runtime/primitive.h index a429914d5c..5b163d8cbe 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -19,7 +19,8 @@ #include <sys/types.h> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/macros.h" namespace art { diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index f94923e065..3a7640fa8b 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -19,8 +19,9 @@ #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" -#include "dex_file_types.h" -#include "dex_instruction.h" +#include "base/logging.h" // For VLOG_IS_ON. +#include "dex/dex_file_types.h" +#include "dex/dex_instruction.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "entrypoints/runtime_asm_entrypoints.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 12b63c933d..1103dab52c 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 <android-base/logging.h> + #include "base/macros.h" #include "base/mutex.h" #include "deoptimization_kind.h" diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h index 5b72468fcd..ce11f3c19b 100644 --- a/runtime/quicken_info.h +++ b/runtime/quicken_info.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_QUICKEN_INFO_H_ #define ART_RUNTIME_QUICKEN_INFO_H_ -#include "dex_instruction.h" +#include "dex/dex_instruction.h" namespace art { diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index 642459924e..a77d100b92 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -131,7 +131,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root, // Update the field atomically. This may fail if mutator updates before us, but it's ok. if (ref != old_ref) { Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root); - atomic_root->CompareExchangeStrongRelaxed(old_ref, ref); + atomic_root->CompareAndSetStrongRelaxed(old_ref, ref); } } AssertToSpaceInvariant(gc_root_source, ref); @@ -174,7 +174,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(mirror::CompressedReference<Mirro if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) { auto* atomic_root = reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root); - atomic_root->CompareExchangeStrongRelaxed(old_ref, new_ref); + atomic_root->CompareAndSetStrongRelaxed(old_ref, new_ref); } } AssertToSpaceInvariant(gc_root_source, ref); diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index d4b9f4311f..e8df2ad4ce 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 <android-base/logging.h> + #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/reflection.cc b/runtime/reflection.cc index 9683cedd4d..635a03afe0 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -21,7 +21,7 @@ #include "base/enums.h" #include "class_linker.h" #include "common_throws.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "indirect_reference_table-inl.h" #include "java_vm_ext.h" #include "jni_internal.h" @@ -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<mirror::Object> result) { IndirectRef ref = reinterpret_cast<IndirectRef>(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 f09b6c9825..2f45b100d7 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" @@ -68,7 +69,7 @@ #include "class_linker-inl.h" #include "compiler_callbacks.h" #include "debugger.h" -#include "dex_file_loader.h" +#include "dex/dex_file_loader.h" #include "elf_file.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "experimental_flags.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), @@ -288,13 +290,19 @@ Runtime::~Runtime() { Thread* self = Thread::Current(); const bool attach_shutdown_thread = self == nullptr; if (attach_shutdown_thread) { - CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false)); + // We can only create a peer if the runtime is actually started. This is only not true during + // some tests. + CHECK(AttachCurrentThread("Shutdown thread", + false, + GetSystemThreadGroup(), + /* Create peer */IsStarted())); self = Thread::Current(); } else { LOG(WARNING) << "Current thread not detached in Runtime shutdown"; } 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 +360,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. @@ -363,7 +371,7 @@ Runtime::~Runtime() { // TODO Maybe do some locking. for (auto& agent : agents_) { - agent.Unload(); + agent->Unload(); } // TODO Maybe do some locking @@ -804,7 +812,7 @@ bool Runtime::Start() { { ScopedObjectAccess soa(self); - self->GetJniEnv()->locals.AssertEmpty(); + self->GetJniEnv()->AssertLocalsEmpty(); } VLOG(startup) << "Runtime::Start exiting"; @@ -868,8 +876,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() { @@ -1161,7 +1171,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess); plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins); - agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath); + agent_specs_ = runtime_options.ReleaseOrDefault(Opt::AgentPath); // TODO Add back in -agentlib // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) { // agents_.push_back(lib); @@ -1218,8 +1228,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()); @@ -1472,16 +1503,32 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Startup agents // TODO Maybe we should start a new thread to run these on. Investigate RI behavior more. - for (auto& agent : agents_) { + for (auto& agent_spec : agent_specs_) { // TODO Check err int res = 0; std::string err = ""; - ti::Agent::LoadError result = agent.Load(&res, &err); - if (result == ti::Agent::kInitializationError) { - LOG(FATAL) << "Unable to initialize agent!"; - } else if (result != ti::Agent::kNoError) { - LOG(ERROR) << "Unable to load an agent: " << err; + ti::LoadError error; + std::unique_ptr<ti::Agent> agent = agent_spec.Load(&res, &error, &err); + + if (agent != nullptr) { + agents_.push_back(std::move(agent)); + continue; + } + + switch (error) { + case ti::LoadError::kInitializationError: + LOG(FATAL) << "Unable to initialize agent!"; + UNREACHABLE(); + + case ti::LoadError::kLoadingError: + LOG(ERROR) << "Unable to load an agent: " << err; + continue; + + case ti::LoadError::kNoError: + break; } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } { ScopedObjectAccess soa(self); @@ -1506,6 +1553,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; @@ -1527,7 +1575,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, // revisit this and make sure we're doing this on the right thread // (and we synchronize access to any shared data structures like "agents_") // -void Runtime::AttachAgent(const std::string& agent_arg) { +void Runtime::AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader) { std::string error_msg; if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) { LOG(WARNING) << "Could not load plugin: " << error_msg; @@ -1536,15 +1584,16 @@ void Runtime::AttachAgent(const std::string& agent_arg) { return; } - ti::Agent agent(agent_arg); + ti::AgentSpec agent_spec(agent_arg); int res = 0; - ti::Agent::LoadError result = agent.Attach(&res, &error_msg); + ti::LoadError error; + std::unique_ptr<ti::Agent> agent = agent_spec.Attach(env, class_loader, &res, &error, &error_msg); - if (result == ti::Agent::kNoError) { + if (agent != nullptr) { agents_.push_back(std::move(agent)); } else { - LOG(WARNING) << "Agent attach failed (result=" << result << ") : " << error_msg; + LOG(WARNING) << "Agent attach failed (result=" << error << ") : " << error_msg; ScopedObjectAccess soa(Thread::Current()); ThrowIOException("%s", error_msg.c_str()); } @@ -1570,7 +1619,7 @@ void Runtime::InitNativeMethods() { // libcore can't because it's the library that implements System.loadLibrary! { std::string error_msg; - if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) { + if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, &error_msg)) { LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg; } } @@ -1579,7 +1628,7 @@ void Runtime::InitNativeMethods() { ? "libopenjdkd.so" : "libopenjdk.so"; std::string error_msg; - if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) { + if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, &error_msg)) { LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg; } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 6b01cc220f..c8edabce09 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -31,10 +31,11 @@ #include "base/macros.h" #include "base/mutex.h" #include "deoptimization_kind.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #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" @@ -65,6 +66,7 @@ class Throwable; } // namespace mirror namespace ti { class Agent; +class AgentSpec; } // namespace ti namespace verifier { class MethodVerifier; @@ -586,6 +588,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_; @@ -651,9 +661,9 @@ class Runtime { void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); - void AttachAgent(const std::string& agent_arg); + void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader); - const std::list<ti::Agent>& GetAgents() const { + const std::list<std::unique_ptr<ti::Agent>>& GetAgents() const { return agents_; } @@ -688,6 +698,14 @@ class Runtime { return madvise_random_access_; } + const std::string& GetJdwpOptions() { + return jdwp_options_; + } + + JdwpProvider GetJdwpProvider() const { + return jdwp_provider_; + } + private: static void InitPlatformSignalHandlers(); @@ -762,7 +780,8 @@ class Runtime { std::string class_path_string_; std::vector<std::string> properties_; - std::list<ti::Agent> agents_; + std::list<ti::AgentSpec> agent_specs_; + std::list<std::unique_ptr<ti::Agent>> agents_; std::vector<Plugin> plugins_; // The default stack size for managed threads created by the runtime. @@ -899,6 +918,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 +964,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 40d7889565..cd3c0b7c88 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -49,6 +49,35 @@ void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8 } } +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 baf941a8e1..24386ba14a 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -22,7 +22,7 @@ #include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "handle.h" namespace art { @@ -62,6 +62,19 @@ class DdmCallback { 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() {} @@ -197,6 +210,17 @@ class RuntimeCallbacks { 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<ThreadLifecycleCallback*> thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -214,6 +238,8 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector<DdmCallback*> ddm_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector<DebuggerControlCallback*> debugger_control_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index eb69d91dad..59af9187f9 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -23,10 +23,12 @@ #include <sstream> #include <string> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#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.def b/runtime/runtime_options.def index 2e03562505..1dd3de5039 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 @@ -123,8 +124,8 @@ RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) RUNTIME_OPTIONS_KEY (std::string, CpuAbiList) RUNTIME_OPTIONS_KEY (std::string, Fingerprint) RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{...} -RUNTIME_OPTIONS_KEY (std::list<ti::Agent>, AgentLib) // -agentlib:<libname>=<options> -RUNTIME_OPTIONS_KEY (std::list<ti::Agent>, AgentPath) // -agentpath:<libname>=<options> +RUNTIME_OPTIONS_KEY (std::list<ti::AgentSpec>, AgentLib) // -agentlib:<libname>=<options> +RUNTIME_OPTIONS_KEY (std::list<ti::AgentSpec>, AgentPath) // -agentpath:<libname>=<options> RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> // Not parse-able from command line, but can be provided explicitly. diff --git a/runtime/safe_map.h b/runtime/safe_map.h index f29869172e..33e45bdbd8 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -21,8 +21,9 @@ #include <memory> #include <type_traits> +#include <android-base/logging.h> + #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 aa96871145..f95593209b 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 <android-base/logging.h> + #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<JNIEnvExt*>(env)), vm_(env_->vm) {} + : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {} inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self) : self_(self), env_(down_cast<JNIEnvExt*>(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 94354fc586..6a86cc6411 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,6 @@ #include <type_traits> #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 02b6124118..0c42c5ae8d 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 MirrorType> class ObjPtr; class Thread; diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index bf5d718113..d9c4da9b96 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 6f888525cb..53613236fa 100644 --- a/runtime/signal_set.h +++ b/runtime/signal_set.h @@ -19,7 +19,7 @@ #include <signal.h> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/stack.cc b/runtime/stack.cc index 5ad1f7c9c5..dfdea28ae8 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -23,7 +23,7 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/space/image_space.h" @@ -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 diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 85c734ee4c..62fb54fb56 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -23,8 +23,8 @@ #include "base/bit_utils.h" #include "base/bit_vector.h" #include "bit_memory_region.h" +#include "dex/dex_file_types.h" #include "leb128.h" -#include "dex_file_types.h" #include "memory_region.h" #include "method_info.h" diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h index 0560c33eee..511c2c66f2 100644 --- a/runtime/stride_iterator.h +++ b/runtime/stride_iterator.h @@ -19,7 +19,7 @@ #include <iterator> -#include "base/logging.h" +#include <android-base/logging.h> namespace art { diff --git a/runtime/string_reference.h b/runtime/string_reference.h index d0ab4e40d0..97661c6019 100644 --- a/runtime/string_reference.h +++ b/runtime/string_reference.h @@ -19,10 +19,11 @@ #include <stdint.h> -#include "base/logging.h" -#include "dex_file-inl.h" -#include "dex_file_reference.h" -#include "dex_file_types.h" +#include <android-base/logging.h> + +#include "dex/dex_file-inl.h" +#include "dex/dex_file_reference.h" +#include "dex/dex_file_types.h" #include "utf-inl.h" namespace art { diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 1556abe67f..54d2f00106 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -216,12 +216,13 @@ * All node targets (in `src <: target`) get Assigned, and any parent of an Initialized * node also gets Assigned. */ -struct MockSubtypeCheck; // Forward declaration for testing. namespace art { -// This is class is using a template parameter to enable testability without losing performance. -// KlassT is almost always `mirror::Class*` or `ObjPtr<mirror::Class>`. -template <typename KlassT /* Pointer-like type to Class */> +struct MockSubtypeCheck; // Forward declaration for testing. + +// This class is using a template parameter to enable testability without losing performance. +// ClassPtr is almost always `mirror::Class*` or `ObjPtr<mirror::Class>`. +template <typename ClassPtr /* Pointer-like type to Class */> struct SubtypeCheck { // Force this class's SubtypeCheckInfo state into at least Initialized. // As a side-effect, all parent classes also become Assigned|Overflowed. @@ -230,7 +231,7 @@ struct SubtypeCheck { // // Post-condition: State is >= Initialized. // Returns: The precise SubtypeCheckInfo::State. - static SubtypeCheckInfo::State EnsureInitialized(KlassT& klass) + static SubtypeCheckInfo::State EnsureInitialized(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { return InitializeOrAssign(klass, /*assign*/false).GetState(); @@ -243,7 +244,7 @@ struct SubtypeCheck { // // Post-condition: State is Assigned|Overflowed. // Returns: The precise SubtypeCheckInfo::State. - static SubtypeCheckInfo::State EnsureAssigned(KlassT& klass) + static SubtypeCheckInfo::State EnsureAssigned(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { return InitializeOrAssign(klass, /*assign*/true).GetState(); @@ -258,7 +259,7 @@ struct SubtypeCheck { // Cost: O(1). // // Returns: A state that is always Uninitialized. - static SubtypeCheckInfo::State ForceUninitialize(KlassT& klass) + static SubtypeCheckInfo::State ForceUninitialize(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { // Trying to do this in a real runtime will break thread safety invariants @@ -288,7 +289,7 @@ struct SubtypeCheck { // Cost: O(Depth(Class)). // // Returns the encoded_src value. Must be >= Initialized (EnsureInitialized). - static BitString::StorageType GetEncodedPathToRootForSource(const KlassT& klass) + static BitString::StorageType GetEncodedPathToRootForSource(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_NE(SubtypeCheckInfo::kUninitialized, GetSubtypeCheckInfo(klass).GetState()); @@ -301,7 +302,7 @@ struct SubtypeCheck { // Cost: O(Depth(Class)). // // Returns the encoded_target value. Must be Assigned (EnsureAssigned). - static BitString::StorageType GetEncodedPathToRootForTarget(const KlassT& klass) + static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); @@ -314,7 +315,7 @@ struct SubtypeCheck { // Cost: O(Depth(Class)). // // Returns the mask_target value. Must be Assigned (EnsureAssigned). - static BitString::StorageType GetEncodedPathToRootMask(const KlassT& klass) + static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState()); @@ -333,7 +334,7 @@ struct SubtypeCheck { // Runtime cost: O(Depth(Class)), but would be O(1) if depth was known. // // If the result is known, return kSubtypeOf or kNotSubtypeOf. - static SubtypeCheckInfo::Result IsSubtypeOf(const KlassT& source, const KlassT& target) + static SubtypeCheckInfo::Result IsSubtypeOf(ClassPtr source, ClassPtr target) REQUIRES_SHARED(Locks::mutator_lock_) { SubtypeCheckInfo sci = GetSubtypeCheckInfo(source); SubtypeCheckInfo target_sci = GetSubtypeCheckInfo(target); @@ -342,24 +343,24 @@ struct SubtypeCheck { } // Print SubtypeCheck bitstring and overflow to a stream (e.g. for oatdump). - static std::ostream& Dump(const KlassT& klass, std::ostream& os) + static std::ostream& Dump(ClassPtr klass, std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_) { return os << GetSubtypeCheckInfo(klass); } - static void WriteStatus(const KlassT& klass, ClassStatus status) + static void WriteStatus(ClassPtr klass, ClassStatus status) REQUIRES_SHARED(Locks::mutator_lock_) { WriteStatusImpl(klass, status); } private: - static KlassT GetParentClass(const KlassT& klass) + static ClassPtr GetParentClass(ClassPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(klass->HasSuperClass()); - return KlassT(klass->GetSuperClass()); + return ClassPtr(klass->GetSuperClass()); } - static SubtypeCheckInfo InitializeOrAssign(KlassT& klass, bool assign) + static SubtypeCheckInfo InitializeOrAssign(ClassPtr klass, bool assign) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(!klass->HasSuperClass())) { @@ -380,8 +381,8 @@ struct SubtypeCheck { } // Force all ancestors to Assigned | Overflowed. - KlassT parent_klass = GetParentClass(klass); - EnsureAssigned(parent_klass); + ClassPtr parent_klass = GetParentClass(klass); + size_t parent_depth = InitializeOrAssign(parent_klass, /*assign*/true).GetDepth(); if (kIsDebugBuild) { SubtypeCheckInfo::State parent_state = GetSubtypeCheckInfo(parent_klass).GetState(); DCHECK(parent_state == SubtypeCheckInfo::kAssigned || @@ -390,8 +391,8 @@ struct SubtypeCheck { } // Read. - SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass); - SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass); + SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass, parent_depth + 1u); + SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass, parent_depth); // Modify. const SubtypeCheckInfo::State sci_state = sci.GetState(); @@ -426,7 +427,7 @@ struct SubtypeCheck { return sci; } - static SubtypeCheckBitsAndStatus ReadField(const KlassT& klass) + static SubtypeCheckBitsAndStatus ReadField(ClassPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { SubtypeCheckBitsAndStatus current_bits_and_status; @@ -441,7 +442,7 @@ struct SubtypeCheck { return current_bits_and_status; } - static void WriteSubtypeCheckBits(const KlassT& klass, const SubtypeCheckBits& new_bits) + static void WriteSubtypeCheckBits(ClassPtr klass, const SubtypeCheckBits& new_bits) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { // Use a "CAS" to write the SubtypeCheckBits in the class. @@ -490,7 +491,7 @@ struct SubtypeCheck { } } - static void WriteStatusImpl(const KlassT& klass, ClassStatus status) + static void WriteStatusImpl(ClassPtr klass, ClassStatus status) REQUIRES_SHARED(Locks::mutator_lock_) { // Despite not having a lock annotation, this is done with mutual exclusion. // See Class::SetStatus for more details. @@ -519,7 +520,7 @@ struct SubtypeCheck { } } - static bool CasFieldWeakSequentiallyConsistent32(const KlassT& klass, + static bool CasFieldWeakSequentiallyConsistent32(ClassPtr klass, MemberOffset offset, int32_t old_value, int32_t new_value) @@ -541,18 +542,23 @@ struct SubtypeCheck { // it also requires calling klass->Depth. // // Anything calling this function will also be O(Depth(Class)). - static SubtypeCheckInfo GetSubtypeCheckInfo(const KlassT& klass) + static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetSubtypeCheckInfo(klass, klass->Depth()); + } + + // Get the SubtypeCheckInfo for a klass with known depth. + static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass, size_t depth) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(depth, klass->Depth()); SubtypeCheckBitsAndStatus current_bits_and_status = ReadField(klass); - size_t depth = klass->Depth(); const SubtypeCheckInfo current = SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth); return current; } - static void SetSubtypeCheckInfo(KlassT& klass, - const SubtypeCheckInfo& new_sci) + static void SetSubtypeCheckInfo(ClassPtr klass, const SubtypeCheckInfo& new_sci) REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { SubtypeCheckBits new_bits = new_sci.GetSubtypeCheckBits(); @@ -565,7 +571,7 @@ struct SubtypeCheck { SubtypeCheck(SubtypeCheck&& other) = default; ~SubtypeCheck() = default; - friend struct ::MockSubtypeCheck; + friend struct MockSubtypeCheck; }; } // namespace art diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index cd579c3a5c..61d590bd59 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: @@ -135,6 +138,11 @@ struct SubtypeCheckInfo { kSubtypeOf // Enough data. src is a subchild of the target. }; + // Get the raw depth. + size_t GetDepth() const { + return depth_; + } + // Chop off the depth, returning only the bitstring+of state. // (Used to store into memory, since storing the depth would be redundant.) SubtypeCheckBits GetSubtypeCheckBits() const { @@ -219,7 +227,7 @@ 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. + // 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. @@ -235,7 +243,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 +267,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,6 +393,7 @@ 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 diff --git a/runtime/subtype_check_test.cc b/runtime/subtype_check_test.cc index dd51c18eff..e297d0beb4 100644 --- a/runtime/subtype_check_test.cc +++ b/runtime/subtype_check_test.cc @@ -24,10 +24,6 @@ namespace art { constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity]; constexpr size_t BitString::kCapacity; -}; // namespace art - -using namespace art; // NOLINT - struct MockClass { explicit MockClass(MockClass* parent, size_t x = 0, size_t y = 0) { parent_ = parent; @@ -1061,3 +1057,5 @@ TEST_F(SubtypeCheckTest, EnsureInitialized_TooWide_TooDeep) { } // TODO: add dcheck for child-parent invariants (e.g. child < parent.next) and death tests + +} // namespace art diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index b5a962691b..2f6f50e31e 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<JNIEnvExt*>(env)); - return full_env->self; + return full_env->GetSelf(); } inline void Thread::AllowThreadSuspension() { @@ -200,7 +201,7 @@ inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state // CAS the value with a memory ordering. bool done = - tls32_.state_and_flags.as_atomic_int.CompareExchangeWeakRelease(old_state_and_flags.as_int, + tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakRelease(old_state_and_flags.as_int, new_state_and_flags.as_int); if (LIKELY(done)) { break; @@ -251,7 +252,7 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.state = kRunnable; // CAS the value with a memory barrier. - if (LIKELY(tls32_.state_and_flags.as_atomic_int.CompareExchangeWeakAcquire( + if (LIKELY(tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakAcquire( old_state_and_flags.as_int, new_state_and_flags.as_int))) { // Mark the acquisition of a share of the mutator_lock_. diff --git a/runtime/thread.cc b/runtime/thread.cc index bec1c908ad..9f4e5441a5 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -47,9 +47,9 @@ #include "base/to_str.h" #include "class_linker-inl.h" #include "debugger.h" -#include "dex_file-inl.h" -#include "dex_file_annotations.h" -#include "dex_file_types.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_annotations.h" +#include "dex/dex_file_types.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "gc/accounting/card_table-inl.h" @@ -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<JNIEnvExt*>(env)->self; + Thread* self = static_cast<JNIEnvExt*>(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 <typename PeerAction> 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"; } } @@ -1275,7 +1278,7 @@ bool Thread::ModifySuspendCountInternal(Thread* self, AtomicClearFlag(kSuspendRequest); } else { // Two bits might be set simultaneously. - tls32_.state_and_flags.as_atomic_int.FetchAndOrSequentiallyConsistent(flags); + tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flags); TriggerSuspend(); } return true; @@ -1316,7 +1319,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { int32_t cur_val = pending_threads->LoadRelaxed(); CHECK_GT(cur_val, 0) << "Unexpected value for PassActiveSuspendBarriers(): " << cur_val; // Reduce value by 1. - done = pending_threads->CompareExchangeWeakRelaxed(cur_val, cur_val - 1); + done = pending_threads->CompareAndSetWeakRelaxed(cur_val, cur_val - 1); #if ART_USE_FUTEXES if (done && (cur_val - 1) == 0) { // Weak CAS may fail spuriously. futex(pending_threads->Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); @@ -1387,7 +1390,7 @@ bool Thread::RequestCheckpoint(Closure* function) { union StateAndFlags new_state_and_flags; new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.flags |= kCheckpointRequest; - bool success = tls32_.state_and_flags.as_atomic_int.CompareExchangeStrongSequentiallyConsistent( + bool success = tls32_.state_and_flags.as_atomic_int.CompareAndSetStrongSequentiallyConsistent( old_state_and_flags.as_int, new_state_and_flags.as_int); if (success) { // Succeeded setting checkpoint flag, now insert the actual checkpoint. @@ -1416,7 +1419,7 @@ bool Thread::RequestEmptyCheckpoint() { union StateAndFlags new_state_and_flags; new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.flags |= kEmptyCheckpointRequest; - bool success = tls32_.state_and_flags.as_atomic_int.CompareExchangeStrongSequentiallyConsistent( + bool success = tls32_.state_and_flags.as_atomic_int.CompareAndSetStrongSequentiallyConsistent( old_state_and_flags.as_int, new_state_and_flags.as_int); if (success) { TriggerSuspend(); @@ -1557,7 +1560,7 @@ Closure* Thread::GetFlipFunction() { if (func == nullptr) { return nullptr; } - } while (!atomic_func->CompareExchangeWeakSequentiallyConsistent(func, nullptr)); + } while (!atomic_func->CompareAndSetWeakSequentiallyConsistent(func, nullptr)); DCHECK(func != nullptr); return func; } @@ -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<mirror::Class> c = m->GetDeclaringClass(); ObjPtr<mirror::DexCache> 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<std::ostream*>(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<intptr_t>(o), - o->PrettyTypeOf().c_str()); + os << msg << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", + reinterpret_cast<intptr_t>(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<java.lang.ref.ReferenceQueue>) + // 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) @@ -2141,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) { @@ -2310,7 +2339,7 @@ ObjPtr<mirror::Object> 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<kWithoutReadBarrier>(ref); } else if (kind == kHandleScopeOrInvalid) { @@ -2321,15 +2350,15 @@ ObjPtr<mirror::Object> Thread::DecodeJObject(jobject obj) const { result = reinterpret_cast<StackReference<mirror::Object>*>(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<Thread*>(this), ref); + result = tlsPtr_.jni_env->vm_->DecodeWeakGlobal(const_cast<Thread*>(this), ref); if (Runtime::Current()->IsClearedJniWeakGlobal(result)) { // This is a special case where it's okay to return null. expect_null = true; @@ -2338,7 +2367,7 @@ ObjPtr<mirror::Object> 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<IndirectRefKind>(kind).c_str(), obj); } return result; @@ -2349,7 +2378,7 @@ bool Thread::IsJWeakCleared(jweak obj) const { IndirectRef ref = reinterpret_cast<IndirectRef>(obj); IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); CHECK_EQ(kind, kWeakGlobal); - return tlsPtr_.jni_env->vm->IsWeakGlobalCleared(const_cast<Thread*>(this), ref); + return tlsPtr_.jni_env->vm_->IsWeakGlobalCleared(const_cast<Thread*>(this), ref); } // Implements java.lang.Thread.interrupted. @@ -2603,6 +2632,61 @@ bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exceptio return count_visitor.GetDepth() == static_cast<uint32_t>(exception->GetStackDepth()); } +static ObjPtr<mirror::StackTraceElement> 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<mirror::String>(nullptr)); + auto source_name_object(hs.NewHandle<mirror::String>(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<mirror::String> 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, @@ -2649,55 +2733,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize); uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>( i + method_trace->GetLength() / 2, kRuntimePointerSize); - int32_t line_number; - StackHandleScope<3> hs(soa.Self()); - auto class_name_object(hs.NewHandle<mirror::String>(nullptr)); - auto source_name_object(hs.NewHandle<mirror::String>(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<mirror::String> method_name_object( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); - if (method_name_object == nullptr) { - return nullptr; - } - ObjPtr<mirror::StackTraceElement> obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object, - method_name_object, - source_name_object, - line_number); + ObjPtr<mirror::StackTraceElement> obj = CreateStackTraceElement(soa, method, dex_pc); if (obj == nullptr) { return nullptr; } @@ -3389,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, @@ -3480,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)); @@ -3700,6 +3736,7 @@ void Thread::DeoptimizeWithDeoptimizationException(JValue* result) { void Thread::SetAsyncException(ObjPtr<mirror::Throwable> 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 0803975d26..1e89887c3e 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; @@ -1086,11 +1086,11 @@ class Thread { } void AtomicSetFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndOrSequentiallyConsistent(flag); + tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseOrSequentiallyConsistent(flag); } void AtomicClearFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.FetchAndAndSequentiallyConsistent(-1 ^ flag); + tls32_.state_and_flags.as_atomic_int.FetchAndBitwiseAndSequentiallyConsistent(-1 ^ flag); } void ResetQuickAllocEntryPointsForThread(bool is_marking); diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index b922d94617..9673eee795 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "thread.h" + #include <signal.h> -#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 9f553147c4..8095ef57c7 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), @@ -364,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 { @@ -377,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 cffaffc045..386cdf006a 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -22,11 +22,11 @@ #include <pthread.h> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #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 548752e980..62bdde6790 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -17,6 +17,8 @@ #include "agent.h" #include "android-base/stringprintf.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativeloader/native_loader.h" #include "base/strlcpy.h" #include "java_vm_ext.h" @@ -33,31 +35,58 @@ const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad"; const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach"; const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; +AgentSpec::AgentSpec(const std::string& arg) { + size_t eq = arg.find_first_of('='); + if (eq == std::string::npos) { + name_ = arg; + } else { + name_ = arg.substr(0, eq); + args_ = arg.substr(eq + 1, arg.length()); + } +} + +std::unique_ptr<Agent> AgentSpec::Load(/*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg) { + VLOG(agents) << "Loading agent: " << name_ << " " << args_; + return DoLoadHelper(nullptr, false, nullptr, call_res, error, error_msg); +} + +// Tries to attach the agent using its OnAttach method. Returns true on success. +std::unique_ptr<Agent> AgentSpec::Attach(JNIEnv* env, + jobject class_loader, + /*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg) { + VLOG(agents) << "Attaching agent: " << name_ << " " << args_; + return DoLoadHelper(env, true, class_loader, call_res, error, error_msg); +} + + // TODO We need to acquire some locks probably. -Agent::LoadError Agent::DoLoadHelper(bool attaching, - /*out*/jint* call_res, - /*out*/std::string* error_msg) { +std::unique_ptr<Agent> AgentSpec::DoLoadHelper(JNIEnv* env, + bool attaching, + jobject class_loader, + /*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg) { ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative); DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); - if (IsStarted()) { - *error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str()); - VLOG(agents) << "err: " << *error_msg; - return kAlreadyStarted; - } - LoadError err = DoDlOpen(error_msg); - if (err != kNoError) { + std::unique_ptr<Agent> agent = DoDlOpen(env, class_loader, error, error_msg); + if (agent == nullptr) { VLOG(agents) << "err: " << *error_msg; - return err; + return nullptr; } - AgentOnLoadFunction callback = attaching ? onattach_ : onload_; + AgentOnLoadFunction callback = attaching ? agent->onattach_ : agent->onload_; if (callback == nullptr) { *error_msg = StringPrintf("Unable to start agent %s: No %s callback found", (attaching ? "attach" : "load"), name_.c_str()); VLOG(agents) << "err: " << *error_msg; - return kLoadingError; + *error = kLoadingError; + return nullptr; } // Need to let the function fiddle with the array. std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]); @@ -70,44 +99,58 @@ Agent::LoadError Agent::DoLoadHelper(bool attaching, *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d", name_.c_str(), *call_res); VLOG(agents) << "err: " << *error_msg; - return kInitializationError; - } else { - return kNoError; + *error = kInitializationError; + return nullptr; } + return agent; } -void* Agent::FindSymbol(const std::string& name) const { - CHECK(IsStarted()) << "Cannot find symbols in an unloaded agent library " << this; - return dlsym(dlopen_handle_, name.c_str()); -} - -Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) { +std::unique_ptr<Agent> AgentSpec::DoDlOpen(JNIEnv* env, + jobject class_loader, + /*out*/LoadError* error, + /*out*/std::string* error_msg) { DCHECK(error_msg != nullptr); - DCHECK(dlopen_handle_ == nullptr); - DCHECK(onload_ == nullptr); - DCHECK(onattach_ == nullptr); - DCHECK(onunload_ == nullptr); - - dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY); - if (dlopen_handle_ == nullptr) { + ScopedLocalRef<jstring> library_path(env, + class_loader == nullptr + ? nullptr + : JavaVMExt::GetLibrarySearchPath(env, class_loader)); + + bool needs_native_bridge = false; + void* dlopen_handle = android::OpenNativeLibrary(env, + Runtime::Current()->GetTargetSdkVersion(), + name_.c_str(), + class_loader, + library_path.get(), + &needs_native_bridge, + error_msg); + if (dlopen_handle == nullptr) { *error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror()); - return kLoadingError; - } - - onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME)); - if (onload_ == nullptr) { - VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this; - } - onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME)); - if (onattach_ == nullptr) { - VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this; + *error = kLoadingError; + return nullptr; } - onunload_ = reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME)); - if (onunload_ == nullptr) { - VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this; + if (needs_native_bridge) { + // TODO: Consider support? + android::CloseNativeLibrary(dlopen_handle, needs_native_bridge); + *error_msg = StringPrintf("Native-bridge agents unsupported: %s", name_.c_str()); + *error = kLoadingError; + return nullptr; } - return kNoError; + + std::unique_ptr<Agent> agent(new Agent(name_, dlopen_handle)); + agent->PopulateFunctions(); + *error = kNoError; + return agent; +} + +std::ostream& operator<<(std::ostream &os, AgentSpec const& m) { + return os << "AgentSpec { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\" }"; +} + + +void* Agent::FindSymbol(const std::string& name) const { + CHECK(dlopen_handle_ != nullptr) << "Cannot find symbols in an unloaded agent library " << this; + return dlsym(dlopen_handle_, name.c_str()); } // TODO Lock some stuff probably. @@ -116,8 +159,9 @@ void Agent::Unload() { if (onunload_ != nullptr) { onunload_(Runtime::Current()->GetJavaVM()); } - // Don't actually dlclose since some agents assume they will never get unloaded. Since this only - // happens when the runtime is shutting down anyway this isn't a big deal. + // Don't actually android::CloseNativeLibrary since some agents assume they will never get + // unloaded. Since this only happens when the runtime is shutting down anyway this isn't a big + // deal. dlopen_handle_ = nullptr; onload_ = nullptr; onattach_ = nullptr; @@ -127,58 +171,6 @@ void Agent::Unload() { } } -Agent::Agent(const std::string& arg) - : dlopen_handle_(nullptr), - onload_(nullptr), - onattach_(nullptr), - onunload_(nullptr) { - size_t eq = arg.find_first_of('='); - if (eq == std::string::npos) { - name_ = arg; - } else { - name_ = arg.substr(0, eq); - args_ = arg.substr(eq + 1, arg.length()); - } -} - -Agent::Agent(const Agent& other) - : dlopen_handle_(nullptr), - onload_(nullptr), - onattach_(nullptr), - onunload_(nullptr) { - *this = other; -} - -// Attempting to copy to/from loaded/started agents is a fatal error -Agent& Agent::operator=(const Agent& other) { - if (this != &other) { - if (other.dlopen_handle_ != nullptr) { - LOG(FATAL) << "Attempting to copy a loaded agent!"; - } - - if (dlopen_handle_ != nullptr) { - LOG(FATAL) << "Attempting to assign into a loaded agent!"; - } - - DCHECK(other.onload_ == nullptr); - DCHECK(other.onattach_ == nullptr); - DCHECK(other.onunload_ == nullptr); - - DCHECK(onload_ == nullptr); - DCHECK(onattach_ == nullptr); - DCHECK(onunload_ == nullptr); - - name_ = other.name_; - args_ = other.args_; - - dlopen_handle_ = nullptr; - onload_ = nullptr; - onattach_ = nullptr; - onunload_ = nullptr; - } - return *this; -} - Agent::Agent(Agent&& other) : dlopen_handle_(nullptr), onload_(nullptr), @@ -190,10 +182,9 @@ Agent::Agent(Agent&& other) Agent& Agent::operator=(Agent&& other) { if (this != &other) { if (dlopen_handle_ != nullptr) { - dlclose(dlopen_handle_); + Unload(); } name_ = std::move(other.name_); - args_ = std::move(other.args_); dlopen_handle_ = other.dlopen_handle_; onload_ = other.onload_; onattach_ = other.onattach_; @@ -206,9 +197,24 @@ Agent& Agent::operator=(Agent&& other) { return *this; } +void Agent::PopulateFunctions() { + onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME)); + if (onload_ == nullptr) { + VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this; + } + onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME)); + if (onattach_ == nullptr) { + VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this; + } + onunload_ = reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME)); + if (onunload_ == nullptr) { + VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this; + } +} + Agent::~Agent() { if (dlopen_handle_ != nullptr) { - dlclose(dlopen_handle_); + Unload(); } } @@ -217,8 +223,7 @@ std::ostream& operator<<(std::ostream &os, const Agent* m) { } std::ostream& operator<<(std::ostream &os, Agent const& m) { - return os << "Agent { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\", handle=" - << m.dlopen_handle_ << " }"; + return os << "Agent { name=\"" << m.name_ << "\", handle=" << m.dlopen_handle_ << " }"; } } // namespace ti diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h index d6f1f2ef06..24a6f1ce6a 100644 --- a/runtime/ti/agent.h +++ b/runtime/ti/agent.h @@ -20,34 +20,24 @@ #include <dlfcn.h> #include <jni.h> // for jint, JavaVM* etc declarations +#include <memory> + #include "base/logging.h" namespace art { namespace ti { -using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*); -using AgentOnUnloadFunction = void (*)(JavaVM*); +class Agent; -// Agents are native libraries that will be loaded by the runtime for the purpose of -// instrumentation. They will be entered by Agent_OnLoad or Agent_OnAttach depending on whether the -// agent is being attached during runtime startup or later. -// -// The agent's Agent_OnUnload function will be called during runtime shutdown. -// -// TODO: consider splitting ti::Agent into command line, agent and shared library handler classes -// TODO Support native-bridge. Currently agents can only be the actual runtime ISA of the device. -class Agent { +enum LoadError { + kNoError, // No error occurred.. + kLoadingError, // dlopen or dlsym returned an error. + kInitializationError, // The entrypoint did not return 0. This might require an abort. +}; + +class AgentSpec { public: - enum LoadError { - kNoError, // No error occurred.. - kAlreadyStarted, // The agent has already been loaded. - kLoadingError, // dlopen or dlsym returned an error. - kInitializationError, // The entrypoint did not return 0. This might require an abort. - }; - - bool IsStarted() const { - return dlopen_handle_ != nullptr; - } + explicit AgentSpec(const std::string& arg); const std::string& GetName() const { return name_; @@ -61,26 +51,59 @@ class Agent { return !GetArgs().empty(); } - void* FindSymbol(const std::string& name) const; + std::unique_ptr<Agent> Load(/*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg); - LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg) { - VLOG(agents) << "Loading agent: " << name_ << " " << args_; - return DoLoadHelper(false, call_res, error_msg); - } + // Tries to attach the agent using its OnAttach method. Returns true on success. + std::unique_ptr<Agent> Attach(JNIEnv* env, + jobject class_loader, + /*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg); - // TODO We need to acquire some locks probably. - void Unload(); + private: + std::unique_ptr<Agent> DoDlOpen(JNIEnv* env, + jobject class_loader, + /*out*/LoadError* error, + /*out*/std::string* error_msg); + + std::unique_ptr<Agent> DoLoadHelper(JNIEnv* env, + bool attaching, + jobject class_loader, + /*out*/jint* call_res, + /*out*/LoadError* error, + /*out*/std::string* error_msg); - // Tries to attach the agent using its OnAttach method. Returns true on success. - LoadError Attach(/*out*/jint* call_res, /*out*/std::string* error_msg) { - VLOG(agents) << "Attaching agent: " << name_ << " " << args_; - return DoLoadHelper(true, call_res, error_msg); + std::string name_; + std::string args_; + + friend std::ostream& operator<<(std::ostream &os, AgentSpec const& m); +}; + +std::ostream& operator<<(std::ostream &os, AgentSpec const& m); + +using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*); +using AgentOnUnloadFunction = void (*)(JavaVM*); + +// Agents are native libraries that will be loaded by the runtime for the purpose of +// instrumentation. They will be entered by Agent_OnLoad or Agent_OnAttach depending on whether the +// agent is being attached during runtime startup or later. +// +// The agent's Agent_OnUnload function will be called during runtime shutdown. +// +// TODO: consider splitting ti::Agent into command line, agent and shared library handler classes +// TODO Support native-bridge. Currently agents can only be the actual runtime ISA of the device. +class Agent { + public: + const std::string& GetName() const { + return name_; } - explicit Agent(const std::string& arg); + void* FindSymbol(const std::string& name) const; - Agent(const Agent& other); - Agent& operator=(const Agent& other); + // TODO We need to acquire some locks probably. + void Unload(); Agent(Agent&& other); Agent& operator=(Agent&& other); @@ -88,14 +111,17 @@ class Agent { ~Agent(); private: - LoadError DoDlOpen(/*out*/std::string* error_msg); + Agent(const std::string& name, void* dlopen_handle) : name_(name), + dlopen_handle_(dlopen_handle), + onload_(nullptr), + onattach_(nullptr), + onunload_(nullptr) { + DCHECK(dlopen_handle != nullptr); + } - LoadError DoLoadHelper(bool attaching, - /*out*/jint* call_res, - /*out*/std::string* error_msg); + void PopulateFunctions(); std::string name_; - std::string args_; void* dlopen_handle_; // The entrypoints. @@ -103,7 +129,10 @@ class Agent { AgentOnLoadFunction onattach_; AgentOnUnloadFunction onunload_; + friend class AgentSpec; friend std::ostream& operator<<(std::ostream &os, Agent const& m); + + DISALLOW_COPY_AND_ASSIGN(Agent); }; std::ostream& operator<<(std::ostream &os, Agent const& m); diff --git a/runtime/trace.cc b/runtime/trace.cc index a113ab5cc8..f9d22df543 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -31,7 +31,7 @@ #include "class_linker.h" #include "common_throws.h" #include "debugger.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/scoped_gc_critical_section.h" #include "instrumentation.h" @@ -937,7 +937,7 @@ void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method, overflow_ = true; return; } - } while (!cur_offset_.CompareExchangeWeakSequentiallyConsistent(old_offset, new_offset)); + } while (!cur_offset_.CompareAndSetWeakSequentiallyConsistent(old_offset, new_offset)); } TraceAction action = kTraceMethodEnter; diff --git a/runtime/transaction.cc b/runtime/transaction.cc index e923aff439..c9766bc9ca 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -16,7 +16,8 @@ #include "transaction.h" -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/stl_util.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" diff --git a/runtime/transaction.h b/runtime/transaction.h index 4e9cde521f..8539ebc1d6 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -20,7 +20,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/value_object.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "gc_root.h" #include "offsets.h" #include "primitive.h" diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index e52dd08540..02e61d76a1 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -20,7 +20,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" #include "common_runtime_test.h" -#include "dex_file.h" +#include "dex/dex_file.h" #include "mirror/array-inl.h" #include "scoped_thread_state_change-inl.h" @@ -66,7 +66,7 @@ class TransactionTest : public CommonRuntimeTest { class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); - mirror::Class::Status old_status = h_klass->GetStatus(); + ClassStatus old_status = h_klass->GetStatus(); LockWord old_lock_word = h_klass->GetLockWord(false); Runtime::Current()->EnterTransactionMode(); @@ -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<mirror::String> 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_lookup_table.cc b/runtime/type_lookup_table.cc index 4fab39cd73..6eb3d83631 100644 --- a/runtime/type_lookup_table.cc +++ b/runtime/type_lookup_table.cc @@ -20,7 +20,7 @@ #include <memory> #include "base/bit_utils.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "utf-inl.h" #include "utils.h" diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h index 780b3804e0..6a6f47fba2 100644 --- a/runtime/type_lookup_table.h +++ b/runtime/type_lookup_table.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ #define ART_RUNTIME_TYPE_LOOKUP_TABLE_H_ -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "leb128.h" #include "utf.h" diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc index 0f8f2880fe..d04652a8e7 100644 --- a/runtime/type_lookup_table_test.cc +++ b/runtime/type_lookup_table_test.cc @@ -19,7 +19,7 @@ #include <memory> #include "common_runtime_test.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "scoped_thread_state_change-inl.h" #include "utf-inl.h" diff --git a/runtime/type_reference.h b/runtime/type_reference.h index f7daa2bd58..2b0b99f75e 100644 --- a/runtime/type_reference.h +++ b/runtime/type_reference.h @@ -19,8 +19,9 @@ #include <stdint.h> -#include "base/logging.h" -#include "dex_file_types.h" +#include <android-base/logging.h> + +#include "dex/dex_file_types.h" #include "string_reference.h" namespace art { diff --git a/runtime/utf.cc b/runtime/utf.cc index 7e06482635..93fcb32136 100644 --- a/runtime/utf.cc +++ b/runtime/utf.cc @@ -16,7 +16,8 @@ #include "utf.h" -#include "base/logging.h" +#include <android-base/logging.h> + #include "mirror/array.h" #include "mirror/object-inl.h" #include "utf-inl.h" diff --git a/runtime/utils.cc b/runtime/utils.cc index f6533a7130..bd4175f5fd 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -30,7 +30,7 @@ #include "android-base/strings.h" #include "base/file_utils.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "os.h" #include "utf-inl.h" diff --git a/runtime/utils.h b/runtime/utils.h index ede32dc57a..789498ce09 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -23,9 +23,10 @@ #include <random> #include <string> +#include <android-base/logging.h> + #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 9d4e9fb96c..855b856187 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 <android-base/logging.h> + #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/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h index fc0415957d..6f689f334a 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_ #define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_ -#include "dex_file.h" -#include "dex_file_types.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" namespace art { diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index fb9d24f9bc..a53556ffcc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -20,12 +20,13 @@ #include <memory> +#include <android-base/logging.h> + #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" -#include "dex_file_loader.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" #include "dex_to_dex_decompiler.h" namespace art { @@ -253,6 +254,7 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, quickening_info)); } optimizer::ArtDecompileDEX( + target_dex_file, *code_item, GetQuickeningInfoAt(quickening_info, quickening_offset), decompile_return_instruction); diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 3e0882693a..2d9fcab59c 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -68,6 +68,18 @@ 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: @@ -172,17 +184,13 @@ class VdexFile { } 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<MemMap> mmap_; diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index a7fa9f34d1..445a6ff7de 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 <android-base/logging.h> + #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c6ba2f7e43..2183b60b1e 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -22,17 +22,19 @@ #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" #include "base/time_utils.h" #include "class_linker.h" #include "compiler_callbacks.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" -#include "dex_instruction_utils.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_instruction-inl.h" +#include "dex/dex_instruction_utils.h" #include "experimental_flags.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" @@ -46,6 +48,7 @@ #include "mirror/method_type.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/var_handle.h" #include "reg_type-inl.h" #include "register_line-inl.h" #include "runtime.h" @@ -61,8 +64,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 +232,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, previous_method_idx = method_idx; InvokeType type = it->GetMethodInvokeType(class_def); ArtMethod* method = linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *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 +285,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 +352,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 +409,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 +486,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 +496,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 +569,7 @@ MethodVerifier::MethodVerifier(Thread* self, dex_cache_(dex_cache), class_loader_(class_loader), class_def_(class_def), - code_item_accessor_(CodeItemDataAccessor::CreateNullable(dex_file, code_item)), + code_item_accessor_(dex_file, code_item), declaring_class_(nullptr), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(nullptr), @@ -832,7 +837,7 @@ bool MethodVerifier::Verify() { return false; } else { uint32_t access_flag_options = kAccPublic; - if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + if (dex_file_->SupportsDefaultMethods()) { access_flag_options |= kAccPrivate; } if (!(method_access_flags_ & access_flag_options)) { @@ -1074,9 +1079,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<mirror::Class> exception_type = + linker->ResolveType(iterator.GetHandlerTypeIndex(), dex_cache_, class_loader_); if (exception_type == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -1935,7 +1939,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 @@ -2079,7 +2083,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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"; @@ -2237,7 +2241,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); @@ -2431,8 +2435,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClass<CheckAccess::kYes>(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get()); + ObjPtr<mirror::Class> 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 " @@ -2485,7 +2489,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<LockOp::kClear>(this, @@ -2592,7 +2597,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; @@ -2632,7 +2637,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(); @@ -2717,7 +2722,7 @@ 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_accessor_.RegistersSize(), @@ -3005,7 +3010,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; } @@ -3082,7 +3087,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()) { @@ -3137,6 +3142,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } if (!CheckSignaturePolymorphicMethod(called_method) || !CheckSignaturePolymorphicReceiver(inst)) { + DCHECK(HasFailures()); break; } const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); @@ -3627,7 +3633,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { bool has_catch_all_handler = false; 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_)); + CatchHandlerIterator iterator(code_item_accessor_, *try_item); // Need the linker to try and resolve the handled class to check if it's Throwable. ClassLinker* linker = Runtime::Current()->GetClassLinker(); @@ -3638,8 +3644,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<mirror::Class> klass = + linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { if (klass == mirror::Throwable::GetJavaLangThrowable()) { has_catch_all_handler = true; @@ -3757,16 +3763,16 @@ void MethodVerifier::UninstantiableError(const char* descriptor) { << "non-instantiable klass " << descriptor; } -inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { +inline bool MethodVerifier::IsInstantiableOrPrimitive(ObjPtr<mirror::Class> klass) { return klass->IsInstantiable() || klass->IsPrimitive(); } template <MethodVerifier::CheckAccess C> 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<mirror::Class> 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(); @@ -3779,10 +3785,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); @@ -3797,7 +3803,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 @@ -3961,10 +3967,10 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( // while this check implies an IncompatibleClassChangeError. if (klass->IsInterface()) { // methods called on interfaces should be invoke-interface, invoke-super, invoke-direct (if - // dex file version is 37 or greater), or invoke-static. + // default methods are supported for the dex file), or invoke-static. if (method_type != METHOD_INTERFACE && method_type != METHOD_STATIC && - ((dex_file_->GetVersion() < DexFile::kDefaultMethodsVersion) || + (!dex_file_->SupportsDefaultMethods() || method_type != METHOD_DIRECT) && method_type != METHOD_SUPER) { Fail(VERIFY_ERROR_CLASS_CHANGE) @@ -4081,7 +4087,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 @@ -4414,14 +4420,20 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { mirror::Class* klass = method->GetDeclaringClass(); - if (klass != mirror::MethodHandle::StaticClass()) { + const char* method_name = method->GetName(); + + const char* expected_return_descriptor; + if (klass == mirror::MethodHandle::StaticClass()) { + expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); + } else if (klass == mirror::VarHandle::StaticClass()) { + expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); + } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "Signature polymorphic method must be declared in java.lang.invoke.MethodClass"; + << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor(); return false; } - const char* method_name = method->GetName(); - if (strcmp(method_name, "invoke") != 0 && strcmp(method_name, "invokeExact") != 0) { + if (expected_return_descriptor == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Signature polymorphic method name invalid: " << method_name; return false; @@ -4443,9 +4455,10 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { } const char* return_descriptor = method->GetReturnTypeDescriptor(); - if (strcmp(return_descriptor, "Ljava/lang/Object;") != 0) { + if (strcmp(return_descriptor, expected_return_descriptor) != 0) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "Signature polymorphic method has unexpected return type: " << return_descriptor; + << "Signature polymorphic method has unexpected return type: " << return_descriptor + << " != " << expected_return_descriptor; return false; } @@ -4454,7 +4467,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()) { @@ -4472,9 +4485,10 @@ bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) << "invoke-polymorphic receiver has no class: " << this_type; return false; - } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass())) { + } else if (!this_type.GetClass()->IsSubClass(mirror::MethodHandle::StaticClass()) && + !this_type.GetClass()->IsSubClass(mirror::VarHandle::StaticClass())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "invoke-polymorphic receiver is not a subclass of MethodHandle: " + << "invoke-polymorphic receiver is not a subclass of MethodHandle or VarHandle: " << this_type; return false; } @@ -4573,7 +4587,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 = @@ -4689,13 +4703,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<LockOp::kClear>(this, inst->VRegA_23x(), reg_types_.Zero()); + // instruction type. + if (!is_primitive) { + work_line_->SetRegisterType<LockOp::kClear>(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<LockOp::kClear>( + 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<LockOp::kClear>(this, inst->VRegA_23x(), insn_type); } else { // Category 2 work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(), @@ -4804,7 +4825,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 @@ -4873,7 +4894,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); @@ -4914,7 +4935,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); @@ -4926,7 +4947,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()) { @@ -5038,7 +5059,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } ObjPtr<mirror::Class> 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(), @@ -5154,10 +5175,11 @@ ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterL } uint32_t field_offset = static_cast<uint32_t>(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; } @@ -5186,7 +5208,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy const RegType* field_type; { ObjPtr<mirror::Class> 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(), @@ -5341,7 +5363,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); } @@ -5349,7 +5371,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<void*>(work_insn_idx_) << "]" << " to [" << reinterpret_cast<void*>(next_insn) << "]: " << "\n" << copy->Dump(this) << " MERGE\n" diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c885914633..cadf4eb0ba 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -25,9 +25,9 @@ #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 "dex/code_item_accessors.h" +#include "dex/dex_file.h" +#include "dex/dex_file_types.h" #include "handle.h" #include "instruction_flags.h" #include "method_reference.h" @@ -127,10 +127,6 @@ class MethodVerifier { return *dex_file_; } - uint32_t DexFileVersion() const { - return dex_file_->GetVersion(); - } - RegTypeCache* GetRegTypeCache() { return ®_types_; } @@ -257,7 +253,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<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_); // Is the method being verified a constructor? See the comment on the field. bool IsConstructor() const { diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index d9874677e8..97c1b62abe 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -23,7 +23,7 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" #include "verifier_enums.h" diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 631c6bd7ef..f719782727 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<mirror::Class> 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 8df2e0f50b..7ebdd90fc0 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -22,7 +22,7 @@ #include "base/bit_vector-inl.h" #include "base/casts.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "method_verifier.h" #include "mirror/class-inl.h" #include "mirror/class.h" @@ -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 a2085a3f09..9055849ca0 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 197c97671e..61f34afdac 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<jbyte>::min(), false); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 0029eb90a3..5564684c4f 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -16,13 +16,16 @@ #include "reg_type_cache-inl.h" +#include <type_traits> + +#include "base/aborting.h" #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "base/stl_util.h" #include "class_linker-inl.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "reg_type-inl.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 <class Type> -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 <typename T> +struct TypeHelper { + using type = T; + static_assert(std::is_convertible<T*, RegType*>::value, "T must be a RegType"); + + const char* descriptor; + + explicit TypeHelper(const char* d) : descriptor(d) {} +}; +} // namespace void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { - CreatePrimitiveTypeInstance<UndefinedType>(""); - CreatePrimitiveTypeInstance<ConflictType>(""); - CreatePrimitiveTypeInstance<BooleanType>("Z"); - CreatePrimitiveTypeInstance<ByteType>("B"); - CreatePrimitiveTypeInstance<ShortType>("S"); - CreatePrimitiveTypeInstance<CharType>("C"); - CreatePrimitiveTypeInstance<IntegerType>("I"); - CreatePrimitiveTypeInstance<LongLoType>("J"); - CreatePrimitiveTypeInstance<LongHiType>("J"); - CreatePrimitiveTypeInstance<FloatType>("F"); - CreatePrimitiveTypeInstance<DoubleLoType>("D"); - CreatePrimitiveTypeInstance<DoubleHiType>("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<UndefinedType>("")); + create_primitive_type_instance(TypeHelper<ConflictType>("")); + create_primitive_type_instance(TypeHelper<NullType>("")); + create_primitive_type_instance(TypeHelper<BooleanType>("Z")); + create_primitive_type_instance(TypeHelper<ByteType>("B")); + create_primitive_type_instance(TypeHelper<ShortType>("S")); + create_primitive_type_instance(TypeHelper<CharType>("C")); + create_primitive_type_instance(TypeHelper<IntegerType>("I")); + create_primitive_type_instance(TypeHelper<LongLoType>("J")); + create_primitive_type_instance(TypeHelper<LongHiType>("J")); + create_primitive_type_instance(TypeHelper<FloatType>("F")); + create_primitive_type_instance(TypeHelper<DoubleLoType>("D")); + create_primitive_type_instance(TypeHelper<DoubleHiType>("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 d0907564e2..52776766bc 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 <class Type> - 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 1bc48ed71b..15a38f3fd7 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<const RegType*> uninitialized_types = { + &uninit_this, &uninit_obj_0, &uninit_obj_1, &uninit_number_0, &uninit_integer_0 + }; + std::vector<const RegType*> 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<const RegType*> uninit_unresolved_types = { + &uninit_unres_this, &uninit_unres_a_0, &uninit_unres_b_0 + }; + std::vector<const RegType*> plain_nonobj_classes = { &number, &integer }; + std::vector<const RegType*> plain_nonobj_arr_classes = { + &number_arr, + &number_arr_arr, + &integer_arr, + &char_arr, + }; + // std::vector<const RegType*> others = { &conflict, &zero, &null, &obj, &int_type }; + + std::vector<const RegType*> 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<const RegType*> 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<const RegType*> 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<Edge> 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<MergeExpectation> 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<const RegType*> seen; + std::queue<const RegType*> 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<const RegType*> grey; + auto compute_grey = [&](const RegType* cur) { + grey.insert(cur); // Mark discovered node as grey. + }; + bfs(compute_grey, r1); + + std::set<const RegType*> 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<const RegType*> 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 a9c9428581..39d73f54d8 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.cc b/runtime/verifier/register_line.cc index 34c406e5b0..ea30e05487 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -18,7 +18,7 @@ #include "android-base/stringprintf.h" -#include "dex_instruction-inl.h" +#include "dex/dex_instruction-inl.h" #include "method_verifier-inl.h" #include "reg_type-inl.h" #include "register_line-inl.h" diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 71eb4d6ac7..82f63b281a 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -20,6 +20,8 @@ #include <memory> #include <vector> +#include <android-base/logging.h> + #include "base/scoped_arena_containers.h" #include "safe_map.h" @@ -401,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/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 0481f24c45..7d8c5aae34 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "base/stl_util.h" #include "compiler_callbacks.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "indenter.h" #include "leb128.h" #include "mirror/class-inl.h" diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 4069a1188a..94441da7e2 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -23,7 +23,7 @@ #include "base/array_ref.h" #include "base/mutex.h" -#include "dex_file_types.h" +#include "dex/dex_file_types.h" #include "handle.h" #include "obj_ptr.h" #include "thread.h" diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 9722db9641..dc57f81a67 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -20,9 +20,9 @@ #include <sstream> -#include "android-base/stringprintf.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "jni_internal.h" #include "mirror/class.h" @@ -81,6 +81,7 @@ jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer; +jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath; jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization; jmethodID WellKnownClasses::java_lang_Boolean_valueOf; jmethodID WellKnownClasses::java_lang_Byte_valueOf; @@ -269,7 +270,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.<init>"; return 0; } @@ -325,6 +326,7 @@ void WellKnownClasses::Init(JNIEnv* env) { org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer"); + dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;"); dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V"); java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); @@ -406,7 +408,7 @@ void WellKnownClasses::LateInit(JNIEnv* env) { // to make sure these JNI methods are available. java_lang_Runtime_nativeLoad = CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad", - "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)" + "(Ljava/lang/String;Ljava/lang/ClassLoader;)" "Ljava/lang/String;"); java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", @@ -465,6 +467,7 @@ void WellKnownClasses::Clear() { org_apache_harmony_dalvik_ddmc_Chunk = nullptr; org_apache_harmony_dalvik_ddmc_DdmServer = nullptr; + dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr; dalvik_system_VMRuntime_runFinalization = nullptr; java_lang_Boolean_valueOf = nullptr; java_lang_Byte_valueOf = nullptr; diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 3ebcc33171..024971ae3d 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -92,6 +92,7 @@ struct WellKnownClasses { static jclass org_apache_harmony_dalvik_ddmc_Chunk; static jclass org_apache_harmony_dalvik_ddmc_DdmServer; + static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath; static jmethodID dalvik_system_VMRuntime_runFinalization; static jmethodID java_lang_Boolean_valueOf; static jmethodID java_lang_Byte_valueOf; diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 821cc5ceb1..75f8757f6c 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -21,7 +21,8 @@ #include <memory> #include <string> -#include "base/logging.h" +#include <android-base/logging.h> + #include "base/unix_file/random_access_file.h" #include "globals.h" #include "mem_map.h" diff --git a/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc index 939d2e287f..a64bd0bc0b 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 <android-base/logging.h> using namespace vixl::aarch64; // NOLINT(build/namespaces) diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc index a5f05dc8fc..9f52b320f2 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 31a915e4f1..a21971508a 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 <android-base/logging.h> + #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 1d05160883..b09b9a22eb 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.<init> Super.<init> Subclass.<init> diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index bc5a0a64e8..33a8f5bba2 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -20,8 +20,10 @@ #include <iostream> #include <vector> +#include <android-base/logging.h> + #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 871107c56b..f94dcf6c70 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/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java index a9e0faf500..6ad160c1a6 100644 --- a/test/004-ThreadStress/src/Main.java +++ b/test/004-ThreadStress/src/Main.java @@ -267,6 +267,15 @@ public class Main implements Runnable { semaphore.acquire(); permitAcquired = true; Thread.sleep(SLEEP_TIME); + } catch (OutOfMemoryError ignored) { + // The call to semaphore.acquire() above may trigger an OOME, + // despite the care taken doing some warm-up by forcing + // ahead-of-time initialization of classes used by the Semaphore + // class (see forceTransitiveClassInitialization below). + // For instance, one of the code paths executes + // AbstractQueuedSynchronizer.addWaiter, which allocates an + // AbstractQueuedSynchronizer$Node (see b/67730573). + // In that case, just ignore the OOME and continue. } catch (InterruptedException ignored) { } finally { if (permitAcquired) { diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java index 89fe016856..008576acc3 100644 --- a/test/008-exceptions/src/Main.java +++ b/test/008-exceptions/src/Main.java @@ -158,8 +158,8 @@ public class Main { t.printStackTrace(System.out); } try { - // Before splitting mirror::Class::kStatusError into - // kStatusErrorUnresolved and kStatusErrorResolved, + // Before splitting ClassStatus::kError into + // ClassStatus::kErrorUnresolved and ClassStatus::kErrorResolved, // this would trigger a // CHECK(super_class->IsResolved()) // failure in @@ -188,8 +188,8 @@ public class Main { } catch (Throwable t) { t.printStackTrace(System.out); } - // Before splitting mirror::Class::kStatusError into - // kStatusErrorUnresolved and kStatusErrorResolved, + // Before splitting ClassStatus::kError into + // ClassStatus::kErrorUnresolved and ClassStatus::kErrorResolved, // the exception from wrapper 1 would have been // wrapped in NoClassDefFoundError but the exception // from wrapper 2 would have been unwrapped. 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 0000000000..ea767efdba --- /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 ; <anonymous> <not a member> + +.field i I + +.method <init>()V + .limit stack 2 + .limit locals 1 + .line 112 + 0: aload_0 + 1: invokespecial OtherClass/<init>()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 8489a2c222..f55a34c5f2 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 f168719bf5..f3178f9c2a 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 <android-base/logging.h> namespace art { diff --git a/test/070-nio-buffer/src/Main.java b/test/070-nio-buffer/src/Main.java index a3eeb3fda6..86eb553594 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/085-old-style-inner-class/build b/test/085-old-style-inner-class/build deleted file mode 100644 index 21dc66269d..0000000000 --- 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 0000000000..fde1ddea53 --- /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 <init>()V + .limit stack 1 + .limit locals 1 + .line 23 + aload_0 + invokespecial java/lang/Object/<init>()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 0000000000..dedbe8682e --- /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 <init>()V + .limit stack 1 + .limit locals 1 + .line 28 + aload_0 + invokespecial java/lang/Object/<init>()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 c9a5b72dbd..831364cae1 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 b8d72f66f8..f7801de62f 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 7f88086986..873429e076 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 90ad3155ca..e0d829a0d6 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<Object> l = new ArrayList<Object>(); + 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/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 2248fc4efd..7c382588a4 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -15,7 +15,7 @@ */ #include "class_linker.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "gc/heap.h" #include "gc/space/image_space.h" #include "mirror/class-inl.h" diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index 7d40f5773d..f01b82553d 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<JNIEnvExt*>(env)->vm; + JavaVM* vm = down_cast<JNIEnvExt*>(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 66270fd5c7..ef758e86e1 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -25,11 +25,11 @@ #include "jni.h" +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <backtrace/Backtrace.h> -#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" diff --git a/test/137-cfi/run b/test/137-cfi/run index ebc729bc74..adea71a07f 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/150-loadlibrary/src/Main.java b/test/150-loadlibrary/src/Main.java index 908693760f..ec96221d1e 100644 --- a/test/150-loadlibrary/src/Main.java +++ b/test/150-loadlibrary/src/Main.java @@ -47,7 +47,7 @@ public class Main { // Then call an internal function that accepts the classloader. Do not use load(), as it // is deprecated and only there for backwards compatibility, and prints a warning to the // log that we'd have to strip (it contains the pid). - Method m = Runtime.class.getDeclaredMethod("doLoad", String.class, ClassLoader.class); + Method m = Runtime.class.getDeclaredMethod("nativeLoad", String.class, ClassLoader.class); m.setAccessible(true); Object result = m.invoke(Runtime.getRuntime(), fileName, bootClassLoader); if (result != null) { diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java index 9fe47c8b16..de5890cbfa 100644 --- a/test/151-OpenFileLimit/src/Main.java +++ b/test/151-OpenFileLimit/src/Main.java @@ -55,8 +55,11 @@ public class Main { System.out.println(e.getMessage()); } - for (int i = 0; i < files.size(); i++) { + for (int i = 0; i < streams.size(); i++) { streams.get(i).close(); + } + + for (int i = 0; i < files.size(); i++) { files.get(i).delete(); } System.out.println("done."); diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check new file mode 100755 index 0000000000..d2c03841fc --- /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/expected.txt b/test/1940-ddms-ext/expected.txt index 62d3b7bd4c..1a457a01a5 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -3,8 +3,19 @@ MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, 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/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java index 9f79eaebba..226fe350bd 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -30,6 +30,8 @@ public class Test1940 { 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; @@ -58,19 +60,27 @@ public class Test1940 { public void connected() {} public void disconnected() {} public Chunk handleChunk(Chunk req) { - // For this test we will simply calculate the checksum - checkEq(req.type, MY_DDMS_TYPE); System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); - 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; + 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); + } } } @@ -113,18 +123,42 @@ public class Test1940 { 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) -> { diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc index e8fcc775e9..f973abe28b 100644 --- a/test/1941-dispose-stress/dispose_stress.cc +++ b/test/1941-dispose-stress/dispose_stress.cc @@ -30,6 +30,17 @@ namespace art { namespace Test1941DisposeStress { +extern "C" JNIEXPORT void JNICALL Java_art_Test1941_setTracingOn(JNIEnv* env, + jclass, + jthread thr, + jboolean enable) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE, + JVMTI_EVENT_SINGLE_STEP, + thr)); +} + extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { JavaVM* vm = nullptr; if (env->GetJavaVM(&vm) != 0) { diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java index d5a9de6cab..29cea6bdb8 100644 --- a/test/1941-dispose-stress/src/art/Test1941.java +++ b/test/1941-dispose-stress/src/art/Test1941.java @@ -19,6 +19,7 @@ package art; import java.util.Arrays; import java.lang.reflect.Executable; import java.lang.reflect.Method; +import java.util.concurrent.Semaphore; public class Test1941 { public static final boolean PRINT_CNT = false; @@ -41,7 +42,8 @@ public class Test1941 { // Don't bother actually doing anything. } - public static void LoopAllocFreeEnv() { + public static void LoopAllocFreeEnv(Semaphore sem) { + sem.release(); while (!Thread.interrupted()) { CNT++; long env = AllocEnv(); @@ -52,18 +54,25 @@ public class Test1941 { public static native long AllocEnv(); public static native void FreeEnv(long env); + public static native void setTracingOn(Thread thr, boolean enable); + public static void run() throws Exception { - Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); + final Semaphore sem = new Semaphore(0); + Thread thr = new Thread(() -> { LoopAllocFreeEnv(sem); }, "LoopNative"); thr.start(); + // Make sure the other thread is actually started. + sem.acquire(); Trace.enableSingleStepTracing(Test1941.class, Test1941.class.getDeclaredMethod( "notifySingleStep", Thread.class, Executable.class, Long.TYPE), - null); + thr); + setTracingOn(Thread.currentThread(), true); System.out.println("fib(20) is " + fib(20)); thr.interrupt(); thr.join(); + setTracingOn(Thread.currentThread(), false); Trace.disableTracing(null); if (PRINT_CNT) { System.out.println("Number of envs created/destroyed: " + CNT); diff --git a/test/1942-suspend-raw-monitor-exit/expected.txt b/test/1942-suspend-raw-monitor-exit/expected.txt new file mode 100644 index 0000000000..182e197c05 --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/expected.txt @@ -0,0 +1,11 @@ +Initial state. +isLocked() = true +isSuspended(target_thread) = false +Suspend and sleep. +isLocked() = true +isSuspended(target_thread) = true +Let other thread release the raw monitor. +isLocked() = false +isSuspended(target_thread) = true +other thread doesn't hold lock! +resumed test thread diff --git a/test/1942-suspend-raw-monitor-exit/info.txt b/test/1942-suspend-raw-monitor-exit/info.txt new file mode 100644 index 0000000000..f608e0685a --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/info.txt @@ -0,0 +1,3 @@ +Tests jvmti suspending of a thread that is interacting with a raw monitor. + +Makes sure that the RawMonitorExit function does not act as a suspend point. diff --git a/test/1942-suspend-raw-monitor-exit/native_suspend_monitor.cc b/test/1942-suspend-raw-monitor-exit/native_suspend_monitor.cc new file mode 100644 index 0000000000..b182ad0ecb --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/native_suspend_monitor.cc @@ -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. + */ + +#include <atomic> + +#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 Test1942SuspendRawMonitorExit { + +jrawMonitorID mon; +std::atomic<bool> should_pause(true); +std::atomic<bool> paused(false); +std::atomic<bool> done(false); +std::atomic<bool> locked(false); + +extern "C" JNIEXPORT void JNICALL Java_art_Test1942_nativeRun(JNIEnv* env, jclass) { + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->CreateRawMonitor("Test1942 monitor", &mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + locked.store(true); + while (should_pause.load()) { + paused.store(true); + } + paused.store(false); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } + locked.store(false); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_art_Test1942_isLocked(JNIEnv*, jclass) { + return locked.load(); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1942_waitForPause(JNIEnv*, jclass) { + while (!paused.load()) { } +} +extern "C" JNIEXPORT void JNICALL Java_art_Test1942_resume(JNIEnv*, jclass) { + should_pause.store(false); + while (paused.load()) { } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1942_grabRawMonitor(JNIEnv* env, jclass) { + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } +} + +} // namespace Test1942SuspendRawMonitorExit +} // namespace art diff --git a/test/1942-suspend-raw-monitor-exit/run b/test/1942-suspend-raw-monitor-exit/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/run @@ -0,0 +1,17 @@ +#!/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-run "$@" --jvmti diff --git a/test/1942-suspend-raw-monitor-exit/src/Main.java b/test/1942-suspend-raw-monitor-exit/src/Main.java new file mode 100644 index 0000000000..0f75bc62d4 --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/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.Test1942.run(); + } +} diff --git a/runtime/cdex/compact_dex_level.h b/test/1942-suspend-raw-monitor-exit/src/art/Suspension.java index b824462bf0..16e62ccac9 100644 --- a/runtime/cdex/compact_dex_level.h +++ b/test/1942-suspend-raw-monitor-exit/src/art/Suspension.java @@ -14,21 +14,17 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ -#define ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ +package art; -#include "dex_file.h" +public class Suspension { + // Suspends a thread using jvmti. + public native static void suspend(Thread thr); -namespace art { + // Resumes a thread using jvmti. + public native static void resume(Thread thr); -// Optimization level for compact dex generation. -enum class CompactDexLevel { - // Level none means not generated. - kCompactDexLevelNone, - // Level fast means optimizations that don't take many resources to perform. - kCompactDexLevelFast, -}; + public native static boolean isSuspended(Thread thr); -} // namespace art - -#endif // ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ + public native static int[] suspendList(Thread... threads); + public native static int[] resumeList(Thread... threads); +} diff --git a/test/1942-suspend-raw-monitor-exit/src/art/Test1942.java b/test/1942-suspend-raw-monitor-exit/src/art/Test1942.java new file mode 100644 index 0000000000..eaf756067b --- /dev/null +++ b/test/1942-suspend-raw-monitor-exit/src/art/Test1942.java @@ -0,0 +1,73 @@ +/* + * 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 Test1942 { + public static void run() throws Exception { + final Thread target_thread = new Thread(() -> { + nativeRun(); + }, "target_thread"); + + target_thread.start(); + + // wait for the other thread to spin holding lock. + waitForPause(); + + System.out.println("Initial state."); + System.out.println("isLocked() = " + isLocked()); + System.out.println("isSuspended(target_thread) = " + Suspension.isSuspended(target_thread)); + + // Suspend it from java. + System.out.println("Suspend and sleep."); + Suspension.suspend(target_thread); + // Wait for the other thread to do something. + try { Thread.sleep(1000); } catch (Exception e) {} + + System.out.println("isLocked() = " + isLocked()); + System.out.println("isSuspended(target_thread) = " + Suspension.isSuspended(target_thread)); + + // Let it try to unlock the monitor. + System.out.println("Let other thread release the raw monitor."); + // Let the thread try to lock the monitor. + resume(); + + // Wait for the other thread to do something. It should exit by the time this is done if it + // has not hit a suspend point. + while (isLocked()) { + try { Thread.sleep(1000); } catch (Exception e) {} + } + + System.out.println("isLocked() = " + isLocked()); + System.out.println("isSuspended(target_thread) = " + Suspension.isSuspended(target_thread)); + + // Make sure the monitor is gone. + grabRawMonitor(); + System.out.println("other thread doesn't hold lock!"); + + // Resume it from java + System.out.println("resumed test thread"); + Suspension.resume(target_thread); + target_thread.join(); + } + + public static native void nativeRun(); + public static native void waitForPause(); + public static native void resume(); + public static native boolean isLocked(); + // Gets then releases raw monitor. + public static native void grabRawMonitor(); +} diff --git a/test/1943-suspend-raw-monitor-wait/expected.txt b/test/1943-suspend-raw-monitor-wait/expected.txt new file mode 100644 index 0000000000..9bb1ca37d0 --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/expected.txt @@ -0,0 +1,7 @@ +target_thread is sleeping in a wait. +Suspend target_thread. +Wake up the target_thread. +target_thread is sleeping in suspend without lock. +target_thread.isAlive() = true +resumed target_thread +target_thread doesn't hold lock! diff --git a/test/1943-suspend-raw-monitor-wait/info.txt b/test/1943-suspend-raw-monitor-wait/info.txt new file mode 100644 index 0000000000..1d716e11d1 --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/info.txt @@ -0,0 +1,4 @@ +Tests jvmti suspending of a thread that is interacting with a raw monitor. + +Makes sure that the RawMonitorWait function acts as a suspend point as the +thread leaves the function. diff --git a/test/1943-suspend-raw-monitor-wait/native_suspend_monitor.cc b/test/1943-suspend-raw-monitor-wait/native_suspend_monitor.cc new file mode 100644 index 0000000000..fdfc3c63fc --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/native_suspend_monitor.cc @@ -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. + */ + +#include <atomic> + +#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 Test1943SuspendRawMonitorExit { + +jrawMonitorID mon; +std::atomic<bool> locked(false); + +extern "C" JNIEXPORT void JNICALL Java_art_Test1943_nativeRun(JNIEnv* env, jclass) { + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->CreateRawMonitor("Test1943 monitor", &mon))) { + return; + } + // Grab the monitor twice + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + locked.store(true); + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(mon, 0))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } + locked.store(false); +} + + +extern "C" JNIEXPORT void JNICALL Java_art_Test1943_waitForPause(JNIEnv*, jclass) { + while (!locked.load()) { } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1943_nativeNotify(JNIEnv* env, jclass) { + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorNotifyAll(mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1943_grabRawMonitor(JNIEnv* env, jclass) { + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(mon))) { + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(mon))) { + return; + } +} + +} // namespace Test1943SuspendRawMonitorExit +} // namespace art diff --git a/test/1943-suspend-raw-monitor-wait/run b/test/1943-suspend-raw-monitor-wait/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/run @@ -0,0 +1,17 @@ +#!/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-run "$@" --jvmti diff --git a/test/1943-suspend-raw-monitor-wait/src/Main.java b/test/1943-suspend-raw-monitor-wait/src/Main.java new file mode 100644 index 0000000000..b95905339f --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/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.Test1943.run(); + } +} diff --git a/test/1943-suspend-raw-monitor-wait/src/art/Suspension.java b/test/1943-suspend-raw-monitor-wait/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/src/art/Suspension.java @@ -0,0 +1,30 @@ +/* + * 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 Suspension { + // Suspends a thread using jvmti. + public native static void suspend(Thread thr); + + // Resumes a thread using jvmti. + public native static void resume(Thread thr); + + public native static boolean isSuspended(Thread thr); + + public native static int[] suspendList(Thread... threads); + public native static int[] resumeList(Thread... threads); +} diff --git a/test/1943-suspend-raw-monitor-wait/src/art/Test1943.java b/test/1943-suspend-raw-monitor-wait/src/art/Test1943.java new file mode 100644 index 0000000000..4be68f3d71 --- /dev/null +++ b/test/1943-suspend-raw-monitor-wait/src/art/Test1943.java @@ -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. + */ + +package art; + +public class Test1943 { + public static void run() throws Exception { + final Thread target_thread = new Thread(() -> { + nativeRun(); + }, "target_thread"); + + target_thread.start(); + + // wait for the other thread to spin holding lock. + waitForPause(); + + // Ensure that the other thread is in a wait. + grabRawMonitor(); + System.out.println("target_thread is sleeping in a wait."); + + // Suspend it from java. + System.out.println("Suspend target_thread."); + Suspension.suspend(target_thread); + + // Let it try to unlock the monitor. + System.out.println("Wake up the target_thread."); + // Let the thread try to lock the monitor. + nativeNotify(); + + // Ensure that the other thread is suspended without the monitor. + grabRawMonitor(); + System.out.println("target_thread is sleeping in suspend without lock."); + + // Check other thread is still alive + System.out.println("target_thread.isAlive() = " + target_thread.isAlive()); + + // Resume it from java + System.out.println("resumed target_thread"); + Suspension.resume(target_thread); + + // Make sure the monitor is gone. + grabRawMonitor(); + System.out.println("target_thread doesn't hold lock!"); + + target_thread.join(); + } + + public static native void nativeRun(); + public static native void waitForPause(); + public static native void nativeNotify(); + // Gets then releases raw monitor. + public static native void grabRawMonitor(); +} diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 00ce3a9f8e..9635e70278 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:l\d+>> LoadClass loop:<<Loop:B\d+>> + /// CHECK-DAG: ClinitCheck [<<LoadClass>>] loop:<<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:l\d+>> LoadClass loop:none + /// CHECK-DAG: ClinitCheck [<<LoadClass>>] 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 6cea673b41..aeb9e44541 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 "dex/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 e69de29bb2..ae5318e53d 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 407f590b2b..71e0332e62 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 0000000000..ca4ed10660 --- /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 index 52abc38473..83823a24e5 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 0000000000..01cf1c92ab --- /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 0000000000..bd59d5f68e --- /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 0000000000..2f512d4089 --- /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 0000000000..d332e51f52 --- /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 66e50aacd7..678aef1f43 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 c4cc3b0121..f6332b5503 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -999,6 +999,24 @@ public class Main { 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); diff --git a/test/530-checker-lse3/expected.txt b/test/530-checker-lse3/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/530-checker-lse3/expected.txt diff --git a/test/530-checker-lse3/info.txt b/test/530-checker-lse3/info.txt new file mode 100644 index 0000000000..29b4cb82ab --- /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 0000000000..7fb582c8d1 --- /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: <<Arg:i\d+>> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<<Arg>>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<<Arg>>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <<Val:b\d+>> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField +## CHECK-DAG: <<Val2:b\d+>> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField2 +## CHECK-DAG: <<Val3:i\d+>> Add [<<Val>>,<<Val2>>] +## CHECK-DAG: Return [<<Val3>>] + +## 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: <<Arg:i\d+>> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<<Arg>>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<<Arg>>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <<Conv:b\d+>> TypeConversion [<<Arg>>] +## CHECK-DAG: <<Val3:i\d+>> Add [<<Conv>>,<<Conv>>] +## CHECK-DAG: Return [<<Val3>>] +.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: <<Arg:i\d+>> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<<Arg>>] field_name:StoreLoad.byteField +## CHECK-DAG: Return [<<Arg>>] + +## 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 0000000000..caef0b33cc --- /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 61c9e88e9e..f6f877c559 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 9e6fd3d96a..b76efeac15 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<<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:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecSub loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<<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:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecSub loop:<<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:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<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 55873eabf0..3173afdfcd 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/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index 06e3fb48fc..b2af91e49f 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "dex_file.h" +#include "dex/dex_file.h" #include "art_method-inl.h" #include "jit/profile_compilation_info.h" diff --git a/test/603-checker-instanceof/src/Main.java b/test/603-checker-instanceof/src/Main.java index ddf4b92fba..1487969c03 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 3ef8fe64bb..29f3817afb 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/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc index e1af02edfd..96ef2665cb 100644 --- a/test/626-const-class-linking/clear_dex_cache_types.cc +++ b/test/626-const-class-linking/clear_dex_cache_types.cc @@ -36,10 +36,10 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeSkipVerification(JNIEnv*, jcla ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> klass = hs.NewHandle(soa.Decode<mirror::Class>(cls)); - mirror::Class::Status status = klass->GetStatus(); - if (status == mirror::Class::kStatusResolved) { + ClassStatus status = klass->GetStatus(); + if (status == ClassStatus::kResolved) { ObjectLock<mirror::Class> lock(soa.Self(), klass); - klass->SetStatus(klass, mirror::Class::kStatusVerified, soa.Self()); + klass->SetStatus(klass, ClassStatus::kVerified, soa.Self()); } else { LOG(ERROR) << klass->PrettyClass() << " has unexpected status: " << status; } diff --git a/test/706-jit-skip-compilation/expected.txt b/test/638-checker-inline-cache-intrinsic/expected.txt index 6a5618ebc6..6a5618ebc6 100644 --- a/test/706-jit-skip-compilation/expected.txt +++ b/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 0000000000..764577be54 --- /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/706-jit-skip-compilation/run b/test/638-checker-inline-cache-intrinsic/run index 6c5720a099..f43681dd56 100644 --- a/test/706-jit-skip-compilation/run +++ b/test/638-checker-inline-cache-intrinsic/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,4 @@ # 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 +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 0000000000..472cbf68bc --- /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 347f916c8d..7d98e68a5a 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAnd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAnd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAnd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecOr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecOr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecOr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecXor loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecXor loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecXor loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -98,17 +68,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<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 5c13fc3926..6b691277b0 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -111,17 +81,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -134,17 +94,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -157,17 +107,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -180,17 +120,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<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 b3dff1411b..317a666980 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -192,17 +133,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<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 5d0899864a..0f04f734cc 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecDiv loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecDiv loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.abs() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -138,11 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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 c7883f37a3..d4eef9f763 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecDiv loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecDiv loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-MIPS64: void Main.abs() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -138,12 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.conv(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecCnv loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecCnv loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<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 aa230bfcaf..85d8b1b70b 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -112,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -135,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -158,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -181,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -204,17 +134,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -242,15 +162,7 @@ public class Main { /// CHECK-DAG: <<Get:i\d+>> ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Get>>] loop:<<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: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none static void shr32() { @@ -271,19 +183,7 @@ public class Main { /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 1 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 1 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<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: <<Dist:i\d+>> IntConstant 1 loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none @@ -305,19 +205,7 @@ public class Main { /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<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: <<Dist:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<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 c754f2a309..bb4d0cbd67 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -47,12 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -65,14 +55,15 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -114,12 +100,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -132,12 +113,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -150,12 +126,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -168,12 +139,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -201,11 +167,7 @@ public class Main { /// CHECK-DAG: <<Get:j\d+>> ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr64() loop_optimization (after) - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Get>>] loop:<<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: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none static void shr64() { @@ -226,13 +188,7 @@ public class Main { /// CHECK-DAG: <<UShr:j\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr65() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 1 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<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: <<Dist:i\d+>> IntConstant 1 loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none @@ -254,13 +210,7 @@ public class Main { /// CHECK-DAG: <<UShr:j\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <<Dist:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<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: <<Dist:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<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 e187397853..2b4ba87b71 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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none @@ -180,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<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:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none diff --git a/test/642-fp-callees/fp_callees.cc b/test/642-fp-callees/fp_callees.cc index 600f9690eb..17bb55b70d 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 <android-base/logging.h> + #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 823908c20e..d498bda052 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -38,25 +38,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-ARM64: void Main.doitByte(byte[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" - // - /// CHECK-START-MIPS64: void Main.doitByte(byte[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitShort(short[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" - // - /// 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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -104,15 +75,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-MIPS64: void Main.doitShort(short[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -157,25 +119,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-ARM64: void Main.doitInt(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" - // - /// CHECK-START-MIPS64: void Main.doitInt(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -198,15 +141,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-MIPS64: void Main.doitLong(long[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -229,15 +163,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-MIPS64: void Main.doitFloat(float[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet loop:<<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:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none @@ -260,15 +185,6 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-MIPS64: void Main.doitDouble(double[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" 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 41aa40cd6d..2ef340ab45 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: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<I127:i\d+>> IntConstant 127 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<I255:i\d+>> IntConstant 255 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> 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 8f879c77d0..2a1382dfde 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: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none @@ -87,19 +75,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none @@ -125,19 +101,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none @@ -174,19 +138,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none @@ -211,21 +163,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<UMAX:i\d+>> IntConstant 65535 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none @@ -259,21 +197,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<UMAX:i\d+>> IntConstant 65535 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> 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 b591081fba..4035b97209 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: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<SMAX:i\d+>> IntConstant 32767 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<UMAX:i\d+>> IntConstant 65535 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none diff --git a/test/646-checker-hadd-byte/src/Main.java b/test/646-checker-hadd-byte/src/Main.java index 4d259c437b..ca22200a1a 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: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none @@ -83,19 +71,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none @@ -118,19 +94,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int8 rounded:true loop:<<Loop>> outer_loop:none @@ -167,19 +131,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint8 rounded:true loop:<<Loop>> outer_loop:none @@ -201,21 +153,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<I127:i\d+>> IntConstant 127 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none @@ -249,21 +187,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint8 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<I255:i\d+>> IntConstant 255 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 55bb958670..85c2fcaf22 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: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none @@ -74,19 +62,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none @@ -122,19 +98,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none @@ -157,19 +121,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none @@ -192,19 +144,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none @@ -231,19 +171,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none @@ -281,19 +209,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none @@ -330,19 +246,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Uint16 rounded:true loop:<<Loop>> outer_loop:none @@ -365,21 +269,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Int16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<SMAX:i\d+>> IntConstant 32767 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none @@ -413,21 +303,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] packed_type:Uint16 rounded:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<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: <<UMAX:i\d+>> IntConstant 65535 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> 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 2188346aa9..45949ae90a 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: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none @@ -177,14 +129,7 @@ public class Main { /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Repl>>] packed_type:Int8 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin100(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> 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 d92bdaf808..9b056094a3 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: <<Cnv:c\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -59,19 +47,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -91,14 +67,7 @@ public class Main { /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Repl>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin100(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> 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 598106e604..66343adaa8 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: <<Min:i\d+>> InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none @@ -57,19 +45,7 @@ public class Main { /// CHECK-DAG: <<Max:i\d+>> InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<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 91f2a2dbdb..5f10adab79 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: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<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: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none @@ -177,14 +129,7 @@ public class Main { /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none - /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Repl>>] packed_type:Int16 loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin100(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java index 31d28e851e..081e421422 100644 --- a/test/656-checker-simd-opt/src/Main.java +++ b/test/656-checker-simd-opt/src/Main.java @@ -102,20 +102,7 @@ public class Main { /// CHECK-DAG: <<Add2>> Add [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Add1>> Add [<<Phi1>>,<<L1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.longInductionReduction(long[]) loop_optimization (after) - /// CHECK-DAG: <<L0:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<L1:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<L2:j\d+>> LongConstant 2 loop:none - /// CHECK-DAG: <<I0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Get:j\d+>> ArrayGet [{{l\d+}},<<I0>>] loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Get>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<L1>>] loop:none - /// CHECK-DAG: <<Phi1:j\d+>> Phi [<<L0>>,{{j\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi2>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<L2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.longInductionReduction(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.longInductionReduction(long[]) loop_optimization (after) /// CHECK-DAG: <<L0:j\d+>> LongConstant 0 loop:none /// CHECK-DAG: <<L1:j\d+>> LongConstant 1 loop:none /// CHECK-DAG: <<L2:j\d+>> LongConstant 2 loop:none @@ -144,18 +131,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Add>> Add [<<Phi>>,<<I1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) - /// CHECK-DAG: <<I0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none - /// CHECK-DAG: <<I4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Get:j\d+>> ArrayGet [{{l\d+}},<<I0>>] loop:none - /// CHECK-DAG: <<Cnv:i\d+>> TypeConversion [<<Get>>] loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Cnv>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<I0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi>>,<<I4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) /// CHECK-DAG: <<I0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none /// CHECK-DAG: <<I4:i\d+>> IntConstant 4 loop:none @@ -183,19 +159,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv2>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Add>> Add [<<Phi>>,<<I1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<I0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<I4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<L1:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Cnv:i\d+>> TypeConversion [<<L1>>] loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Cnv>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<I0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi>>,<<I4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) /// CHECK-DAG: <<I0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<I4:i\d+>> IntConstant 4 loop:none /// CHECK-DAG: <<L1:j\d+>> 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 877d7189a2..594948b7f9 100644 --- a/test/660-checker-simd-sad-byte/src/Main.java +++ b/test/660-checker-simd-sad-byte/src/Main.java @@ -99,18 +99,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons16>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -141,18 +130,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load2>>,<<Load1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons16>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -185,18 +163,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons16>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -234,19 +201,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons16>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -283,19 +238,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons16>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons16:i\d+>> IntConstant 16 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-int/src/Main.java b/test/660-checker-simd-sad-int/src/Main.java index d7d5a959d7..aa8431c1f5 100644 --- a/test/660-checker-simd-sad-int/src/Main.java +++ b/test/660-checker-simd-sad-int/src/Main.java @@ -31,32 +31,14 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2Int(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none private static int sadInt2Int(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -106,32 +88,14 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-ARM64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Ld1:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Ld2:d\d+>> VecLoad [{{l\d+}},<<I>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi>>,<<Ld1>>,<<Ld2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none private static int sadInt2IntAlt2(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -160,19 +124,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2Long(int[], int[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -209,19 +161,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none /// CHECK-DAG: <<ConsL:j\d+>> 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 d080e0cf3a..8281812b5b 100644 --- a/test/660-checker-simd-sad-long/src/Main.java +++ b/test/660-checker-simd-sad-long/src/Main.java @@ -32,19 +32,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2Long(long[], long[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -106,19 +94,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -155,19 +131,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<ConsL:j\d+>> 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 4ab66827b6..16bcabac95 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -66,18 +66,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2Int(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -108,18 +97,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load2>>,<<Load1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -152,18 +130,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -201,19 +168,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2Long(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -250,19 +205,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsL:j\d+>> 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 331f5ce690..274892d001 100644 --- a/test/660-checker-simd-sad-short2/src/Main.java +++ b/test/660-checker-simd-sad-short2/src/Main.java @@ -84,18 +84,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -145,18 +134,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load2>>,<<Load1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -208,18 +186,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none @@ -276,19 +243,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 0 loop:none @@ -344,19 +299,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsL:j\d+>> LongConstant 1 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsL>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Load2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsL:j\d+>> 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 ecda8848c3..5016b658e5 100644 --- a/test/660-checker-simd-sad-short3/src/Main.java +++ b/test/660-checker-simd-sad-short3/src/Main.java @@ -33,19 +33,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Param:s\d+>> ParameterValue loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Param>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Param:s\d+>> ParameterValue loop:none @@ -76,19 +64,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Param:s\d+>> ParameterValue loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Param>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Rep>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Param:s\d+>> ParameterValue loop:none @@ -119,19 +95,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<ConsI>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 32767 loop:none @@ -162,19 +126,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 32767 loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<ConsI>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Rep>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 32767 loop:none @@ -205,19 +157,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [{{i\d+}}] loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Conv>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [{{i\d+}}] loop:none @@ -249,18 +189,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [{{i\d+}}] loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Conv>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Rep>>,<<Load>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [{{i\d+}}] loop:none @@ -270,6 +199,7 @@ public class Main { /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Rep>>,<<Load>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none private static int sadShort2IntInvariantLeft(short[] s, int val) { int sad = 0; short x = (short) (val + 1); @@ -293,19 +223,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 110 loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<ConsI>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load>>,<<Add>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 110 loop:none @@ -316,6 +234,7 @@ public class Main { /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load>>,<<Add>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none private static int sadShort2IntCastedExprRight(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -339,19 +258,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) - /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none - /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 110 loop:none - /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<ConsI>>] loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none - /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Add>>,<<Load>>] loop:<<Loop>> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none /// CHECK-DAG: <<ConsI:i\d+>> IntConstant 110 loop:none @@ -362,6 +269,7 @@ public class Main { /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Add:d\d+>> VecAdd [<<Load>>,<<Rep>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Add>>,<<Load>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<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 1add0f1026..3a0a0495c4 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 [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionInt(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none private static int reductionInt(int[] x) { @@ -116,58 +96,19 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-ARM: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set1:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi1:d\d+>> Phi [<<Set1>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<I1:i\d+>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi1>>,<<Load1>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I1>>,<<Cons2>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: <<Red1:d\d+>> VecReduce [<<Phi1>>] loop:none - /// CHECK-DAG: <<Extr1:i\d+>> VecExtractScalar [<<Red1>>] loop:none - /// CHECK-DAG: <<Set2:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set2>>,{{d\d+}}] loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<I2:i\d+>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load2>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: Add [<<I2>>,<<Cons2>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: <<Red2:d\d+>> VecReduce [<<Phi2>>] loop:none - /// CHECK-DAG: <<Extr2:i\d+>> VecExtractScalar [<<Red2>>] loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" - // - /// CHECK-START-ARM64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set1:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi1:d\d+>> Phi [<<Set1>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<I1:i\d+>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi1>>,<<Load1>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I1>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: <<Red1:d\d+>> VecReduce [<<Phi1>>] loop:none - /// CHECK-DAG: <<Extr1:i\d+>> VecExtractScalar [<<Red1>>] loop:none - /// CHECK-DAG: <<Set2:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set2>>,{{d\d+}}] loop:<<Loop2:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<I2:i\d+>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load2>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: Add [<<I2>>,<<Cons4>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: <<Red2:d\d+>> VecReduce [<<Phi2>>] loop:none - /// CHECK-DAG: <<Extr2:i\d+>> VecExtractScalar [<<Red2>>] loop:none - // - /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" - // - /// CHECK-START-MIPS64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntChain() loop_optimization (after) /// CHECK-DAG: <<Set1:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi1:d\d+>> Phi [<<Set1>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<I1:i\d+>>] loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecAdd [<<Phi1>>,<<Load1>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I1>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none + /// CHECK-DAG: Add [<<I1>>,{{i\d+}}] loop:<<Loop1>> outer_loop:none /// CHECK-DAG: <<Red1:d\d+>> VecReduce [<<Phi1>>] loop:none /// CHECK-DAG: <<Extr1:i\d+>> VecExtractScalar [<<Red1>>] loop:none /// CHECK-DAG: <<Set2:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set2>>,{{d\d+}}] loop:<<Loop2:B\d+>> outer_loop:none /// CHECK-DAG: <<Load2:d\d+>> VecLoad [{{l\d+}},<<I2:i\d+>>] loop:<<Loop2>> outer_loop:none /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load2>>] loop:<<Loop2>> outer_loop:none - /// CHECK-DAG: Add [<<I2>>,<<Cons4>>] loop:<<Loop2>> outer_loop:none + /// CHECK-DAG: Add [<<I2>>,{{i\d+}}] loop:<<Loop2>> outer_loop:none /// CHECK-DAG: <<Red2:d\d+>> VecReduce [<<Phi2>>] loop:none /// CHECK-DAG: <<Extr2:i\d+>> VecExtractScalar [<<Red2>>] loop:none // @@ -199,38 +140,18 @@ public class Main { // /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // - /// CHECK-START-ARM: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntToLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop1>> outer_loop:none /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop1>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop1>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop1>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] 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 [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLong(long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] 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: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none @@ -312,33 +223,13 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntM1(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none private static int reductionIntM1(int[] x) { @@ -360,17 +251,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLongM1(long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecAdd [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] 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: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none @@ -421,33 +302,13 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinusInt(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none private static int reductionMinusInt(int[] x) { @@ -469,17 +330,7 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM64: long Main.reductionMinusLong(long[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecSub [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] 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: <<Cons2:i\d+>> IntConstant 2 loop:none /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none @@ -531,33 +382,13 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecMin [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecMin [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinInt(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecMin [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none private static int reductionMinInt(int[] x) { @@ -611,33 +442,13 @@ public class Main { /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: Return [<<Phi2>>] loop:none // - /// CHECK-START-ARM: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecMax [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons2>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none - /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: VecMax [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none - /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMaxInt(int[]) loop_optimization (after) + /// CHECK-DAG: <<Cons:i\d+>> IntConstant {{2|4}} loop:none /// CHECK-DAG: <<Set:d\d+>> VecReplicateScalar [{{i\d+}}] loop:none /// CHECK-DAG: <<Phi:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<I:i\d+>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: VecMax [<<Phi>>,<<Load>>] loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Add [<<I>>,<<Cons4>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Add [<<I>>,<<Cons>>] loop:<<Loop>> outer_loop:none /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi>>] loop:none /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] 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/664-aget-verifier/aget-verifier.cc b/test/664-aget-verifier/aget-verifier.cc index 41372adf02..4a263fae7a 100644 --- a/test/664-aget-verifier/aget-verifier.cc +++ b/test/664-aget-verifier/aget-verifier.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "dex_file.h" +#include "dex/dex_file.h" #include "art_method-inl.h" #include "jni.h" diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java index 6cd6d6465a..5c581c4fc7 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:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroz(boolean[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -46,13 +40,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerob(byte[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -68,13 +56,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroc(char[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -90,13 +72,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeros(short[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -112,13 +88,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroi(int[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -134,13 +104,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerol(long[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:j\d+>> LongConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -156,13 +120,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerof(float[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:f\d+>> FloatConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none @@ -178,13 +136,7 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerod(double[]) loop_optimization (after) - /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0 loop:none - /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none - /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<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: <<Zero:d\d+>> DoubleConstant 0 loop:none /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java index b867970eab..794308d6e1 100644 --- a/test/667-jit-jni-stub/src/Main.java +++ b/test/667-jit-jni-stub/src/Main.java @@ -135,13 +135,16 @@ public class Main { int count = 0; while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { // If `call` is true, also exercise the `callThrough()` method to increase hotness. - int limit = call ? 1 << Math.min(count, 12) : 0; + // 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 `hasJit` stub. - Thread.sleep(100); + // 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 } diff --git a/test/671-npe-field-opts/expected.txt b/test/671-npe-field-opts/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/671-npe-field-opts/expected.txt diff --git a/test/671-npe-field-opts/info.txt b/test/671-npe-field-opts/info.txt new file mode 100644 index 0000000000..f1e584633f --- /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 0000000000..a5e81ce672 --- /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 d21596d4bc..25e4fad714 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:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecReplicateScalar loop:<<Loop>> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (after) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecReplicateScalar loop:<<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:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecSetScalars loop:<<Loop>> outer_loop:none + // + /// CHECK: VecReduce loop:<<Loop>> outer_loop:none + /// CHECK: VecExtractScalar loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (after) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecSetScalars loop:<<Loop>> outer_loop:none + // + /// CHECK: VecReduce loop:<<Loop>> outer_loop:none + /// CHECK: VecExtractScalar loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<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 e9ef86bfb3..0000000000 --- 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 410504cb2f..0000000000 --- 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 aa847248d6..0000000000 --- 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/src/Main.java b/test/711-checker-type-conversion/src/Main.java index 64ffcd2f1f..ae58200b1b 100644 --- a/test/711-checker-type-conversion/src/Main.java +++ b/test/711-checker-type-conversion/src/Main.java @@ -22,20 +22,41 @@ public class Main { } } - /// CHECK-START: byte Main.getByte1() instruction_simplifier (before) + 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() instruction_simplifier (after) + /// CHECK-START: byte Main.getByte1() constant_folding (after) /// CHECK-NOT: TypeConversion - /// CHECK: Add - /// CHECK: TypeConversion - - /// CHECK-START: byte Main.getByte1() instruction_simplifier$before_codegen (after) /// CHECK-NOT: Add - /// CHECK-NOT: TypeConversion static byte getByte1() { int i = -2; @@ -43,20 +64,15 @@ public class Main { return (byte)((byte)i + (byte)j); } - /// CHECK-START: byte Main.getByte2() instruction_simplifier (before) + /// CHECK-START: byte Main.getByte2() constant_folding (before) /// CHECK: TypeConversion /// CHECK: TypeConversion /// CHECK: Add /// CHECK: TypeConversion - /// CHECK-START: byte Main.getByte2() instruction_simplifier (after) + /// CHECK-START: byte Main.getByte2() constant_folding (after) /// CHECK-NOT: TypeConversion - /// CHECK: Add - /// CHECK: TypeConversion - - /// CHECK-START: byte Main.getByte2() instruction_simplifier$before_codegen (after) /// CHECK-NOT: Add - /// CHECK: TypeConversion static byte getByte2() { int i = -100; @@ -64,8 +80,185 @@ public class Main { 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 19312b4d71..cab0abf58b 100644 --- a/test/900-hello-plugin/load_unload.cc +++ b/test/900-hello-plugin/load_unload.cc @@ -17,9 +17,10 @@ #include <jni.h> #include <stdio.h> +#include <android-base/logging.h> +#include <android-base/macros.h> + #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 472f2b768e..43a1d8319f 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -56,9 +56,13 @@ static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env, fsync(1); } -static void JNICALL VMDeatchCallback(jvmtiEnv *jenv, JNIEnv* jni_env ATTRIBUTE_UNUSED) { +static void JNICALL VMDeathCallback(jvmtiEnv *jenv, JNIEnv* jni_env) { printf("VMDeath (phase %d)\n", getPhase(jenv)); fsync(1); + jthread cur_thr; + CHECK_EQ(jenv->GetCurrentThread(&cur_thr), JVMTI_ERROR_NONE); + CHECK(cur_thr != nullptr); + jni_env->DeleteLocalRef(cur_thr); } @@ -67,7 +71,7 @@ static void InstallVMEvents(jvmtiEnv* env) { memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); callbacks.VMStart = VMStartCallback; callbacks.VMInit = VMInitCallback; - callbacks.VMDeath = VMDeatchCallback; + callbacks.VMDeath = VMDeathCallback; jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks)); if (ret != JVMTI_ERROR_NONE) { printf("Failed to install callbacks"); diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt index c0bccd6486..4d687f531e 100644 --- a/test/909-attach-agent/expected.txt +++ b/test/909-attach-agent/expected.txt @@ -1,11 +1,12 @@ Hello, world! Attached Agent for test 909-attach-agent +Attached Agent for test 909-attach-agent Goodbye! Hello, world! Attached Agent for test 909-attach-agent +Attached Agent for test 909-attach-agent Goodbye! Hello, world! -java.io.IOException: Process is not debuggable. - at dalvik.system.VMDebug.attachAgent(Native Method) - at Main.main(Main.java:27) +Process is not debuggable. +Process is not debuggable. Goodbye! diff --git a/test/909-attach-agent/src-art/Main.java b/test/909-attach-agent/src-art/Main.java index 25ebd57236..705e61eb99 100644 --- a/test/909-attach-agent/src-art/Main.java +++ b/test/909-attach-agent/src-art/Main.java @@ -14,7 +14,13 @@ * limitations under the License. */ +import dalvik.system.PathClassLoader; import dalvik.system.VMDebug; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.File; import java.io.IOException; public class Main { @@ -26,10 +32,76 @@ public class Main { try { VMDebug.attachAgent(agent); } catch(IOException e) { - e.printStackTrace(System.out); + System.out.println(e.getMessage()); } } } + attachWithClassLoader(args); System.out.println("Goodbye!"); } + + private static void attachWithClassLoader(String[] args) { + for(String a : args) { + if(a.startsWith("agent:")) { + String agentName = a.substring(6, a.indexOf('=')); + File tmp = null; + try { + tmp = File.createTempFile("lib", ".so"); + prepare(agentName, tmp); + + String newAgentName = tmp.getName(); + String agent = a.substring(6).replace(agentName, newAgentName); + + ClassLoader cl = new PathClassLoader("", tmp.getParentFile().getAbsolutePath(), + Main.class.getClassLoader()); + try { + VMDebug.attachAgent(agent, cl); + } catch(IOException e) { + System.out.println(e.getMessage()); + } + } catch (Exception e) { + e.printStackTrace(System.out); + } finally { + if (tmp != null) { + tmp.delete(); + } + } + } + } + } + + private static void prepare(String in, File tmp) throws Exception { + // Find the original. + File orig = find(in); + if (orig == null) { + throw new RuntimeException("Could not find " + in); + } + // Copy the original. + { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(orig)); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmp)); + byte[] buf = new byte[16 * 1024]; + for (;;) { + int r = bis.read(buf, 0, buf.length); + if (r < 0) { + break; + } else if (r > 0) { + bos.write(buf, 0, r); + } + } + bos.close(); + bis.close(); + } + } + + private static File find(String in) { + String libraryPath = System.getProperty("java.library.path"); + for (String path : libraryPath.split(":")) { + File f = new File(path + "/" + in); + if (f.exists()) { + return f; + } + } + return null; + } } diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc index 90d87e0e7b..23cea83be6 100644 --- a/test/936-search-onload/search_onload.cc +++ b/test/936-search-onload/search_onload.cc @@ -18,8 +18,9 @@ #include <inttypes.h> -#include "android-base/stringprintf.h" -#include "base/logging.h" +#include <android-base/macros.h> +#include <android-base/stringprintf.h> + #include "base/macros.h" #include "jni.h" #include "jvmti.h" diff --git a/test/954-invoke-polymorphic-verifier/expected.txt b/test/954-invoke-polymorphic-verifier/expected.txt index 5df393aede..d49af9648f 100644 --- a/test/954-invoke-polymorphic-verifier/expected.txt +++ b/test/954-invoke-polymorphic-verifier/expected.txt @@ -5,6 +5,12 @@ java.lang.VerifyError: Verifier rejected class TooFewArguments: void TooFewArgum java.lang.VerifyError: Verifier rejected class TooManyArguments: void TooManyArguments.<init>() failed to verify: void TooManyArguments.<init>(): void TooManyArguments.<init>(): Rejecting invocation, expected 4 argument registers, method signature has 3 java.lang.VerifyError: Verifier rejected class BadThis: void BadThis.<init>() failed to verify: void BadThis.<init>(): void BadThis.<init>(): 'this' argument 'Precise Reference: java.lang.String' not instance of 'Reference: java.lang.invoke.MethodHandle' java.lang.VerifyError: Verifier rejected class FakeSignaturePolymorphic: void FakeSignaturePolymorphic.<init>() failed to verify: void FakeSignaturePolymorphic.<init>(): void FakeSignaturePolymorphic.<init>(): invoke type (METHOD_POLYMORPHIC) does not match method type of java.lang.Object Main.invoke(java.lang.Object[]) -java.lang.VerifyError: Verifier rejected class BetterFakeSignaturePolymorphic: void BetterFakeSignaturePolymorphic.<init>() failed to verify: void BetterFakeSignaturePolymorphic.<init>(): Signature polymorphic method must be declared in java.lang.invoke.MethodClass +java.lang.VerifyError: Verifier rejected class BetterFakeSignaturePolymorphic: void BetterFakeSignaturePolymorphic.<init>() failed to verify: void BetterFakeSignaturePolymorphic.<init>(): Signature polymorphic method in unsuppported class: Main Passed Subclass test java.lang.VerifyError: Verifier rejected class Unresolved: void Unresolved.<init>() failed to verify: void Unresolved.<init>(): invoke-polymorphic receiver has no class: Unresolved Reference: other.thing.Foo +Passed VarHandleHappyAccessors test +java.lang.VerifyError: Verifier rejected class VarHandleUnhappyAccessors: void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]) failed to verify: void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]): void VarHandleUnhappyAccessors.compareAndExchange(java.lang.invoke.VarHandle, java.lang.Object[]): couldn't find method java.lang.invoke.VarHandle.compareAndExchange ([Ljava/lang/Object;)Ljava/lang/Integer; + void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndExchangeAcquire(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndExchangeAcquire (I)Ljava/lang/Object; + void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndExchangeRelease(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndExchangeRelease ()V + void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle) failed to verify: void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle): void VarHandleUnhappyAccessors.compareAndSet(java.lang.invoke.VarHandle): couldn't find method java.lang.invoke.VarHandle.compareAndSet (I)Z +java.lang.VerifyError: Verifier rejected class VarHandleUnknownAccessor: void VarHandleUnknownAccessor.<init>() failed to verify: void VarHandleUnknownAccessor.<init>(): void VarHandleUnknownAccessor.<init>(): couldn't find method java.lang.invoke.VarHandle.unknownAccessor ([Ljava/lang/Object;)Ljava/lang/Object; diff --git a/test/954-invoke-polymorphic-verifier/smali/Main.smali b/test/954-invoke-polymorphic-verifier/smali/Main.smali index 5b5e5557d7..e35aae7f4e 100644 --- a/test/954-invoke-polymorphic-verifier/smali/Main.smali +++ b/test/954-invoke-polymorphic-verifier/smali/Main.smali @@ -50,6 +50,12 @@ invoke-static {v0}, LMain;->test(Ljava/lang/String;)V const-string v0, "Unresolved" invoke-static {v0}, LMain;->test(Ljava/lang/String;)V + const-string v0, "VarHandleHappyAccessors" + invoke-static {v0}, LMain;->test(Ljava/lang/String;)V + const-string v0, "VarHandleUnhappyAccessors" + invoke-static {v0}, LMain;->test(Ljava/lang/String;)V +const-string v0, "VarHandleUnknownAccessor" + invoke-static {v0}, LMain;->test(Ljava/lang/String;)V return-void .end method diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali new file mode 100644 index 0000000000..ec6aa5bbce --- /dev/null +++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleHappyAccessors.smali @@ -0,0 +1,72 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.source "VarHandleHappyAccessors.smali" + +.class public LVarHandleHappyAccessors; +.super Ljava/lang/Object; + +.method public constructor <init>()V +.registers 4 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + invoke-static {}, LVarHandleHappyAccessors;->getVarHandle()Ljava/lang/invoke/VarHandle; + move-result-object v0 + if-eqz v0, :done + const/4 v1, 0 + move-object v1, v1 + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->compareAndSet([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->get([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAdd([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAddAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndAddRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSet([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSetAcquire([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getAndSetRelease([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getOpaque([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->getVolatile([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->set([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setOpaque([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setRelease([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->setVolatile([Ljava/lang/Object;)V, ([Ljava/lang/Object;)V + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSet([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object; + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([Ljava/lang/Object;)Z, ([Ljava/lang/Object;)Ljava/lang/Object; + return-void + :done + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Passed VarHandleHappyAccessors test" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + return-void +.end method + +.method public static getVarHandle()Ljava/lang/invoke/VarHandle; +.registers 1 + const/4 v0, 0 + return-object v0 +.end method diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali new file mode 100644 index 0000000000..0832c04d38 --- /dev/null +++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnhappyAccessors.smali @@ -0,0 +1,70 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.source "VarHandleUnhappyAccessors.smali" + +.class public LVarHandleUnhappyAccessors; +.super Ljava/lang/Object; + +.method public constructor <init>()V +.registers 4 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + invoke-static {}, LVarHandleUnhappyAccessors;->getVarHandle()Ljava/lang/invoke/VarHandle; + move-result-object v0 + invoke-static {}, LVarHandleUnhappyAccessors;->getObjectArray()[Ljava/lang/Object; + move-result-object v1 + invoke-static {v0, v1}, LVarHandleUnhappyAccessors;->compareAndExchange(Ljava/lang/invoke/VarHandle;[Ljava/lang/Object;)V + invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndExchangeAcquire(Ljava/lang/invoke/VarHandle;)V + invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndExchangeRelease(Ljava/lang/invoke/VarHandle;)V + invoke-static {v0}, LVarHandleUnhappyAccessors;->compareAndSet(Ljava/lang/invoke/VarHandle;)V + return-void +.end method + +# The following methods all invoke VarHandle accessors but the targetted +# accessor methods have the wrong signature. + +.method public static compareAndExchange(Ljava/lang/invoke/VarHandle;[Ljava/lang/Object;)V +.registers 2 + invoke-polymorphic {p0, p1}, Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Integer;, ([Ljava/lang/Object;)Ljava/lang/Object; +.end method + +.method public static compareAndExchangeAcquire(Ljava/lang/invoke/VarHandle;)V +.registers 2 + const/4 v0, 1 + invoke-polymorphic {p0, v0}, Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire(I)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; +.end method + +.method public static compareAndExchangeRelease(Ljava/lang/invoke/VarHandle;)V +.registers 1 + invoke-polymorphic {p0}, Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease()V, ([Ljava/lang/Object;)Ljava/lang/Object; +.end method + +.method public static compareAndSet(Ljava/lang/invoke/VarHandle;)V +.registers 2 + const/4 v0, 1 + invoke-polymorphic {p0, v0}, Ljava/lang/invoke/VarHandle;->compareAndSet(I)Z, ([Ljava/lang/Object;)Ljava/lang/Object; +.end method + +.method public static getVarHandle()Ljava/lang/invoke/VarHandle; +.registers 1 + const/4 v0, 0 + return-object v0 +.end method + +.method public static getObjectArray()[Ljava/lang/Object; +.registers 1 + const/4 v0, 0 + return-object v0 +.end method diff --git a/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali new file mode 100644 index 0000000000..35084f5bca --- /dev/null +++ b/test/954-invoke-polymorphic-verifier/smali/VarHandleUnknownAccessor.smali @@ -0,0 +1,37 @@ +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.source "VarHandleUnknownAccessor.smali" + +.class public LVarHandleUnknownAccessor; +.super Ljava/lang/Object; + +.method public constructor <init>()V +.registers 4 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + invoke-static {}, LVarHandleUnknownAccessor;->getVarHandle()Ljava/lang/invoke/VarHandle; + move-result-object v0 + const/4 v1, 0 + move-object v1, v1 + # Attempt invoke-polymorphic on VarHandle.unknownAccessor(). + invoke-polymorphic {v0, v1}, Ljava/lang/invoke/VarHandle;->unknownAccessor([Ljava/lang/Object;)Ljava/lang/Object;, ([Ljava/lang/Object;)Ljava/lang/Object; + return-void +.end method + +.method public static getVarHandle()Ljava/lang/invoke/VarHandle; +.registers 1 + const/4 v0, 0 + return-object v0 +.end method diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt index 4c294bc870..0a80882de1 100644 --- a/test/980-redefine-object/expected.txt +++ b/test/980-redefine-object/expected.txt @@ -30,3 +30,7 @@ Object allocated of type 'java.util.LinkedList$Node' Object allocated of type 'java.lang.Exception' Exception caught. Finishing test! +Object allocated of type 'java.lang.Thread' +Object allocated of type 'java.lang.Object' +Object allocated of type 'java.lang.Object' +Object allocated of type 'java.security.AccessControlContext' diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc index ca3f88b347..55dc603c4f 100644 --- a/test/983-source-transform-verify/source_transform.cc +++ b/test/983-source-transform-verify/source_transform.cc @@ -25,12 +25,12 @@ #include "jni.h" #include "jvmti.h" -#include "base/logging.h" #include "base/macros.h" #include "bytecode_utils.h" -#include "dex_file.h" -#include "dex_file_loader.h" -#include "dex_instruction.h" +#include "dex/code_item_accessors-inl.h" +#include "dex/dex_file.h" +#include "dex/dex_file_loader.h" +#include "dex/dex_instruction.h" #include "jit/jit.h" #include "native_stack_dump.h" #include "runtime.h" @@ -89,7 +89,8 @@ void JNICALL CheckDexFileHook(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, if (!it.IsAtMethod() || it.GetMethodCodeItem() == nullptr) { continue; } - for (const DexInstructionPcPair& pair : it.GetMethodCodeItem()->Instructions()) { + for (const DexInstructionPcPair& pair : + art::CodeItemInstructionAccessor(dex.get(), it.GetMethodCodeItem())) { const Instruction& inst = pair.Inst(); int forbiden_flags = (Instruction::kVerifyError | Instruction::kVerifyRuntimeOnly); if (inst.Opcode() == Instruction::RETURN_VOID_NO_BARRIER || diff --git a/test/AllFields/AllFields.java b/test/AllFields/AllFields.java index d5eac8fa2e..24f8ba1a0b 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 0000000000..d5f933f88d --- /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 0000000000..4db66b1886 --- /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 01e424d5e3..f5ca2f0338 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -260,6 +260,8 @@ art_cc_defaults { "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", "1941-dispose-stress/dispose_stress.cc", + "1942-suspend-raw-monitor-exit/native_suspend_monitor.cc", + "1943-suspend-raw-monitor-wait/native_suspend_monitor.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index fe4a327a1f..2cada76d90 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -22,7 +22,8 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ $(HOST_OUT_EXECUTABLES)/dx \ $(HOST_OUT_EXECUTABLES)/jasmin \ $(HOST_OUT_EXECUTABLES)/smali \ - $(HOST_OUT_EXECUTABLES)/dexmerger + $(HOST_OUT_EXECUTABLES)/dexmerger \ + $(HOST_OUT_JAVA_LIBRARIES)/desugar.jar # Add d8 dependency, if enabled. ifeq ($(USE_D8),true) diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 34580800cc..22c51063fb 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -16,10 +16,12 @@ #include "jni.h" +#include <android-base/logging.h> +#include <android-base/macros.h> + #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" -#include "dex_file-inl.h" +#include "dex/dex_file-inl.h" #include "instrumentation.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index 80a278012d..fd6273769b 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -16,8 +16,10 @@ #include "jni.h" -#include "base/logging.h" -#include "dex_file-inl.h" +#include <android-base/logging.h> + +#include "base/mutex.h" +#include "dex/dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "nth_caller_visitor.h" diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 31f43fc536..132099a45d 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -90,6 +90,9 @@ SYNC_BEFORE_RUN="n" # When running a debug build, we want to run with all checks. ANDROID_FLAGS="${ANDROID_FLAGS} -XX:SlowDebug=true" +# The same for dex2oatd, both prebuild and runtime-driven. +ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --runtime-arg -Xcompiler-option -XX:SlowDebug=true" +COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -XX:SlowDebug=true" while true; do if [ "x$1" = "x--quiet" ]; then @@ -184,6 +187,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 5b2ebf58a4..a12510c9dc 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -237,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."] @@ -649,4 +650,5 @@ "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace", "description": ["Test is designed to only check --compiler-filter=speed"] } + ] diff --git a/test/run-test b/test/run-test index fdb2ee47a7..75fe15c919 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 <agent-path>" 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 6d21442045..297ce08bee 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 554b8a5429..93998579f3 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") diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index d85f33a05d..9a7352e479 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -17,7 +17,8 @@ #include <jni.h> #include <stdio.h> -#include "base/logging.h" +#include <android-base/logging.h> + #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 8b74c7c089..b590175d77 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -27,6 +27,49 @@ namespace art { namespace common_trace { +static bool IsInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr) { + void* data; + ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred()); + env->ExceptionClear(); + jvmti->GetThreadLocalStorage(thr, &data); + if (exc.get() != nullptr) { + env->Throw(exc.get()); + } + if (data == nullptr) { + return false; + } else { + return true; + } +} + +static void SetInCallback(JNIEnv* env, jvmtiEnv *jvmti, jthread thr, bool val) { + ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred()); + env->ExceptionClear(); + jvmti->SetThreadLocalStorage(thr, (val ? reinterpret_cast<void*>(0x1) + : reinterpret_cast<void*>(0x0))); + if (exc.get() != nullptr) { + env->Throw(exc.get()); + } +} + +class ScopedCallbackState { + public: + ScopedCallbackState(JNIEnv* jnienv, jvmtiEnv* env, jthread thr) + : jnienv_(jnienv), env_(env), thr_(thr) { + CHECK(!IsInCallback(jnienv_, env_, thr_)); + SetInCallback(jnienv_, env_, thr_, true); + } + ~ScopedCallbackState() { + CHECK(IsInCallback(jnienv_, env_, thr_)); + SetInCallback(jnienv_, env_, thr_, false); + } + + private: + JNIEnv* jnienv_; + jvmtiEnv* env_; + jthread thr_; +}; + struct TraceData { jclass test_klass; jmethodID enter_method; @@ -36,9 +79,20 @@ struct TraceData { jmethodID single_step; jmethodID thread_start; jmethodID thread_end; - 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<jclass>(env->NewLocalRef(test_klass)); + if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) { + return nullptr; + } + return out; + } }; static void threadStartCB(jvmtiEnv* jvmti, @@ -49,8 +103,12 @@ static void threadStartCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } + ScopedLocalRef<jclass> 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 +118,12 @@ static void threadEndCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } + ScopedLocalRef<jclass> 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, @@ -74,24 +136,27 @@ static void singleStepCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } - if (data->in_callback) { + if (IsInCallback(jnienv, jvmti, thread)) { + return; + } + ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { return; } CHECK(data->single_step != nullptr); - data->in_callback = true; + ScopedCallbackState st(jnienv, jvmti, thread); jobject method_arg = GetJavaMethod(jvmti, jnienv, method); - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->single_step, thread, method_arg, static_cast<jlong>(location)); jnienv->DeleteLocalRef(method_arg); - data->in_callback = false; } static void fieldAccessCB(jvmtiEnv* jvmti, JNIEnv* jnienv, - jthread thr ATTRIBUTE_UNUSED, + jthread thr, jmethodID method, jlocation location, jclass field_klass, @@ -102,15 +167,19 @@ static void fieldAccessCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } - if (data->in_callback) { + if (IsInCallback(jnienv, jvmti, thr)) { // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_access != nullptr); - data->in_callback = true; + ScopedCallbackState st(jnienv, jvmti, thr); 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<jlong>(location), @@ -119,12 +188,11 @@ static void fieldAccessCB(jvmtiEnv* jvmti, field_arg); jnienv->DeleteLocalRef(method_arg); jnienv->DeleteLocalRef(field_arg); - data->in_callback = false; } static void fieldModificationCB(jvmtiEnv* jvmti, JNIEnv* jnienv, - jthread thr ATTRIBUTE_UNUSED, + jthread thr, jmethodID method, jlocation location, jclass field_klass, @@ -137,22 +205,25 @@ static void fieldModificationCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } - if (data->in_callback) { + if (IsInCallback(jnienv, jvmti, thr)) { // Don't do callback recursively to prevent an infinite loop. return; } + ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_modify != nullptr); - data->in_callback = true; + ScopedCallbackState st(jnienv, jvmti, thr); jobject method_arg = GetJavaMethod(jvmti, jnienv, method); jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); jobject value = GetJavaValueByType(jnienv, type_char, new_value); if (jnienv->ExceptionCheck()) { - data->in_callback = false; jnienv->DeleteLocalRef(method_arg); jnienv->DeleteLocalRef(field_arg); return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->field_modify, method_arg, static_cast<jlong>(location), @@ -162,12 +233,11 @@ static void fieldModificationCB(jvmtiEnv* jvmti, value); jnienv->DeleteLocalRef(method_arg); jnienv->DeleteLocalRef(field_arg); - data->in_callback = false; } static void methodExitCB(jvmtiEnv* jvmti, JNIEnv* jnienv, - jthread thr ATTRIBUTE_UNUSED, + jthread thr, jmethodID method, jboolean was_popped_by_exception, jvalue return_value) { @@ -176,31 +246,35 @@ static void methodExitCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { return; } - if (method == data->exit_method || method == data->enter_method || data->in_callback) { + if (method == data->exit_method || + method == data->enter_method || + IsInCallback(jnienv, jvmti, thr)) { // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->exit_method != nullptr); - data->in_callback = true; + ScopedCallbackState st(jnienv, jvmti, thr); jobject method_arg = GetJavaMethod(jvmti, jnienv, method); jobject result = was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value); if (jnienv->ExceptionCheck()) { - data->in_callback = false; return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->exit_method, method_arg, was_popped_by_exception, result); jnienv->DeleteLocalRef(method_arg); - data->in_callback = false; } static void methodEntryCB(jvmtiEnv* jvmti, JNIEnv* jnienv, - jthread thr ATTRIBUTE_UNUSED, + jthread thr, jmethodID method) { TraceData* data = nullptr; if (JvmtiErrorToException(jnienv, jvmti, @@ -208,18 +282,23 @@ static void methodEntryCB(jvmtiEnv* jvmti, return; } CHECK(data->enter_method != nullptr); - if (method == data->exit_method || method == data->enter_method || data->in_callback) { + if (method == data->exit_method || + method == data->enter_method || + IsInCallback(jnienv, jvmti, thr)) { // Don't do callback for either of these to prevent an infinite loop. return; } - data->in_callback = true; + ScopedLocalRef<jclass> klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } + ScopedCallbackState st(jnienv, jvmti, thr); 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; } static void classPrepareCB(jvmtiEnv* jvmti, @@ -407,6 +486,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<jclass>(env->NewGlobalRef(klass)); data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr; data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr; @@ -415,7 +498,6 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr; data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr; data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr; - data->in_callback = false; TraceData* old_data = nullptr; if (JvmtiErrorToException(env, jvmti_env, @@ -537,42 +619,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<jthrowable> 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 a9a0492fe9..34e6a9cd42 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 a765b17951..cdfeba45cb 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 1753406e6e..df964838bd 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 7920adae55..93fe46bf8b 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<com.android.ahat.heapdump.AhatInstance> defaultInstanceCompare(com.android.ahat.heapdump.AhatSnapshot); method public static java.util.Comparator<com.android.ahat.heapdump.Site> defaultSiteCompare(com.android.ahat.heapdump.AhatSnapshot); + method public static <T> java.util.Comparator<T> withPriority(java.util.Comparator<T>...); field public static final java.util.Comparator<com.android.ahat.heapdump.FieldValue> FIELD_VALUE_BY_NAME; field public static final java.util.Comparator<com.android.ahat.heapdump.FieldValue> FIELD_VALUE_BY_TYPE; field public static final java.util.Comparator<com.android.ahat.heapdump.AhatInstance> INSTANCE_BY_TOTAL_RETAINED_SIZE; @@ -246,22 +242,6 @@ package com.android.ahat.heapdump { field public static final java.util.Comparator<com.android.ahat.heapdump.Size> 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<T> implements java.util.Comparator { - ctor public Sort.WithPriority(java.util.Comparator<T>...); - ctor public Sort.WithPriority(java.util.List<java.util.Comparator<T>>); - 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/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java index 048573e915..04a6012a61 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 543eaa376a..5093f0d43e 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<Site.ObjectsInfo> infos = site.getObjectsInfos(); - Comparator<Site.ObjectsInfo> compare = new Sort.WithPriority<Site.ObjectsInfo>( + Comparator<Site.ObjectsInfo> 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 58b7b59f9a..d3fea4869a 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 <code>Node</code> interface. To use the + * dominators computation on your graph, you must make the following + * functionality available to the dominators computation: + * <ul> + * <li>Efficiently mapping from node to associated internal dominators + * computation state using the + * {@link #setDominatorsComputationState setDominatorsComputationState} and + * {@link #getDominatorsComputationState getDominatorsComputationState} methods. + * <li>Iterating over all outgoing edges of an node using the + * {@link #getReferencesForDominators getReferencesForDominators} method. + * <li>Setting the computed dominator for a node using the + * {@link #setDominator setDominator} method. + * </ul> */ 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 <code>state</code> 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 <code>setDominatorsComputationState</code> 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<? extends Node> 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 <code>root</code> node. + * There must not be any incoming references to the <code>root</code> node. + * <p> + * 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 ccdd6e4df7..9c80802673 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<Value> 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 cb9d959508..c82ef20e9b 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. + * <p> + * 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<FieldValue> 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 3babf76842..36ada2857c 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<FieldValue> 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 b8897a182c..60c9a0d086 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<AhatHeap> { private String mName; private Size mSize = Size.ZERO; @@ -61,6 +68,9 @@ public class AhatHeap implements Diffable<AhatHeap> { /** * 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<AhatHeap> { /** * 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 a9f819f710..67253bf0e7 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<AhatInstance>, DominatorsComputation.Node { // The id of this instance from the heap dump. @@ -80,14 +85,20 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** - * 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<AhatInstance>, 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<AhatInstance>, } /** - * 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<AhatInstance>, } /** - * 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<AhatInstance>, /** * 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. + * <p> + * 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<AhatInstance>, /** * 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<AhatInstance>, abstract Iterable<Reference> 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<AhatInstance>, /** * 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<RootType> getRootTypes() { if (!isRoot()) { @@ -205,14 +250,17 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, /** * 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<AhatInstance> getDominated() { return mDominated; @@ -220,13 +268,17 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, /** * 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<AhatInstance>, /** * 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<AhatInstance>, } /** - * 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<AhatInstance>, /** * 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<AhatInstance>, } /** - * 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<AhatInstance>, /** * 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<AhatInstance>, } /** - * 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<AhatInstance>, /** * 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 <code>referent</code> 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<AhatInstance>, } /** - * 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<AhatInstance> getHardReverseReferences() { if (mHardReverseReferences != null) { @@ -310,7 +385,10 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** - * 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<AhatInstance> getSoftReverseReferences() { if (mSoftReverseReferences != null) { @@ -320,9 +398,12 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** - * 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<AhatInstance>, } /** - * 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 + * <code>null</code> 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<AhatInstance>, } /** - * 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<AhatInstance>, } /** - * 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<AhatInstance>, } /** - * 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. + * <p> + * 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<PathElement> getPathFromGcRoot() { + if (isUnreachable()) { + return null; + } + List<PathElement> path = new ArrayList<PathElement>(); AhatInstance dom = this; @@ -434,12 +549,15 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, 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 59ce5d1c6c..535db082c1 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<AhatSnapshot> { private final Site mRootSite; @@ -60,16 +65,24 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { } /** - * 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<AhatSnapshot> { } /** - * 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<AhatSnapshot> { /** * 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. + * <p> + * 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<AhatHeap> 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<AhatInstance> 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<AhatSnapshot> { } /** - * 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 98c7e58d56..b35b4244ae 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. + * <p> + * The diff algorithm attempts to match instances in snapshot <code>a</code> + * to corresponding instances in snapshot <code>b</code>. 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 e3c671fe21..ff07af0028 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<DiffedFieldValue> diff(Iterable<FieldValue> current, Iterable<FieldValue> baseline) { @@ -85,5 +89,5 @@ public class DiffFields { * by field name and type. */ private static final Comparator<FieldValue> 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 53442c857e..09c8ee6d39 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<T> { /** - * 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<T> { * 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 3cd273ed98..8de337ea8c 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 dff401796a..6494069dcb 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 20e6da7271..70314da830 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 256a3b46f6..29ac9b0c5a 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 3bed29bafc..13be57d415 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. + * <p> + * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + * <ul> + * <li> + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + * <ul> + * <li> All elements are defined before they are referenced. + * <li> Ids are densely packed in some range [a, b] where a is not necessarily 0. + * <li> There are not more than 2^31 elements defined. + * </ul> + * <li> All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + * <li> The ID size used in the heap dump is 4 bytes. + * </ul> + * <p> + * 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. + * <p> + * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + * <ul> + * <li> + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + * <ul> + * <li> All elements are defined before they are referenced. + * <li> Ids are densely packed in some range [a, b] where a is not necessarily 0. + * <li> There are not more than 2^31 elements defined. + * </ul> + * <li> All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + * <li> The ID size used in the heap dump is 4 bytes. + * </ul> + * <p> + * 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 { 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 196a24628c..5ce0b1edfe 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. + * <p> + * For example, assuming object A is a root a path to some object X might look + * like: + * <pre> + * A.x --> B.y --> C.z --> X + * </pre> + * + * A path element is a single node of that path, such as <code>B.y</code>. + * @see AhatInstance#getPathFromGcRoot + */ public class PathElement implements Diffable<PathElement> { + /** + * The instance along the reference path that this PathElement is associated + * with. + */ public final AhatInstance instance; + + /** + * A human readable description of which field in <code>instance</code> is + * followed to reach the next element in the path. + * Some examples: + * <ul> + * <li> "mBlah" for a class instance + * <li> "[4]" for an array instance + * <li> "" for the last element of the path + * </ul> + */ public final String field; + + /** + * True if <code>instance</code> is a (not necessarily immediate) dominator + * of the final object in the path. + */ public boolean isDominator; + /** + * Constructs a PathElement object. + * <code>isDominator</code> 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 734f889af6..99d85dc940 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 4978d52830..72c0a4a750 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<Site> { // 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<Site> { 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<ObjectsInfo> { + /** + * 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<Site> { /** * 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<Site> { } /** - * 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<Site> { 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 <code>heap</code> 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<AhatInstance> objects) { for (AhatInstance inst : mObjects) { @@ -263,11 +297,24 @@ public class Site implements Diffable<Site> { 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<ObjectsInfo> 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<Site> { } /** - * 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<Site> { /** * Returns an unmodifiable list of this site's immediate children. + * + * @return this site's child sites */ public List<Site> 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 7c8db900df..a4593e195b 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. - * + * <p> * 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 <code>size</code> 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 efe0d6b59b..a629b3ce7f 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. - * + * <p> * 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> SIZE_BY_SIZE = new Comparator<Size>() { @@ -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<AhatInstance> { + private static class InstanceByHeapRetainedSize implements Comparator<AhatInstance> { 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<T> implements Comparator<T> { + private static class WithPriority<T> implements Comparator<T> { private List<Comparator<T>> 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<T>... 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<Comparator<T>> comparators) { mComparators = comparators; } @@ -101,6 +113,27 @@ public class Sort { } } + /** + * Returns a comparator that gives sort priority to earlier comparators in + * the list. + * + * @param <T> the type of object being sorted + * @param comparators the list of comparators to use for sorting + * @return the composite comparator + */ + public static <T> Comparator<T> withPriority(Comparator<T>... 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<AhatInstance> defaultInstanceCompare(AhatSnapshot snapshot) { List<Comparator<AhatInstance>> comparators = new ArrayList<Comparator<AhatInstance>>(); @@ -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<Site> { + private static class SiteByHeapSize implements Comparator<Site> { 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> SITE_BY_TOTAL_SIZE = new Comparator<Site>() { @@ -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<Site> defaultSiteCompare(AhatSnapshot snapshot) { List<Comparator<Site>> comparators = new ArrayList<Comparator<Site>>(); @@ -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<FieldValue> FIELD_VALUE_BY_NAME = new Comparator<FieldValue>() { @@ -213,7 +259,7 @@ public class Sort { }; /** - * Compare FieldValue by type name. + * Compares FieldValue by type name. */ public static final Comparator<FieldValue> FIELD_VALUE_BY_TYPE = new Comparator<FieldValue>() { 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 40249615a2..ff79864505 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 eea427774b..b219bf1564 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 <code>value</code>. + * + * @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 32bb209dc6..79a737cc18 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 = "[]"; @@ -98,6 +101,10 @@ public class ProguardMap { private Map<String, ClassData> mClassesFromClearName = new HashMap<String, ClassData>(); private Map<String, ClassData> mClassesFromObfuscatedName = new HashMap<String, ClassData>(); + /** + * 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; @@ -106,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; } @@ -116,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 <code>mapFile</code> to this + * proguard mapping. + * The <code>mapFile</code> should be a proguard mapping file generated with + * the <code>-printmapping</code> option when proguard was run. + * + * @param mapFile the name of a file with proguard mapping information + * @throws FileNotFoundException If the <code>mapFile</code> could not be + * found + * @throws IOException If an input exception occurred. + * @throws ParseException If the <code>mapFile</code> 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 <code>mapReader</code> to + * this proguard mapping. + * <code>mapReader</code> should be a Reader of a proguard mapping file + * generated with the <code>-printmapping</code> 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 <code>mapFile</code> 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(); @@ -207,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. @@ -224,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) { @@ -235,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); diff --git a/tools/checker/README b/tools/checker/README index 65f5bd25a5..b8dd80376e 100644 --- a/tools/checker/README +++ b/tools/checker/README @@ -76,3 +76,10 @@ Example: /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) /// CHECK: <<ID:i\d+>> IntConstant {{11|22}} /// CHECK: Return [<<ID>>] + +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 2e9faba9fb..65b01a7f15 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 f199a50ebe..7a5a4c8c26 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/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def index f46cd339b0..4f1d875e5a 100644 --- a/tools/cpp-define-generator/constant_class.def +++ b/tools/cpp-define-generator/constant_class.def @@ -15,7 +15,6 @@ */ #if defined(DEFINE_INCLUDE_DEPENDENCIES) -#include "mirror/class.h" // kStatusInitialized #include "modifiers.h" // kAccClassIsFinalizable #include "base/bit_utils.h" // MostSignificantBit #endif @@ -23,7 +22,6 @@ #define DEFINE_FLAG_OFFSET(type_name, field_name, expr) \ DEFINE_EXPR(type_name ## _ ## field_name, uint32_t, (expr)) -DEFINE_FLAG_OFFSET(MIRROR_CLASS, STATUS_INITIALIZED, art::mirror::Class::kStatusInitialized) DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE, art::kAccClassIsFinalizable) DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_INTERFACE, art::kAccInterface) // TODO: We should really have a BitPosition which also checks it's a power of 2. diff --git a/tools/dt_fds_forward.py b/tools/dt_fds_forward.py new file mode 100755 index 0000000000..516b7fef96 --- /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 index e1cc831303..c96830a592 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. */ [ { @@ -21,19 +24,6 @@ bug: 66905894, name: "org.apache.harmony.jpda.tests.jdwp.ReferenceType.GetValues006Test#testGetValues006" }, -{ - description: "Tests fail with assertion error on slot number", - result: EXEC_FAILED, - bug: 66905468, - names: [ "org.apache.harmony.jpda.tests.jdwp.Method.VariableTableTest#testVariableTableTest001", - "org.apache.harmony.jpda.tests.jdwp.Method.VariableTableWithGenericTest#testVariableTableWithGenericTest001" ] -}, -{ - description: "Test fails with Error VM_DEAD when trying to resume during VM_DEATH event", - result: EXEC_FAILED, - bug: 66904725, - name: "org.apache.harmony.jpda.tests.jdwp.Events.VMDeath002Test#testVMDeathRequest" -}, /* TODO Categorize these failures more. */ { description: "Tests that fail on both ART and RI. These tests are likely incorrect", @@ -69,15 +59,16 @@ name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" }, { - description: "Test for ddms extensions that are not yet implemented", - 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" +}, +{ + 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 10d175b83d..eb0e71f53d 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 7990c6cf8e..60c62752ef 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[] = { "+", "-" }; @@ -51,11 +49,21 @@ 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 <typename T, uint32_t N> +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 <std::uint32_t N> + 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 ca0aec01cc..a25ef3fca5 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 dac1c79817..34180d993f 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/libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt index abcc728890..7694a4c7e4 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", diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 47e7c4595d..e116facd98 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -79,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 46c2a153a7..e7f028ae63 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/instruction_decoder.cc b/tools/titrace/instruction_decoder.cc index bc4660b497..d8fb713414 100644 --- a/tools/titrace/instruction_decoder.cc +++ b/tools/titrace/instruction_decoder.cc @@ -15,7 +15,7 @@ #include "instruction_decoder.h" -#include "dex_instruction_list.h" +#include "dex/dex_instruction_list.h" #include <android-base/logging.h> |