From 1a600266c3ad1193d41ebb08a5a87c00cf726b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 10 Sep 2020 08:24:15 +0200 Subject: [PATCH 1/9] [lldb] Initial version of FreeBSD remote process plugin Add a new FreeBSD Process plugin using client/server model. This plugin is based on the one used by NetBSD. It currently supports a subset of functionality for amd64. It is automatically used when spawning lldb-server. It can also be used by lldb client by setting FREEBSD_REMOTE_PLUGIN environment variable (to any value). The code is capable of debugging simple single-threaded programs. It supports general purpose, debug and FPU registers (up to XMM) of amd64, basic signalling, software breakpoints. Adding the support for the plugin involves removing some dead code from FreeBSDPlatform plugin (that was not ever used because CanDebugProcess() returned false), and replacing it with appropriate code from NetBSD platform support. Differential Revision: https://reviews.llvm.org/D88796 --- .../Platform/FreeBSD/PlatformFreeBSD.cpp | 57 +- .../Platform/FreeBSD/PlatformFreeBSD.h | 5 - lldb/source/Plugins/Process/CMakeLists.txt | 1 + .../Process/FreeBSD/ProcessFreeBSD.cpp | 12 +- .../Process/FreeBSDRemote/CMakeLists.txt | 16 + .../FreeBSDRemote/NativeProcessFreeBSD.cpp | 781 ++++++++++ .../FreeBSDRemote/NativeProcessFreeBSD.h | 121 ++ .../NativeRegisterContextFreeBSD.cpp | 39 + .../NativeRegisterContextFreeBSD.h | 48 + .../NativeRegisterContextFreeBSD_x86_64.cpp | 1315 +++++++++++++++++ .../NativeRegisterContextFreeBSD_x86_64.h | 101 ++ .../FreeBSDRemote/NativeThreadFreeBSD.cpp | 216 +++ .../FreeBSDRemote/NativeThreadFreeBSD.h | 83 ++ .../GDBRemoteCommunicationServerCommon.cpp | 2 +- lldb/test/Shell/lit.cfg.py | 3 + lldb/tools/lldb-server/CMakeLists.txt | 6 + lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 + 17 files changed, 2749 insertions(+), 61 deletions(-) create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp create mode 100644 lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h diff --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp index 97c2f22b505f5f..7318264c554bba 100644 --- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp +++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -246,58 +246,15 @@ PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target, } } -Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) { - Status error; - if (IsHost()) { - error = Platform::LaunchProcess(launch_info); - } else { - if (m_remote_platform_sp) - error = m_remote_platform_sp->LaunchProcess(launch_info); - else - error.SetErrorString("the platform is not currently connected"); - } - return error; -} - -lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, - Debugger &debugger, Target *target, - Status &error) { - lldb::ProcessSP process_sp; - if (IsHost()) { - if (target == nullptr) { - TargetSP new_target_sp; - ArchSpec emptyArchSpec; - - error = debugger.GetTargetList().CreateTarget( - debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp, - new_target_sp); - target = new_target_sp.get(); - } else - error.Clear(); - - if (target && error.Success()) { - debugger.GetTargetList().SetSelectedTarget(target); - // The freebsd always currently uses the GDB remote debugger plug-in so - // even when debugging locally we are debugging remotely! Just like the - // darwin plugin. - process_sp = target->CreateProcess( - attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); - - if (process_sp) - error = process_sp->Attach(attach_info); +bool PlatformFreeBSD::CanDebugProcess() { + if (getenv("FREEBSD_REMOTE_PLUGIN")) { + if (IsHost()) { + return true; + } else { + // If we're connected, we can debug. + return IsConnected(); } - } else { - if (m_remote_platform_sp) - process_sp = - m_remote_platform_sp->Attach(attach_info, debugger, target, error); - else - error.SetErrorString("the platform is not currently connected"); } - return process_sp; -} - -// FreeBSD processes cannot yet be launched by spawning and attaching. -bool PlatformFreeBSD::CanDebugProcess() { return false; } diff --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h index 56f2f2771d124f..4f3b119c568f52 100644 --- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h +++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h @@ -49,11 +49,6 @@ class PlatformFreeBSD : public PlatformPOSIX { size_t GetSoftwareBreakpointTrapOpcode(Target &target, BreakpointSite *bp_site) override; - Status LaunchProcess(ProcessLaunchInfo &launch_info) override; - - lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, - Target *target, Status &error) override; - void CalculateTrapHandlerSymbolNames() override; MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index fdeb211fe7a20f..91f20ec22ac55d 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -2,6 +2,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") add_subdirectory(Linux) add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_subdirectory(FreeBSDRemote) add_subdirectory(FreeBSD) add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index 67a18bd8de13b4..d87b01be8fc3bc 100644 --- a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -79,12 +79,14 @@ ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp, } void ProcessFreeBSD::Initialize() { - static llvm::once_flag g_once_flag; + if (!getenv("FREEBSD_REMOTE_PLUGIN")) { + static llvm::once_flag g_once_flag; - llvm::call_once(g_once_flag, []() { - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), CreateInstance); - }); + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); + } } lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() { diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt new file mode 100644 index 00000000000000..8c1cec52fb15dd --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_library(lldbPluginProcessFreeBSDRemote + NativeProcessFreeBSD.cpp + NativeRegisterContextFreeBSD.cpp + NativeRegisterContextFreeBSD_x86_64.cpp + NativeThreadFreeBSD.cpp + + LINK_LIBS + lldbHost + lldbSymbol + lldbTarget + lldbUtility + lldbPluginProcessPOSIX + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp new file mode 100644 index 00000000000000..c96e05f238d4a8 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.cpp @@ -0,0 +1,781 @@ +//===-- NativeProcessFreeBSD.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeProcessFreeBSD.h" + +// clang-format off +#include +#include +#include +#include +#include +#include +// clang-format on + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/Errno.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; +using namespace llvm; + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Status EnsureFDFlags(int fd, int flags) { + Status error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +// Public Static Methods + +llvm::Expected> +NativeProcessFreeBSD::Factory::Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + + Status status; + ::pid_t pid = ProcessLauncherPosixFork() + .LaunchProcess(launch_info, status) + .GetProcessId(); + LLDB_LOG(log, "pid = {0:x}", pid); + if (status.Fail()) { + LLDB_LOG(log, "failed to launch process: {0}", status); + return status.ToError(); + } + + // Wait for the child process to trap on its call to execve. + int wstatus; + ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); + assert(wpid == pid); + (void)wpid; + if (!WIFSTOPPED(wstatus)) { + LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", + WaitStatus::Decode(wstatus)); + return llvm::make_error("Could not sync with inferior process", + llvm::inconvertibleErrorCode()); + } + LLDB_LOG(log, "inferior started, now in stopped state"); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architecture", + llvm::inconvertibleErrorCode()); + } + + // Set the architecture to the exe architecture. + LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, + Info.GetArchitecture().GetArchitectureName()); + + std::unique_ptr process_up(new NativeProcessFreeBSD( + pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, + Info.GetArchitecture(), mainloop)); + + status = process_up->ReinitializeThreads(); + if (status.Fail()) + return status.ToError(); + + for (const auto &thread : process_up->m_threads) + static_cast(*thread).SetStoppedBySignal(SIGSTOP); + process_up->SetState(StateType::eStateStopped, false); + + return std::move(process_up); +} + +llvm::Expected> +NativeProcessFreeBSD::Factory::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + LLDB_LOG(log, "pid = {0:x}", pid); + + // Retrieve the architecture for the running process. + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error("Cannot get process architecture", + llvm::inconvertibleErrorCode()); + } + + std::unique_ptr process_up(new NativeProcessFreeBSD( + pid, -1, native_delegate, Info.GetArchitecture(), mainloop)); + + Status status = process_up->Attach(); + if (!status.Success()) + return status.ToError(); + + return std::move(process_up); +} + +// Public Instance Methods + +NativeProcessFreeBSD::NativeProcessFreeBSD(::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + const ArchSpec &arch, + MainLoop &mainloop) + : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) { + if (m_terminal_fd != -1) { + Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + assert(status.Success()); + } + + Status status; + m_sigchld_handle = mainloop.RegisterSignal( + SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); + assert(m_sigchld_handle && status.Success()); +} + +// Handles all waitpid events from the inferior process. +void NativeProcessFreeBSD::MonitorCallback(lldb::pid_t pid, int signal) { + switch (signal) { + case SIGTRAP: + return MonitorSIGTRAP(pid); + case SIGSTOP: + return MonitorSIGSTOP(pid); + default: + return MonitorSignal(pid, signal); + } +} + +void NativeProcessFreeBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + + LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid); + + /* Stop Tracking All Threads attached to Process */ + m_threads.clear(); + + SetExitStatus(status, true); + + // Notify delegate that our process has exited. + SetState(StateType::eStateExited, true); +} + +void NativeProcessFreeBSD::MonitorSIGSTOP(lldb::pid_t pid) { + /* Stop all Threads attached to Process */ + for (const auto &thread : m_threads) { + static_cast(*thread).SetStoppedBySignal(SIGSTOP, + nullptr); + } + SetState(StateType::eStateStopped, true); +} + +void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + struct ptrace_lwpinfo info; + + const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + assert(info.pl_event == PL_EVENT_SIGNAL); + // TODO: do we need to handle !PL_FLAG_SI? + assert(info.pl_flags & PL_FLAG_SI); + assert(info.pl_siginfo.si_signo == SIGTRAP); + + LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, si_code = {2}", pid, + info.pl_lwpid, info.pl_siginfo.si_code); + + NativeThreadFreeBSD *thread = nullptr; + if (info.pl_lwpid > 0) { + for (const auto &t : m_threads) { + if (t->GetID() == static_cast(info.pl_lwpid)) { + thread = static_cast(t.get()); + break; + } + static_cast(t.get())->SetStoppedWithNoReason(); + } + if (!thread) + LLDB_LOG(log, "thread not found in m_threads, pid = {0}, LWP = {1}", pid, + info.pl_lwpid); + } + + switch (info.pl_siginfo.si_code) { + case TRAP_BRKPT: + if (thread) { + thread->SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(*thread); + } + SetState(StateType::eStateStopped, true); + break; + case TRAP_TRACE: + if (thread) + thread->SetStoppedByTrace(); + SetState(StateType::eStateStopped, true); + break; + } +} + +void NativeProcessFreeBSD::MonitorSignal(lldb::pid_t pid, int signal) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + struct ptrace_lwpinfo info; + + const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + assert(info.pl_event == PL_EVENT_SIGNAL); + // TODO: do we need to handle !PL_FLAG_SI? + assert(info.pl_flags & PL_FLAG_SI); + assert(info.pl_siginfo.si_signo == signal); + + for (const auto &abs_thread : m_threads) { + NativeThreadFreeBSD &thread = + static_cast(*abs_thread); + assert(info.pl_lwpid >= 0); + if (info.pl_lwpid == 0 || + static_cast(info.pl_lwpid) == thread.GetID()) + thread.SetStoppedBySignal(info.pl_siginfo.si_signo, &info.pl_siginfo); + else + thread.SetStoppedWithNoReason(); + } + SetState(StateType::eStateStopped, true); +} + +Status NativeProcessFreeBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr, + int data, int *result) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + Status error; + int ret; + + errno = 0; + ret = + ptrace(req, static_cast<::pid_t>(pid), static_cast(addr), data); + + if (ret == -1) + error.SetErrorToErrno(); + + if (result) + *result = ret; + + LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret); + + if (error.Fail()) + LLDB_LOG(log, "ptrace() failed: {0}", error); + + return error; +} + +Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + LLDB_LOG(log, "pid {0}", GetID()); + + Status ret; + + int signal = 0; + for (const auto &abs_thread : m_threads) { + assert(abs_thread && "thread list should not contain NULL threads"); + NativeThreadFreeBSD &thread = + static_cast(*abs_thread); + + const ResumeAction *action = + resume_actions.GetActionForThread(thread.GetID(), true); + // we need to explicit issue suspend requests, so it is simpler to map it + // into proper action + ResumeAction suspend_action{thread.GetID(), eStateSuspended, + LLDB_INVALID_SIGNAL_NUMBER}; + + if (action == nullptr) { + LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), + thread.GetID()); + action = &suspend_action; + } + + LLDB_LOG( + log, + "processing resume action state {0} signal {1} for pid {2} tid {3}", + action->state, action->signal, GetID(), thread.GetID()); + + switch (action->state) { + case eStateRunning: + ret = thread.Resume(); + break; + case eStateStepping: + ret = thread.SingleStep(); + break; + case eStateSuspended: + case eStateStopped: + if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) + return Status("Passing signal to suspended thread unsupported"); + + ret = thread.Suspend(); + break; + + default: + return Status( + "NativeProcessFreeBSD::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), thread.GetID()); + } + + if (!ret.Success()) + return ret; + if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) + signal = action->signal; + } + + ret = + PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast(1), signal); + if (ret.Success()) + SetState(eStateRunning, true); + return ret; +} + +Status NativeProcessFreeBSD::Halt() { + Status error; + + if (kill(GetID(), SIGSTOP) != 0) + error.SetErrorToErrno(); + return error; +} + +Status NativeProcessFreeBSD::Detach() { + Status error; + + // Stop monitoring the inferior. + m_sigchld_handle.reset(); + + // Tell ptrace to detach from the process. + if (GetID() == LLDB_INVALID_PROCESS_ID) + return error; + + return PtraceWrapper(PT_DETACH, GetID()); +} + +Status NativeProcessFreeBSD::Signal(int signo) { + Status error; + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessFreeBSD::Interrupt() { return Halt(); } + +Status NativeProcessFreeBSD::Kill() { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + LLDB_LOG(log, "pid {0}", GetID()); + + Status error; + + switch (m_state) { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), + StateAsCString(m_state)); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + if (kill(GetID(), SIGKILL) != 0) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +Status NativeProcessFreeBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) { + // We're done. + return Status("unsupported"); + } + + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + return error; + } + + lldb::addr_t prev_base_address = 0; + // FIXME start by finding the last region that is <= target address using + // binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); + ++it) { + MemoryRegionInfo &proc_entry_info = it->first; + // Sanity check assumption that memory map entries are ascending. + assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && + "descending memory map entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange().GetRangeBase(); + UNUSED_IF_ASSERT_DISABLED(prev_base_address); + // If the target address comes before this entry, indicate distance to next + // region. + if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetByteSize( + proc_entry_info.GetRange().GetRangeBase() - load_addr); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; + } else if (proc_entry_info.GetRange().Contains(load_addr)) { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + // The target memory address comes somewhere after the region we just + // parsed. + } + // If we made it here, we didn't find an entry that contained the given + // address. Return the load_addr as start and the amount of bytes betwwen + // load address and the end of the memory as size. + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +Status NativeProcessFreeBSD::PopulateMemoryRegionCache() { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + // If our cache is empty, pull the latest. There should always be at least + // one memory region if memory region handling is supported. + if (!m_mem_region_cache.empty()) { + LLDB_LOG(log, "reusing {0} cached memory region entries", + m_mem_region_cache.size()); + return Status(); + } + + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, static_cast(m_pid)}; + int ret; + size_t len; + + ret = ::sysctl(mib, 4, nullptr, &len, nullptr, 0); + if (ret != 0) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("sysctl() for KERN_PROC_VMMAP failed"); + } + + std::unique_ptr buf = + llvm::WritableMemoryBuffer::getNewMemBuffer(len); + ret = ::sysctl(mib, 4, buf->getBufferStart(), &len, nullptr, 0); + if (ret != 0) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("sysctl() for KERN_PROC_VMMAP failed"); + } + + char *bp = buf->getBufferStart();; + char *end = bp + len; + while (bp < end) { + auto *kv = reinterpret_cast(bp); + if (kv->kve_structsize == 0) + break; + bp += kv->kve_structsize; + + MemoryRegionInfo info; + info.Clear(); + info.GetRange().SetRangeBase(kv->kve_start); + info.GetRange().SetRangeEnd(kv->kve_end); + info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + + if (kv->kve_protection & VM_PROT_READ) + info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_protection & VM_PROT_WRITE) + info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_protection & VM_PROT_EXECUTE) + info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_path[0]) + info.SetName(kv->kve_path); + + m_mem_region_cache.emplace_back(info, + FileSpec(info.GetName().GetCString())); + } + + if (m_mem_region_cache.empty()) { + // No entries after attempting to read them. This shouldn't happen. Assume + // we don't support map entries. + LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " + "for memory region metadata retrieval"); + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("not supported"); + } + LLDB_LOG(log, "read {0} memory region entries from process {1}", + m_mem_region_cache.size(), GetID()); + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; + + return Status(); +} + +Status NativeProcessFreeBSD::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) { + return Status("Unimplemented"); +} + +Status NativeProcessFreeBSD::DeallocateMemory(lldb::addr_t addr) { + return Status("Unimplemented"); +} + +lldb::addr_t NativeProcessFreeBSD::GetSharedLibraryInfoAddress() { + // punt on this for now + return LLDB_INVALID_ADDRESS; +} + +size_t NativeProcessFreeBSD::UpdateThreads() { return m_threads.size(); } + +Status NativeProcessFreeBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return Status("NativeProcessFreeBSD does not support hardware breakpoints"); + else + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessFreeBSD::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + return Status("Unimplemented"); +} + +Status +NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + return Status(); +} + +void NativeProcessFreeBSD::SigchldHandler() { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + // Process all pending waitpid notifications. + int status; + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG); + + if (wait_pid == 0) + return; // We are done. + + if (wait_pid == -1) { + Status error(errno, eErrorTypePOSIX); + LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); + } + + WaitStatus wait_status = WaitStatus::Decode(status); + bool exited = wait_status.type == WaitStatus::Exit || + (wait_status.type == WaitStatus::Signal && + wait_pid == static_cast<::pid_t>(GetID())); + + LLDB_LOG(log, + "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", + GetID(), wait_pid, status, exited); + + if (exited) + MonitorExited(wait_pid, wait_status); + else { + assert(wait_status.type == WaitStatus::Stop); + MonitorCallback(wait_pid, wait_status.status); + } +} + +bool NativeProcessFreeBSD::HasThreadNoLock(lldb::tid_t thread_id) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + if (thread->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +NativeThreadFreeBSD &NativeProcessFreeBSD::AddThread(lldb::tid_t thread_id) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); + + assert(thread_id > 0); + assert(!HasThreadNoLock(thread_id) && + "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty()) + SetCurrentThreadID(thread_id); + + m_threads.push_back(std::make_unique(*this, thread_id)); + return static_cast(*m_threads.back()); +} + +void NativeProcessFreeBSD::RemoveThread(lldb::tid_t thread_id) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOG(log, "pid {0} removing thread with tid {1}", GetID(), thread_id); + + assert(thread_id > 0); + assert(HasThreadNoLock(thread_id) && + "attempted to remove a thread that does not exist"); + + for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { + if ((*it)->GetID() == thread_id) { + m_threads.erase(it); + break; + } + } +} + +Status NativeProcessFreeBSD::Attach() { + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + Status status = PtraceWrapper(PT_ATTACH, m_pid); + if (status.Fail()) + return status; + + int wstatus; + // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this + // point we should have a thread stopped if waitpid succeeds. + if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid, m_pid, nullptr, 0)) < + 0) + return Status(errno, eErrorTypePOSIX); + + /* Initialize threads */ + status = ReinitializeThreads(); + if (status.Fail()) + return status; + + for (const auto &thread : m_threads) + static_cast(*thread).SetStoppedBySignal(SIGSTOP); + + // Let our process instance know the thread has stopped. + SetState(StateType::eStateStopped); + return Status(); +} + +Status NativeProcessFreeBSD::ReadMemory(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) { + unsigned char *dst = static_cast(buf); + struct ptrace_io_desc io; + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + bytes_read = 0; + io.piod_op = PIOD_READ_D; + io.piod_len = size; + + do { + io.piod_offs = (void *)(addr + bytes_read); + io.piod_addr = dst + bytes_read; + + Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io); + if (error.Fail() || io.piod_len == 0) + return error; + + bytes_read += io.piod_len; + io.piod_len = size - bytes_read; + } while (bytes_read < size); + + return Status(); +} + +Status NativeProcessFreeBSD::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + const unsigned char *src = static_cast(buf); + Status error; + struct ptrace_io_desc io; + + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + bytes_written = 0; + io.piod_op = PIOD_WRITE_D; + io.piod_len = size; + + do { + io.piod_addr = + const_cast(static_cast(src + bytes_written)); + io.piod_offs = (void *)(addr + bytes_written); + + Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io); + if (error.Fail() || io.piod_len == 0) + return error; + + bytes_written += io.piod_len; + io.piod_len = size - bytes_written; + } while (bytes_written < size); + + return error; +} + +llvm::ErrorOr> +NativeProcessFreeBSD::GetAuxvData() const { + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, static_cast(GetID())}; + size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); + std::unique_ptr buf = + llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size); + + if (::sysctl(mib, 4, buf->getBufferStart(), &auxv_size, nullptr, 0) != 0) + return std::error_code(errno, std::generic_category()); + + return buf; +} + +Status NativeProcessFreeBSD::ReinitializeThreads() { + // Clear old threads + m_threads.clear(); + + int num_lwps; + Status error = PtraceWrapper(PT_GETNUMLWPS, GetID(), nullptr, 0, &num_lwps); + if (error.Fail()) + return error; + + std::vector lwp_ids; + lwp_ids.resize(num_lwps); + error = PtraceWrapper(PT_GETLWPLIST, GetID(), lwp_ids.data(), + lwp_ids.size() * sizeof(lwpid_t), &num_lwps); + if (error.Fail()) + return error; + + // Reinitialize from scratch threads and register them in process + for (lwpid_t lwp : lwp_ids) + AddThread(lwp); + + return error; +} diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h new file mode 100644 index 00000000000000..e1334c3bcf5d65 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h @@ -0,0 +1,121 @@ +//===-- NativeProcessFreeBSD.h -------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessFreeBSD_H_ +#define liblldb_NativeProcessFreeBSD_H_ + +#include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" + +#include "NativeThreadFreeBSD.h" + +namespace lldb_private { +namespace process_freebsd { +/// \class NativeProcessFreeBSD +/// Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process +/// for debugging. +/// +/// Changes in the inferior process state are broadcasted. +class NativeProcessFreeBSD : public NativeProcessELF { +public: + class Factory : public NativeProcessProtocol::Factory { + public: + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + + llvm::Expected> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + }; + + // NativeProcessProtocol Interface + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + Status AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) override; + + Status DeallocateMemory(lldb::addr_t addr) override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + llvm::ErrorOr> + GetAuxvData() const override; + + // Interface used by NativeRegisterContext-derived classes. + static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, + int data = 0, int *result = nullptr); + +private: + MainLoop::SignalHandleUP m_sigchld_handle; + ArchSpec m_arch; + LazyBool m_supports_mem_region = eLazyBoolCalculate; + std::vector> m_mem_region_cache; + + // Private Instance Methods + NativeProcessFreeBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate, + const ArchSpec &arch, MainLoop &mainloop); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + NativeThreadFreeBSD &AddThread(lldb::tid_t thread_id); + void RemoveThread(lldb::tid_t thread_id); + + void MonitorCallback(lldb::pid_t pid, int signal); + void MonitorExited(lldb::pid_t pid, WaitStatus status); + void MonitorSIGSTOP(lldb::pid_t pid); + void MonitorSIGTRAP(lldb::pid_t pid); + void MonitorSignal(lldb::pid_t pid, int signal); + + Status PopulateMemoryRegionCache(); + void SigchldHandler(); + + Status Attach(); + Status ReinitializeThreads(); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessFreeBSD_H_ diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp new file mode 100644 index 00000000000000..2a7dc3dbc44190 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.cpp @@ -0,0 +1,39 @@ +//===-- NativeRegisterContextFreeBSD.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextFreeBSD.h" + +#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" + +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +// clang-format off +#include +#include +// clang-format on + +NativeRegisterContextFreeBSD::NativeRegisterContextFreeBSD( + NativeThreadProtocol &native_thread, + RegisterInfoInterface *reg_info_interface_p) + : NativeRegisterContextRegisterInfo(native_thread, reg_info_interface_p) {} + +Status NativeRegisterContextFreeBSD::DoRegisterSet(int ptrace_req, void *buf) { + return NativeProcessFreeBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf, + m_thread.GetID()); +} + +NativeProcessFreeBSD &NativeRegisterContextFreeBSD::GetProcess() { + return static_cast(m_thread.GetProcess()); +} + +::pid_t NativeRegisterContextFreeBSD::GetProcessPid() { + return GetProcess().GetID(); +} diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h new file mode 100644 index 00000000000000..db32e216c9253d --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h @@ -0,0 +1,48 @@ +//===-- NativeRegisterContextFreeBSD.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextFreeBSD_h +#define lldb_NativeRegisterContextFreeBSD_h + +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD : public NativeRegisterContextRegisterInfo { +public: + NativeRegisterContextFreeBSD(NativeThreadProtocol &native_thread, + RegisterInfoInterface *reg_info_interface_p); + + // This function is implemented in the NativeRegisterContextFreeBSD_* + // subclasses to create a new instance of the host specific + // NativeRegisterContextFreeBSD. The implementations can't collide as only one + // NativeRegisterContextFreeBSD_* variant should be compiled into the final + // executable. + static NativeRegisterContextFreeBSD * + CreateHostNativeRegisterContextFreeBSD(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + virtual Status + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0; + + virtual Status ClearWatchpointHit(uint32_t wp_index) = 0; + +protected: + Status DoRegisterSet(int req, void *buf); + virtual NativeProcessFreeBSD &GetProcess(); + virtual ::pid_t GetProcessPid(); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_h diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp new file mode 100644 index 00000000000000..157b2167f2ff9e --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.cpp @@ -0,0 +1,1315 @@ +//===-- NativeRegisterContextFreeBSD_x86_64.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include "NativeRegisterContextFreeBSD_x86_64.h" + +#include + +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" + +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +// Private namespace. + +namespace { +// x86 64-bit general purpose registers. +static const uint32_t g_gpr_regnums_x86_64[] = { + lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, + lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64, + lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, + lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64, + lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64, + lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64, + lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64, + lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits or r8 + lldb_r9d_x86_64, // Low 32 bits or r9 + lldb_r10d_x86_64, // Low 32 bits or r10 + lldb_r11d_x86_64, // Low 32 bits or r11 + lldb_r12d_x86_64, // Low 32 bits or r12 + lldb_r13d_x86_64, // Low 32 bits or r13 + lldb_r14d_x86_64, // Low 32 bits or r14 + lldb_r15d_x86_64, // Low 32 bits or r15 + lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64, + lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits or r8 + lldb_r9w_x86_64, // Low 16 bits or r9 + lldb_r10w_x86_64, // Low 16 bits or r10 + lldb_r11w_x86_64, // Low 16 bits or r11 + lldb_r12w_x86_64, // Low 16 bits or r12 + lldb_r13w_x86_64, // Low 16 bits or r13 + lldb_r14w_x86_64, // Low 16 bits or r14 + lldb_r15w_x86_64, // Low 16 bits or r15 + lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64, + lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64, + lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits or r8 + lldb_r9l_x86_64, // Low 8 bits or r9 + lldb_r10l_x86_64, // Low 8 bits or r10 + lldb_r11l_x86_64, // Low 8 bits or r11 + lldb_r12l_x86_64, // Low 8 bits or r12 + lldb_r13l_x86_64, // Low 8 bits or r13 + lldb_r14l_x86_64, // Low 8 bits or r14 + lldb_r15l_x86_64, // Low 8 bits or r15 + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - + 1 == + k_num_gpr_registers_x86_64, + "g_gpr_regnums_x86_64 has wrong number of register infos"); + +// x86 64-bit floating point registers. +static const uint32_t g_fpu_regnums_x86_64[] = { + lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64, + lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64, + lldb_foseg_x86_64, lldb_fooff_x86_64, lldb_mxcsr_x86_64, + lldb_mxcsrmask_x86_64, lldb_st0_x86_64, lldb_st1_x86_64, + lldb_st2_x86_64, lldb_st3_x86_64, lldb_st4_x86_64, + lldb_st5_x86_64, lldb_st6_x86_64, lldb_st7_x86_64, + lldb_mm0_x86_64, lldb_mm1_x86_64, lldb_mm2_x86_64, + lldb_mm3_x86_64, lldb_mm4_x86_64, lldb_mm5_x86_64, + lldb_mm6_x86_64, lldb_mm7_x86_64, lldb_xmm0_x86_64, + lldb_xmm1_x86_64, lldb_xmm2_x86_64, lldb_xmm3_x86_64, + lldb_xmm4_x86_64, lldb_xmm5_x86_64, lldb_xmm6_x86_64, + lldb_xmm7_x86_64, lldb_xmm8_x86_64, lldb_xmm9_x86_64, + lldb_xmm10_x86_64, lldb_xmm11_x86_64, lldb_xmm12_x86_64, + lldb_xmm13_x86_64, lldb_xmm14_x86_64, lldb_xmm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - + 1 == + k_num_fpr_registers_x86_64, + "g_fpu_regnums_x86_64 has wrong number of register infos"); + +// x86 64-bit registers available via XState. +static const uint32_t g_xstate_regnums_x86_64[] = { + lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64, + lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64, + lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64, + lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64, + // Note: we currently do not provide them but this is needed to avoid + // unnamed groups in SBFrame::GetRegisterContext(). + lldb_bnd0_x86_64, lldb_bnd1_x86_64, lldb_bnd2_x86_64, lldb_bnd3_x86_64, + lldb_bndcfgu_x86_64, lldb_bndstatus_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_xstate_regnums_x86_64) / + sizeof(g_xstate_regnums_x86_64[0])) - + 1 == + k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64, + "g_xstate_regnums_x86_64 has wrong number of register infos"); + +// x86 debug registers. +static const uint32_t g_dbr_regnums_x86_64[] = { + lldb_dr0_x86_64, lldb_dr1_x86_64, lldb_dr2_x86_64, lldb_dr3_x86_64, + lldb_dr4_x86_64, lldb_dr5_x86_64, lldb_dr6_x86_64, lldb_dr7_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_dbr_regnums_x86_64) / sizeof(g_dbr_regnums_x86_64[0])) - + 1 == + k_num_dbr_registers_x86_64, + "g_dbr_regnums_x86_64 has wrong number of register infos"); + +// x86 32-bit general purpose registers. +const uint32_t g_gpr_regnums_i386[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + lldb_ax_i386, lldb_bx_i386, lldb_cx_i386, lldb_dx_i386, + lldb_di_i386, lldb_si_i386, lldb_bp_i386, lldb_sp_i386, + lldb_ah_i386, lldb_bh_i386, lldb_ch_i386, lldb_dh_i386, + lldb_al_i386, lldb_bl_i386, lldb_cl_i386, lldb_dl_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - + 1 == + k_num_gpr_registers_i386, + "g_gpr_regnums_i386 has wrong number of register infos"); + +// x86 32-bit floating point registers. +const uint32_t g_fpu_regnums_i386[] = { + lldb_fctrl_i386, lldb_fstat_i386, lldb_ftag_i386, lldb_fop_i386, + lldb_fiseg_i386, lldb_fioff_i386, lldb_foseg_i386, lldb_fooff_i386, + lldb_mxcsr_i386, lldb_mxcsrmask_i386, lldb_st0_i386, lldb_st1_i386, + lldb_st2_i386, lldb_st3_i386, lldb_st4_i386, lldb_st5_i386, + lldb_st6_i386, lldb_st7_i386, lldb_mm0_i386, lldb_mm1_i386, + lldb_mm2_i386, lldb_mm3_i386, lldb_mm4_i386, lldb_mm5_i386, + lldb_mm6_i386, lldb_mm7_i386, lldb_xmm0_i386, lldb_xmm1_i386, + lldb_xmm2_i386, lldb_xmm3_i386, lldb_xmm4_i386, lldb_xmm5_i386, + lldb_xmm6_i386, lldb_xmm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - + 1 == + k_num_fpr_registers_i386, + "g_fpu_regnums_i386 has wrong number of register infos"); + +// x86 64-bit registers available via XState. +static const uint32_t g_xstate_regnums_i386[] = { + lldb_ymm0_i386, lldb_ymm1_i386, lldb_ymm2_i386, lldb_ymm3_i386, + lldb_ymm4_i386, lldb_ymm5_i386, lldb_ymm6_i386, lldb_ymm7_i386, + // Note: we currently do not provide them but this is needed to avoid + // unnamed groups in SBFrame::GetRegisterContext(). + lldb_bnd0_i386, lldb_bnd1_i386, lldb_bnd2_i386, lldb_bnd3_i386, + lldb_bndcfgu_i386, lldb_bndstatus_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert( + (sizeof(g_xstate_regnums_i386) / sizeof(g_xstate_regnums_i386[0])) - 1 == + k_num_avx_registers_i386 + k_num_mpx_registers_i386, + "g_xstate_regnums_i386 has wrong number of register infos"); + +// x86 debug registers. +static const uint32_t g_dbr_regnums_i386[] = { + lldb_dr0_i386, lldb_dr1_i386, lldb_dr2_i386, lldb_dr3_i386, + lldb_dr4_i386, lldb_dr5_i386, lldb_dr6_i386, lldb_dr7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_dbr_regnums_i386) / sizeof(g_dbr_regnums_i386[0])) - + 1 == + k_num_dbr_registers_i386, + "g_dbr_regnums_i386 has wrong number of register infos"); + +// Number of register sets provided by this context. +enum { k_num_register_sets = 4 }; + +// Register sets for x86 32-bit. +static const RegisterSet g_reg_sets_i386[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_i386, + g_gpr_regnums_i386}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_i386, + g_fpu_regnums_i386}, + {"Extended State Registers", "xstate", + k_num_avx_registers_i386 + k_num_mpx_registers_i386, + g_xstate_regnums_i386}, + {"Debug Registers", "dbr", k_num_dbr_registers_i386, g_dbr_regnums_i386}, +}; + +// Register sets for x86 64-bit. +static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, + g_gpr_regnums_x86_64}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, + g_fpu_regnums_x86_64}, + {"Extended State Registers", "xstate", + k_num_avx_registers_x86_64 + k_num_mpx_registers_x86_64, + g_xstate_regnums_x86_64}, + {"Debug Registers", "dbr", k_num_dbr_registers_x86_64, + g_dbr_regnums_x86_64}, +}; + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize()) +} // namespace + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + return new NativeRegisterContextFreeBSD_x86_64(target_arch, native_thread); +} + +// NativeRegisterContextFreeBSD_x86_64 members. + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) { + // 32-bit hosts run with a RegisterContextFreeBSD_i386 context. + return new RegisterContextFreeBSD_i386(target_arch); + } else { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the + // x86_64 register context. + return new RegisterContextFreeBSD_x86_64(target_arch); + } +} + +NativeRegisterContextFreeBSD_x86_64::NativeRegisterContextFreeBSD_x86_64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextFreeBSD(native_thread, + CreateRegisterInfoInterface(target_arch)), + m_gpr(), m_fpr(), m_dbr() {} + +// CONSIDER after local and llgs debugging are merged, register set support can +// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual. +uint32_t NativeRegisterContextFreeBSD_x86_64::GetRegisterSetCount() const { + uint32_t sets = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) { + if (GetSetForNativeRegNum(set_index) != -1) + ++sets; + } + + return sets; +} + +const RegisterSet * +NativeRegisterContextFreeBSD_x86_64::GetRegisterSet(uint32_t set_index) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return &g_reg_sets_i386[set_index]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set_index]; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +static constexpr int RegNumX86ToX86_64(int regnum) { + switch (regnum) { + case lldb_eax_i386: + return lldb_rax_x86_64; + case lldb_ebx_i386: + return lldb_rbx_x86_64; + case lldb_ecx_i386: + return lldb_rcx_x86_64; + case lldb_edx_i386: + return lldb_rdx_x86_64; + case lldb_edi_i386: + return lldb_rdi_x86_64; + case lldb_esi_i386: + return lldb_rsi_x86_64; + case lldb_ebp_i386: + return lldb_rbp_x86_64; + case lldb_esp_i386: + return lldb_rsp_x86_64; + case lldb_eip_i386: + return lldb_rip_x86_64; + case lldb_eflags_i386: + return lldb_rflags_x86_64; + case lldb_cs_i386: + return lldb_cs_x86_64; + case lldb_fs_i386: + return lldb_fs_x86_64; + case lldb_gs_i386: + return lldb_gs_x86_64; + case lldb_ss_i386: + return lldb_ss_x86_64; + case lldb_ds_i386: + return lldb_ds_x86_64; + case lldb_es_i386: + return lldb_es_x86_64; + case lldb_fctrl_i386: + return lldb_fctrl_x86_64; + case lldb_fstat_i386: + return lldb_fstat_x86_64; + case lldb_ftag_i386: + return lldb_ftag_x86_64; + case lldb_fop_i386: + return lldb_fop_x86_64; + case lldb_fiseg_i386: + return lldb_fiseg_x86_64; + case lldb_fioff_i386: + return lldb_fioff_x86_64; + case lldb_foseg_i386: + return lldb_foseg_x86_64; + case lldb_fooff_i386: + return lldb_fooff_x86_64; + case lldb_mxcsr_i386: + return lldb_mxcsr_x86_64; + case lldb_mxcsrmask_i386: + return lldb_mxcsrmask_x86_64; + case lldb_st0_i386: + case lldb_st1_i386: + case lldb_st2_i386: + case lldb_st3_i386: + case lldb_st4_i386: + case lldb_st5_i386: + case lldb_st6_i386: + case lldb_st7_i386: + return lldb_st0_x86_64 + regnum - lldb_st0_i386; + case lldb_mm0_i386: + case lldb_mm1_i386: + case lldb_mm2_i386: + case lldb_mm3_i386: + case lldb_mm4_i386: + case lldb_mm5_i386: + case lldb_mm6_i386: + case lldb_mm7_i386: + return lldb_mm0_x86_64 + regnum - lldb_mm0_i386; + case lldb_xmm0_i386: + case lldb_xmm1_i386: + case lldb_xmm2_i386: + case lldb_xmm3_i386: + case lldb_xmm4_i386: + case lldb_xmm5_i386: + case lldb_xmm6_i386: + case lldb_xmm7_i386: + return lldb_xmm0_x86_64 + regnum - lldb_xmm0_i386; + case lldb_ymm0_i386: + case lldb_ymm1_i386: + case lldb_ymm2_i386: + case lldb_ymm3_i386: + case lldb_ymm4_i386: + case lldb_ymm5_i386: + case lldb_ymm6_i386: + case lldb_ymm7_i386: + return lldb_ymm0_x86_64 + regnum - lldb_ymm0_i386; + case lldb_bnd0_i386: + case lldb_bnd1_i386: + case lldb_bnd2_i386: + case lldb_bnd3_i386: + return lldb_bnd0_x86_64 + regnum - lldb_bnd0_i386; + case lldb_bndcfgu_i386: + return lldb_bndcfgu_x86_64; + case lldb_bndstatus_i386: + return lldb_bndstatus_x86_64; + case lldb_dr0_i386: + case lldb_dr1_i386: + case lldb_dr2_i386: + case lldb_dr3_i386: + case lldb_dr4_i386: + case lldb_dr5_i386: + case lldb_dr6_i386: + case lldb_dr7_i386: + return lldb_dr0_x86_64 + regnum - lldb_dr0_i386; + default: + llvm_unreachable("Unhandled i386 register."); + } +} + +int NativeRegisterContextFreeBSD_x86_64::GetSetForNativeRegNum( + int reg_num) const { + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + if (reg_num >= k_first_gpr_i386 && reg_num <= k_last_gpr_i386) + return GPRegSet; + if (reg_num >= k_first_fpr_i386 && reg_num <= k_last_fpr_i386) + return FPRegSet; + if (reg_num >= k_first_avx_i386 && reg_num <= k_last_avx_i386) + return -1; // AVX + if (reg_num >= k_first_mpxr_i386 && reg_num <= k_last_mpxr_i386) + return -1; // MPXR + if (reg_num >= k_first_mpxc_i386 && reg_num <= k_last_mpxc_i386) + return -1; // MPXC + if (reg_num >= k_first_dbr_i386 && reg_num <= k_last_dbr_i386) + return DBRegSet; // DBR + break; + case llvm::Triple::x86_64: + if (reg_num >= k_first_gpr_x86_64 && reg_num <= k_last_gpr_x86_64) + return GPRegSet; + if (reg_num >= k_first_fpr_x86_64 && reg_num <= k_last_fpr_x86_64) + return FPRegSet; + if (reg_num >= k_first_avx_x86_64 && reg_num <= k_last_avx_x86_64) + return -1; // AVX + if (reg_num >= k_first_mpxr_x86_64 && reg_num <= k_last_mpxr_x86_64) + return -1; // MPXR + if (reg_num >= k_first_mpxc_x86_64 && reg_num <= k_last_mpxc_x86_64) + return -1; // MPXC + if (reg_num >= k_first_dbr_x86_64 && reg_num <= k_last_dbr_x86_64) + return DBRegSet; // DBR + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + llvm_unreachable("Register does not belong to any register set"); +} + +Status NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet(uint32_t set) { + switch (set) { + case GPRegSet: + return DoRegisterSet(PT_GETREGS, &m_gpr); + case FPRegSet: +#if defined(__x86_64__) + return DoRegisterSet(PT_GETFPREGS, &m_fpr); +#else + return DoRegisterSet(PT_GETXMMREGS, &m_fpr); +#endif + case DBRegSet: + return DoRegisterSet(PT_GETDBREGS, &m_dbr); + } + llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet(uint32_t set) { + switch (set) { + case GPRegSet: + return DoRegisterSet(PT_SETREGS, &m_gpr); + case FPRegSet: +#if defined(__x86_64__) + return DoRegisterSet(PT_SETFPREGS, &m_fpr); +#else + return DoRegisterSet(PT_SETXMMREGS, &m_fpr); +#endif + case DBRegSet: + return DoRegisterSet(PT_SETDBREGS, &m_dbr); + } + llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + int set = GetSetForNativeRegNum(reg); + if (set == -1) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86_64: + break; + case llvm::Triple::x86: + reg = RegNumX86ToX86_64(reg); + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + switch (reg) { +#if defined(__x86_64__) + case lldb_rax_x86_64: + reg_value = (uint64_t)m_gpr.r_rax; + break; + case lldb_rbx_x86_64: + reg_value = (uint64_t)m_gpr.r_rbx; + break; + case lldb_rcx_x86_64: + reg_value = (uint64_t)m_gpr.r_rcx; + break; + case lldb_rdx_x86_64: + reg_value = (uint64_t)m_gpr.r_rdx; + break; + case lldb_rdi_x86_64: + reg_value = (uint64_t)m_gpr.r_rdi; + break; + case lldb_rsi_x86_64: + reg_value = (uint64_t)m_gpr.r_rsi; + break; + case lldb_rbp_x86_64: + reg_value = (uint64_t)m_gpr.r_rbp; + break; + case lldb_rsp_x86_64: + reg_value = (uint64_t)m_gpr.r_rsp; + break; + case lldb_r8_x86_64: + reg_value = (uint64_t)m_gpr.r_r8; + break; + case lldb_r9_x86_64: + reg_value = (uint64_t)m_gpr.r_r9; + break; + case lldb_r10_x86_64: + reg_value = (uint64_t)m_gpr.r_r10; + break; + case lldb_r11_x86_64: + reg_value = (uint64_t)m_gpr.r_r11; + break; + case lldb_r12_x86_64: + reg_value = (uint64_t)m_gpr.r_r12; + break; + case lldb_r13_x86_64: + reg_value = (uint64_t)m_gpr.r_r13; + break; + case lldb_r14_x86_64: + reg_value = (uint64_t)m_gpr.r_r14; + break; + case lldb_r15_x86_64: + reg_value = (uint64_t)m_gpr.r_r15; + break; + case lldb_rip_x86_64: + reg_value = (uint64_t)m_gpr.r_rip; + break; + case lldb_rflags_x86_64: + reg_value = (uint64_t)m_gpr.r_rflags; + break; + case lldb_cs_x86_64: + reg_value = (uint64_t)m_gpr.r_cs; + break; + case lldb_fs_x86_64: + reg_value = (uint64_t)m_gpr.r_fs; + break; + case lldb_gs_x86_64: + reg_value = (uint64_t)m_gpr.r_gs; + break; + case lldb_ss_x86_64: + reg_value = (uint64_t)m_gpr.r_ss; + break; + case lldb_ds_x86_64: + reg_value = (uint64_t)m_gpr.r_ds; + break; + case lldb_es_x86_64: + reg_value = (uint64_t)m_gpr.r_es; + break; +#else + case lldb_rax_x86_64: + reg_value = (uint32_t)m_gpr.r_eax; + break; + case lldb_rbx_x86_64: + reg_value = (uint32_t)m_gpr.r_ebx; + break; + case lldb_rcx_x86_64: + reg_value = (uint32_t)m_gpr.r_ecx; + break; + case lldb_rdx_x86_64: + reg_value = (uint32_t)m_gpr.r_edx; + break; + case lldb_rdi_x86_64: + reg_value = (uint32_t)m_gpr.r_edi; + break; + case lldb_rsi_x86_64: + reg_value = (uint32_t)m_gpr.r_esi; + break; + case lldb_rsp_x86_64: + reg_value = (uint32_t)m_gpr.r_esp; + break; + case lldb_rbp_x86_64: + reg_value = (uint32_t)m_gpr.r_ebp; + break; + case lldb_rip_x86_64: + reg_value = (uint32_t)m_gpr.r_eip; + break; + case lldb_rflags_x86_64: + reg_value = (uint32_t)m_gpr.r_eflags; + break; + case lldb_cs_x86_64: + reg_value = (uint32_t)m_gpr.r_cs; + break; + case lldb_fs_x86_64: + reg_value = (uint32_t)m_gpr.r_fs; + break; + case lldb_gs_x86_64: + reg_value = (uint32_t)m_gpr.r_gs; + break; + case lldb_ss_x86_64: + reg_value = (uint32_t)m_gpr.r_ss; + break; + case lldb_ds_x86_64: + reg_value = (uint32_t)m_gpr.r_ds; + break; + case lldb_es_x86_64: + reg_value = (uint32_t)m_gpr.r_es; + break; +#endif +#if defined(__x86_64__) +// the 32-bit field carries more detail, so we don't have to reinvent +// the wheel +#define FPR_ENV(x) ((struct envxmm32 *)m_fpr.fpr_env)->x +#else +#define FPR_ENV(x) ((struct envxmm *)m_fpr.xmm_env)->x +#endif + case lldb_fctrl_x86_64: + reg_value = (uint16_t)FPR_ENV(en_cw); + break; + case lldb_fstat_x86_64: + reg_value = (uint16_t)FPR_ENV(en_sw); + break; + case lldb_ftag_x86_64: + reg_value = (uint16_t)FPR_ENV(en_tw); + break; + case lldb_fop_x86_64: + reg_value = (uint16_t)FPR_ENV(en_opcode); + break; + case lldb_fiseg_x86_64: + reg_value = (uint32_t)FPR_ENV(en_fcs); + break; + case lldb_fioff_x86_64: + reg_value = (uint32_t)FPR_ENV(en_fip); + break; + case lldb_foseg_x86_64: + reg_value = (uint32_t)FPR_ENV(en_fos); + break; + case lldb_fooff_x86_64: + reg_value = (uint32_t)FPR_ENV(en_foo); + break; + case lldb_mxcsr_x86_64: + reg_value = (uint32_t)FPR_ENV(en_mxcsr); + break; + case lldb_mxcsrmask_x86_64: + reg_value = (uint32_t)FPR_ENV(en_mxcsr_mask); + break; + case lldb_st0_x86_64: + case lldb_st1_x86_64: + case lldb_st2_x86_64: + case lldb_st3_x86_64: + case lldb_st4_x86_64: + case lldb_st5_x86_64: + case lldb_st6_x86_64: + case lldb_st7_x86_64: +#if defined(__x86_64__) + reg_value.SetBytes(&m_fpr.fpr_acc[reg - lldb_st0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#else + reg_value.SetBytes(&m_fpr.xmm_acc[reg - lldb_st0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#endif + break; + case lldb_mm0_x86_64: + case lldb_mm1_x86_64: + case lldb_mm2_x86_64: + case lldb_mm3_x86_64: + case lldb_mm4_x86_64: + case lldb_mm5_x86_64: + case lldb_mm6_x86_64: + case lldb_mm7_x86_64: +#if defined(__x86_64__) + reg_value.SetBytes(&m_fpr.fpr_acc[reg - lldb_mm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#else + reg_value.SetBytes(&m_fpr.xmm_acc[reg - lldb_mm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#endif + break; + case lldb_xmm0_x86_64: + case lldb_xmm1_x86_64: + case lldb_xmm2_x86_64: + case lldb_xmm3_x86_64: + case lldb_xmm4_x86_64: + case lldb_xmm5_x86_64: + case lldb_xmm6_x86_64: + case lldb_xmm7_x86_64: + case lldb_xmm8_x86_64: + case lldb_xmm9_x86_64: + case lldb_xmm10_x86_64: + case lldb_xmm11_x86_64: + case lldb_xmm12_x86_64: + case lldb_xmm13_x86_64: + case lldb_xmm14_x86_64: + case lldb_xmm15_x86_64: +#if defined(__x86_64__) + reg_value.SetBytes(&m_fpr.fpr_xacc[reg - lldb_xmm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#else + reg_value.SetBytes(&m_fpr.xmm_reg[reg - lldb_xmm0_x86_64], + reg_info->byte_size, endian::InlHostByteOrder()); +#endif + break; + case lldb_dr0_x86_64: + case lldb_dr1_x86_64: + case lldb_dr2_x86_64: + case lldb_dr3_x86_64: + case lldb_dr4_x86_64: + case lldb_dr5_x86_64: + case lldb_dr6_x86_64: + case lldb_dr7_x86_64: + reg_value = (uint64_t)m_dbr.dr[reg - lldb_dr0_x86_64]; + break; + default: + llvm_unreachable("Reading unknown/unsupported register"); + } + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + int set = GetSetForNativeRegNum(reg); + if (set == -1) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86_64: + break; + case llvm::Triple::x86: + reg = RegNumX86ToX86_64(reg); + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + switch (reg) { +#if defined(__x86_64__) + case lldb_rax_x86_64: + m_gpr.r_rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + m_gpr.r_rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + m_gpr.r_rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + m_gpr.r_rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + m_gpr.r_rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + m_gpr.r_rsi = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + m_gpr.r_rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + m_gpr.r_rsp = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + m_gpr.r_r8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + m_gpr.r_r9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + m_gpr.r_r10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + m_gpr.r_r11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + m_gpr.r_r12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + m_gpr.r_r13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + m_gpr.r_r14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + m_gpr.r_r15 = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + m_gpr.r_rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + m_gpr.r_rflags = reg_value.GetAsUInt64(); + break; + case lldb_cs_x86_64: + m_gpr.r_cs = reg_value.GetAsUInt64(); + break; + case lldb_fs_x86_64: + m_gpr.r_fs = reg_value.GetAsUInt64(); + break; + case lldb_gs_x86_64: + m_gpr.r_gs = reg_value.GetAsUInt64(); + break; + case lldb_ss_x86_64: + m_gpr.r_ss = reg_value.GetAsUInt64(); + break; + case lldb_ds_x86_64: + m_gpr.r_ds = reg_value.GetAsUInt64(); + break; + case lldb_es_x86_64: + m_gpr.r_es = reg_value.GetAsUInt64(); + break; +#else + case lldb_rax_x86_64: + m_gpr.r_eax = reg_value.GetAsUInt32(); + break; + case lldb_rbx_x86_64: + m_gpr.r_ebx = reg_value.GetAsUInt32(); + break; + case lldb_rcx_x86_64: + m_gpr.r_ecx = reg_value.GetAsUInt32(); + break; + case lldb_rdx_x86_64: + m_gpr.r_edx = reg_value.GetAsUInt32(); + break; + case lldb_rdi_x86_64: + m_gpr.r_edi = reg_value.GetAsUInt32(); + break; + case lldb_rsi_x86_64: + m_gpr.r_esi = reg_value.GetAsUInt32(); + break; + case lldb_rsp_x86_64: + m_gpr.r_esp = reg_value.GetAsUInt32(); + break; + case lldb_rbp_x86_64: + m_gpr.r_ebp = reg_value.GetAsUInt32(); + break; + case lldb_rip_x86_64: + m_gpr.r_eip = reg_value.GetAsUInt32(); + break; + case lldb_rflags_x86_64: + m_gpr.r_eflags = reg_value.GetAsUInt32(); + break; + case lldb_cs_x86_64: + m_gpr.r_cs = reg_value.GetAsUInt32(); + break; + case lldb_fs_x86_64: + m_gpr.r_fs = reg_value.GetAsUInt32(); + break; + case lldb_gs_x86_64: + m_gpr.r_gs = reg_value.GetAsUInt32(); + break; + case lldb_ss_x86_64: + m_gpr.r_ss = reg_value.GetAsUInt32(); + break; + case lldb_ds_x86_64: + m_gpr.r_ds = reg_value.GetAsUInt32(); + break; + case lldb_es_x86_64: + m_gpr.r_es = reg_value.GetAsUInt32(); + break; +#endif + case lldb_fctrl_x86_64: + FPR_ENV(en_cw) = reg_value.GetAsUInt16(); + break; + case lldb_fstat_x86_64: + FPR_ENV(en_sw) = reg_value.GetAsUInt16(); + break; + case lldb_ftag_x86_64: + FPR_ENV(en_tw) = reg_value.GetAsUInt16(); + break; + case lldb_fop_x86_64: + FPR_ENV(en_opcode) = reg_value.GetAsUInt16(); + break; + case lldb_fiseg_x86_64: + FPR_ENV(en_fcs) = reg_value.GetAsUInt32(); + break; + case lldb_fioff_x86_64: + FPR_ENV(en_fip) = reg_value.GetAsUInt32(); + break; + case lldb_foseg_x86_64: + FPR_ENV(en_fos) = reg_value.GetAsUInt32(); + break; + case lldb_fooff_x86_64: + FPR_ENV(en_foo) = reg_value.GetAsUInt32(); + break; + case lldb_mxcsr_x86_64: + FPR_ENV(en_mxcsr) = reg_value.GetAsUInt32(); + break; + case lldb_mxcsrmask_x86_64: + FPR_ENV(en_mxcsr_mask) = reg_value.GetAsUInt32(); + break; + case lldb_st0_x86_64: + case lldb_st1_x86_64: + case lldb_st2_x86_64: + case lldb_st3_x86_64: + case lldb_st4_x86_64: + case lldb_st5_x86_64: + case lldb_st6_x86_64: + case lldb_st7_x86_64: +#if defined(__x86_64__) + ::memcpy(&m_fpr.fpr_acc[reg - lldb_st0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#else + ::memcpy(&m_fpr.xmm_acc[reg - lldb_st0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#endif + break; + case lldb_mm0_x86_64: + case lldb_mm1_x86_64: + case lldb_mm2_x86_64: + case lldb_mm3_x86_64: + case lldb_mm4_x86_64: + case lldb_mm5_x86_64: + case lldb_mm6_x86_64: + case lldb_mm7_x86_64: +#if defined(__x86_64__) + ::memcpy(&m_fpr.fpr_acc[reg - lldb_mm0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#else + ::memcpy(&m_fpr.xmm_acc[reg - lldb_mm0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#endif + break; + case lldb_xmm0_x86_64: + case lldb_xmm1_x86_64: + case lldb_xmm2_x86_64: + case lldb_xmm3_x86_64: + case lldb_xmm4_x86_64: + case lldb_xmm5_x86_64: + case lldb_xmm6_x86_64: + case lldb_xmm7_x86_64: + case lldb_xmm8_x86_64: + case lldb_xmm9_x86_64: + case lldb_xmm10_x86_64: + case lldb_xmm11_x86_64: + case lldb_xmm12_x86_64: + case lldb_xmm13_x86_64: + case lldb_xmm14_x86_64: + case lldb_xmm15_x86_64: +#if defined(__x86_64__) + ::memcpy(&m_fpr.fpr_xacc[reg - lldb_xmm0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#else + ::memcpy(&m_fpr.xmm_reg[reg - lldb_xmm0_x86_64], reg_value.GetBytes(), + reg_value.GetByteSize()); +#endif + break; + case lldb_dr0_x86_64: + case lldb_dr1_x86_64: + case lldb_dr2_x86_64: + case lldb_dr3_x86_64: + case lldb_dr4_x86_64: + case lldb_dr5_x86_64: + case lldb_dr6_x86_64: + case lldb_dr7_x86_64: + m_dbr.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64(); + break; + default: + llvm_unreachable("Reading unknown/unsupported register"); + } + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_x86_64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + Status error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + error = ReadRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &m_gpr, GetRegisterInfoInterface().GetGPRSize()); + dst += GetRegisterInfoInterface().GetGPRSize(); + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_x86_64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_x86_64::%s data_sp contained mismatched " + "data size, expected %zu, actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_x86_64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(&m_gpr, src, GetRegisterInfoInterface().GetGPRSize()); + + error = WriteRegisterSet(GPRegSet); + if (error.Fail()) + return error; + src += GetRegisterInfoInterface().GetGPRSize(); + + return error; +} + +int NativeRegisterContextFreeBSD_x86_64::GetDR(int num) const { + assert(num >= 0 && num <= 7); + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return lldb_dr0_i386 + num; + case llvm::Triple::x86_64: + return lldb_dr0_x86_64 + num; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +Status NativeRegisterContextFreeBSD_x86_64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(6)); + Status error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_hit = false; + return error; + } + + uint64_t status_bits = reg_value.GetAsUInt64(); + + is_hit = status_bits & (1 << wp_index); + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { + bool is_hit; + Status error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +Status +NativeRegisterContextFreeBSD_x86_64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(GetDR(7)); + Status error = ReadRegister(reg_info, reg_value); + if (error.Fail()) { + is_vacant = false; + return error; + } + + uint64_t control_bits = reg_value.GetAsUInt64(); + + is_vacant = !(control_bits & (1 << (2 * wp_index + 1))); + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write + // waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint + // hit. + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return Status("Invalid read/write bits for watchpoint"); + + if (size != 1 && size != 2 && size != 4 && size != 8) + return Status("Invalid size for watchpoint"); + + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (error.Fail()) + return error; + if (!is_vacant) + return Status("Watchpoint index not vacant"); + + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + RegisterValue dr7_value; + error = ReadRegister(reg_info_dr7, dr7_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1 << (2 * wp_index + 1); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = watch_flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = dr7_value.GetAsUInt64() & ~bit_mask; + + control_bits |= enable_bit | rw_bits | size_bits; + + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(GetDR(wp_index)); + RegisterValue drN_value; + error = ReadRegister(reg_info_drN, drN_value); + if (error.Fail()) + return error; + + // clear dr6 if address or bits changed (i.e. we're not reenabling the same + // watchpoint) + if (drN_value.GetAsUInt64() != addr || + (dr7_value.GetAsUInt64() & bit_mask) != (rw_bits | size_bits)) { + ClearWatchpointHit(wp_index); + + error = WriteRegister(reg_info_drN, RegisterValue(addr)); + if (error.Fail()) + return error; + } + + error = WriteRegister(reg_info_dr7, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error.Clear(); + return error; +} + +bool NativeRegisterContextFreeBSD_x86_64::ClearHardwareWatchpoint( + uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0-1, 2-3, 4-5 + // or 6-7 of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + RegisterValue reg_value; + Status error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return false; + uint64_t bit_mask = 0x3 << (2 * wp_index); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success(); +} + +Status +NativeRegisterContextFreeBSD_x86_64::ClearWatchpointHit(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + // for watchpoints 0, 1, 2, or 3, respectively, check bits 0, 1, 2, or 3 of + // the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6)); + RegisterValue reg_value; + Status error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return error; + + uint64_t bit_mask = 1 << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr6, RegisterValue(status_bits)); +} + +Status NativeRegisterContextFreeBSD_x86_64::ClearAllHardwareWatchpoints() { + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + const RegisterInfo *const reg_info_dr6 = GetRegisterInfoAtIndex(GetDR(6)); + Status error = ReadRegister(reg_info_dr6, reg_value); + if (error.Fail()) + return error; + uint64_t bit_mask = 0xF; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = WriteRegister(reg_info_dr6, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + const RegisterInfo *const reg_info_dr7 = GetRegisterInfoAtIndex(GetDR(7)); + error = ReadRegister(reg_info_dr7, reg_value); + if (error.Fail()) + return error; + bit_mask = 0xFF | (0xFFFF << 16); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return WriteRegister(reg_info_dr7, RegisterValue(control_bits)); +} + +uint32_t NativeRegisterContextFreeBSD_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) { + LLDB_LOGF(log, "NativeRegisterContextFreeBSD_x86_64::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextFreeBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue reg_value; + const RegisterInfo *const reg_info_drN = + GetRegisterInfoAtIndex(GetDR(wp_index)); + if (ReadRegister(reg_info_drN, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + return reg_value.GetAsUInt64(); +} + +uint32_t +NativeRegisterContextFreeBSD_x86_64::NumSupportedHardwareWatchpoints() { + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +Status NativeRegisterContextFreeBSD_x86_64::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + auto &r_source = static_cast(source); + Status res = r_source.ReadRegisterSet(DBRegSet); + if (!res.Fail()) { + // copy dbregs only if any watchpoints were set + if ((r_source.m_dbr.dr[7] & 0xFF) == 0) + return res; + + m_dbr = r_source.m_dbr; + res = WriteRegisterSet(DBRegSet); + } + return res; +} + +#endif // defined(__x86_64__) diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h new file mode 100644 index 00000000000000..261d75f2290f69 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD_x86_64.h @@ -0,0 +1,101 @@ +//===-- NativeRegisterContextFreeBSD_x86_64.h -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h +#define lldb_NativeRegisterContextFreeBSD_x86_64_h + +// clang-format off +#include +#include +#include +#include +// clang-format on + +#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_x86_64 + : public NativeRegisterContextFreeBSD { +public: + NativeRegisterContextFreeBSD_x86_64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearWatchpointHit(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + Status + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + // Private member types. + enum { GPRegSet, FPRegSet, DBRegSet }; + + // Private member variables. + struct reg m_gpr; +#if defined(__x86_64__) + struct fpreg m_fpr; +#else + struct xmmreg m_fpr; +#endif + struct dbreg m_dbr; + + int GetSetForNativeRegNum(int reg_num) const; + int GetDR(int num) const; + + Status ReadRegisterSet(uint32_t set); + Status WriteRegisterSet(uint32_t set); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h + +#endif // defined(__x86_64__) diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp new file mode 100644 index 00000000000000..1517e7ff8ab5d5 --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.cpp @@ -0,0 +1,216 @@ +//===-- NativeThreadFreeBSD.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeThreadFreeBSD.h" +#include "NativeRegisterContextFreeBSD.h" + +#include "NativeProcessFreeBSD.h" + +#include "Plugins/Process/POSIX/CrashReason.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/Errno.h" + +// clang-format off +#include +#include +#include +// clang-format on + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +NativeThreadFreeBSD::NativeThreadFreeBSD(NativeProcessFreeBSD &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + process.GetArchitecture(), *this)), + m_stop_description() {} + +Status NativeThreadFreeBSD::Resume() { + Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, m_process.GetID(), + nullptr, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, m_process.GetID(), + nullptr, GetID()); + if (ret.Success()) + SetRunning(); + return ret; +} + +Status NativeThreadFreeBSD::SingleStep() { + Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, m_process.GetID(), + nullptr, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessFreeBSD::PtraceWrapper(PT_SETSTEP, m_process.GetID(), + nullptr, GetID()); + if (ret.Success()) + SetStepping(); + return ret; +} + +Status NativeThreadFreeBSD::Suspend() { + Status ret = NativeProcessFreeBSD::PtraceWrapper( + PT_SUSPEND, m_process.GetID(), nullptr, GetID()); + if (ret.Success()) + SetStopped(); + return ret; +} + +void NativeThreadFreeBSD::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.details.signal.signo = signo; + + m_stop_description.clear(); + if (info) { + switch (signo) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + const auto reason = GetCrashReason(*info); + m_stop_description = GetCrashReasonString(reason, *info); + break; + } + } +} + +void NativeThreadFreeBSD::SetStoppedByBreakpoint() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByTrace() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByExec() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.details.signal.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByWatchpoint(uint32_t wp_index) { + SetStopped(); +} + +void NativeThreadFreeBSD::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.details.signal.signo = 0; +} + +void NativeThreadFreeBSD::SetStopped() { + const StateType new_state = StateType::eStateStopped; + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadFreeBSD::SetRunning() { + m_state = StateType::eStateRunning; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void NativeThreadFreeBSD::SetStepping() { + m_state = StateType::eStateStepping; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +std::string NativeThreadFreeBSD::GetName() { return ""; } + +lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; } + +bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + stop_info = m_stop_info; + description = m_stop_description; + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(), + StateAsCString(m_state)); + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +NativeRegisterContextFreeBSD &NativeThreadFreeBSD::GetRegisterContext() { + assert(m_reg_context_up); + return *m_reg_context_up; +} + +Status NativeThreadFreeBSD::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + return Status("not implemented"); +} + +Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) { + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + return Status("not implemented"); +} + +Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + if (m_state == eStateLaunching) + return Status(); + + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + return Status("not implemented"); +} + +Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Status(); + + return Status("not implemented"); +} + +Status NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) { + return Status("not implemented"); +} diff --git a/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h new file mode 100644 index 00000000000000..e4d4941747364f --- /dev/null +++ b/lldb/source/Plugins/Process/FreeBSDRemote/NativeThreadFreeBSD.h @@ -0,0 +1,83 @@ +//===-- NativeThreadFreeBSD.h --------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadFreeBSD_H_ +#define liblldb_NativeThreadFreeBSD_H_ + +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h" + +#include +#include +#include + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeThreadFreeBSD : public NativeThreadProtocol { + friend class NativeProcessFreeBSD; + +public: + NativeThreadFreeBSD(NativeProcessFreeBSD &process, lldb::tid_t tid); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + NativeRegisterContextFreeBSD &GetRegisterContext() override; + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + +private: + // Interface for friend classes + + Status Resume(); + Status SingleStep(); + Status Suspend(); + + void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + void SetStoppedByBreakpoint(); + void SetStoppedByTrace(); + void SetStoppedByExec(); + void SetStoppedByWatchpoint(uint32_t wp_index); + void SetStoppedWithNoReason(); + void SetStopped(); + void SetRunning(); + void SetStepping(); + + Status CopyWatchpointsFrom(NativeThreadFreeBSD &source); + + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr m_reg_context_up; + std::string m_stop_description; + using WatchpointIndexMap = std::map; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; +}; + +typedef std::shared_ptr NativeThreadFreeBSDSP; +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadFreeBSD_H_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp index 08d48985179980..1ca0290eda13d7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -843,7 +843,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported( response.PutCString(";QListThreadsInStopReply+"); response.PutCString(";qEcho+"); response.PutCString(";qXfer:features:read+"); -#if defined(__linux__) || defined(__NetBSD__) +#if defined(__linux__) || defined(__NetBSD__) || defined(__FreeBSD__) response.PutCString(";QPassSignals+"); response.PutCString(";qXfer:auxv:read+"); response.PutCString(";qXfer:libraries-svr4:read+"); diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py index 2ee646e3fc7df7..56357c603ae339 100644 --- a/lldb/test/Shell/lit.cfg.py +++ b/lldb/test/Shell/lit.cfg.py @@ -133,3 +133,6 @@ def calculate_arch_features(arch_string): can_set_dbregs = False if can_set_dbregs: config.available_features.add('dbregs-set') + +# pass control variable through +llvm_config.with_system_environment('FREEBSD_REMOTE_PLUGIN') diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 02453b205deb1f..6e7b30df5c581d 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -4,6 +4,12 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android") list(APPEND LLDB_PLUGINS lldbPluginProcessLinux) endif() +if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + list(APPEND LLDB_PLUGINS + lldbPluginProcessFreeBSDRemote + lldbPluginProcessFreeBSD) +endif() + if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD) endif() diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 7f53756424c670..633e37c3a0435b 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -38,6 +38,8 @@ #if defined(__linux__) #include "Plugins/Process/Linux/NativeProcessLinux.h" +#elif defined(__FreeBSD__) +#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h" #elif defined(__NetBSD__) #include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" #elif defined(_WIN32) @@ -61,6 +63,8 @@ using namespace lldb_private::process_gdb_remote; namespace { #if defined(__linux__) typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory; +#elif defined(__FreeBSD__) +typedef process_freebsd::NativeProcessFreeBSD::Factory NativeProcessFactory; #elif defined(__NetBSD__) typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory; #elif defined(_WIN32) From c78fecba326ca493d7db2416e101b4c91676dc6c Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 8 Oct 2020 10:08:38 -0400 Subject: [PATCH 2/9] [gn build] (manually) port 9b58b0c06e6 --- llvm/utils/gn/secondary/lld/test/BUILD.gn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/utils/gn/secondary/lld/test/BUILD.gn b/llvm/utils/gn/secondary/lld/test/BUILD.gn index 00cb2f2c024c88..c5b53232443e2f 100644 --- a/llvm/utils/gn/secondary/lld/test/BUILD.gn +++ b/llvm/utils/gn/secondary/lld/test/BUILD.gn @@ -35,7 +35,8 @@ write_lit_cfg("lit_site_cfg") { input = "//lld/test/lit.site.cfg.py.in" output = lld_lit_site_cfg_file - extra_values = [] + extra_values = [ "LLD_DEFAULT_LD_LLD_IS_MINGW=" ] + if (llvm_enable_dia_sdk) { extra_values += [ "LLVM_ENABLE_DIA_SDK=1" ] } else { From 02e4800eeb89c4a5dbb8faa15d21d1d8b52aa877 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 8 Oct 2020 10:13:54 -0400 Subject: [PATCH 3/9] [gn build] (manually) port 9b58b0c06e6 better --- llvm/utils/gn/secondary/lld/test/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/utils/gn/secondary/lld/test/BUILD.gn b/llvm/utils/gn/secondary/lld/test/BUILD.gn index c5b53232443e2f..4e075bbf610472 100644 --- a/llvm/utils/gn/secondary/lld/test/BUILD.gn +++ b/llvm/utils/gn/secondary/lld/test/BUILD.gn @@ -35,7 +35,7 @@ write_lit_cfg("lit_site_cfg") { input = "//lld/test/lit.site.cfg.py.in" output = lld_lit_site_cfg_file - extra_values = [ "LLD_DEFAULT_LD_LLD_IS_MINGW=" ] + extra_values = [ "LLD_DEFAULT_LD_LLD_IS_MINGW=0" ] # Must be 0. if (llvm_enable_dia_sdk) { extra_values += [ "LLVM_ENABLE_DIA_SDK=1" ] From 7238faa4ae977523903192e287d442eb53c49ee5 Mon Sep 17 00:00:00 2001 From: Jay Foad Date: Tue, 6 Oct 2020 09:03:53 +0100 Subject: [PATCH 4/9] [AMDGPU] Add patterns for mad/mac legacy f32 instructions Note that all subtargets up to GFX10.1 have v_mad_legacy_f32, but GFX8/9 lack v_mac_legacy_f32. GFX10.3 has no mad/mac f32 instructions at all. Differential Revision: https://reviews.llvm.org/D88890 --- .../AMDGPU/AsmParser/AMDGPUAsmParser.cpp | 2 + .../Disassembler/AMDGPUDisassembler.cpp | 2 + llvm/lib/Target/AMDGPU/SIInstructions.td | 39 +- llvm/lib/Target/AMDGPU/VOP2Instructions.td | 13 +- .../GlobalISel/llvm.amdgcn.fmul.legacy.ll | 512 +++++++++++++++--- .../CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll | 38 +- 6 files changed, 513 insertions(+), 93 deletions(-) diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp index cdb686fe00439a..6d2e29590abf23 100644 --- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp +++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp @@ -6889,6 +6889,8 @@ void AMDGPUAsmParser::cvtVOP3(MCInst &Inst, const OperandVector &Operands, if (Opc == AMDGPU::V_MAC_F32_e64_gfx6_gfx7 || Opc == AMDGPU::V_MAC_F32_e64_gfx10 || Opc == AMDGPU::V_MAC_F32_e64_vi || + Opc == AMDGPU::V_MAC_LEGACY_F32_e64_gfx6_gfx7 || + Opc == AMDGPU::V_MAC_LEGACY_F32_e64_gfx10 || Opc == AMDGPU::V_MAC_F16_e64_vi || Opc == AMDGPU::V_FMAC_F32_e64_gfx10 || Opc == AMDGPU::V_FMAC_F32_e64_vi || diff --git a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp index 5955cc75c8ea23..33c666f29a2da0 100644 --- a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp +++ b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp @@ -385,6 +385,8 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size, if (Res && (MI.getOpcode() == AMDGPU::V_MAC_F32_e64_vi || MI.getOpcode() == AMDGPU::V_MAC_F32_e64_gfx6_gfx7 || MI.getOpcode() == AMDGPU::V_MAC_F32_e64_gfx10 || + MI.getOpcode() == AMDGPU::V_MAC_LEGACY_F32_e64_gfx6_gfx7 || + MI.getOpcode() == AMDGPU::V_MAC_LEGACY_F32_e64_gfx10 || MI.getOpcode() == AMDGPU::V_MAC_F16_e64_vi || MI.getOpcode() == AMDGPU::V_FMAC_F32_e64_vi || MI.getOpcode() == AMDGPU::V_FMAC_F32_e64_gfx10 || diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td index 7cffe615f3b309..068d8dc2a0fe31 100644 --- a/llvm/lib/Target/AMDGPU/SIInstructions.td +++ b/llvm/lib/Target/AMDGPU/SIInstructions.td @@ -866,7 +866,8 @@ def : GCNPat < // VOP2 Patterns //===----------------------------------------------------------------------===// -// TODO: Check only no src2 mods? +// NoMods pattern used for mac. If there are any source modifiers then it's +// better to select mad instead of mac. class FMADPat : GCNPat <(vt (node (vt (VOP3NoMods vt:$src0)), (vt (VOP3NoMods vt:$src1)), @@ -875,18 +876,29 @@ class FMADPat SRCMODS.NONE, $src2, DSTCLAMP.NONE, DSTOMOD.NONE) >; - // Prefer mac form when there are no modifiers. let AddedComplexity = 9 in { +let OtherPredicates = [HasMadMacF32Insts] in { def : FMADPat ; def : FMADPat ; +// Don't allow source modifiers. If there are any source modifiers then it's +// better to select mad instead of mac. +let SubtargetPredicate = isGFX6GFX7GFX10 in +def : GCNPat < + (f32 (fadd (AMDGPUfmul_legacy (VOP3NoMods f32:$src0), + (VOP3NoMods f32:$src1)), + (VOP3NoMods f32:$src2))), + (V_MAC_LEGACY_F32_e64 SRCMODS.NONE, $src0, SRCMODS.NONE, $src1, + SRCMODS.NONE, $src2, DSTCLAMP.NONE, DSTOMOD.NONE) +>; +} // OtherPredicates = [HasMadMacF32Insts] + let SubtargetPredicate = Has16BitInsts in { def : FMADPat ; def : FMADPat ; -} - -} +} // SubtargetPredicate = Has16BitInsts +} // AddedComplexity = 9 class FMADModsPat : GCNPat< @@ -897,11 +909,20 @@ class FMADModsPat $src2_mod, $src2, DSTCLAMP.NONE, DSTOMOD.NONE) >; -let SubtargetPredicate = HasMadMacF32Insts in +let OtherPredicates = [HasMadMacF32Insts] in { def : FMADModsPat; -def : FMADModsPat { - let SubtargetPredicate = Has16BitInsts; -} + +def : GCNPat < + (f32 (fadd (AMDGPUfmul_legacy (VOP3Mods f32:$src0, i32:$src0_mod), + (VOP3Mods f32:$src1, i32:$src1_mod)), + (VOP3Mods f32:$src2, i32:$src2_mod))), + (V_MAD_LEGACY_F32 $src0_mod, $src0, $src1_mod, $src1, + $src2_mod, $src2, DSTCLAMP.NONE, DSTOMOD.NONE) +>; +} // OtherPredicates = [HasMadMacF32Insts] + +let SubtargetPredicate = Has16BitInsts in +def : FMADModsPat; class VOPSelectModsPat : GCNPat < (vt (select i1:$src0, (VOP3Mods vt:$src1, i32:$src1_mods), diff --git a/llvm/lib/Target/AMDGPU/VOP2Instructions.td b/llvm/lib/Target/AMDGPU/VOP2Instructions.td index 4c263de673d671..09f65c5c944e37 100644 --- a/llvm/lib/Target/AMDGPU/VOP2Instructions.td +++ b/llvm/lib/Target/AMDGPU/VOP2Instructions.td @@ -499,11 +499,15 @@ let OtherPredicates = [HasMadMacF32Insts] in { let Constraints = "$vdst = $src2", DisableEncoding="$src2", isConvertibleToThreeAddress = 1 in { defm V_MAC_F32 : VOP2Inst <"v_mac_f32", VOP_MAC_F32>; -} + +let SubtargetPredicate = isGFX6GFX7GFX10 in +defm V_MAC_LEGACY_F32 : VOP2Inst <"v_mac_legacy_f32", VOP_MAC_F32>; +} // End Constraints = "$vdst = $src2", DisableEncoding="$src2", + // isConvertibleToThreeAddress = 1 def V_MADAK_F32 : VOP2_Pseudo <"v_madak_f32", VOP_MADAK_F32, []>; } // End OtherPredicates = [HasMadMacF32Insts] -} +} // End mayRaiseFPException = 0 // No patterns so that the scalar instructions are always selected. // The scalar versions will be replaced with vector when needed later. @@ -557,10 +561,6 @@ defm V_MAX_LEGACY_F32 : VOP2Inst <"v_max_legacy_f32", VOP_F32_F32_F32, AMDGPUfma } // End SubtargetPredicate = isGFX6GFX7 let isCommutable = 1 in { -let SubtargetPredicate = isGFX6GFX7GFX10 in { -let OtherPredicates = [HasMadMacF32Insts] in -defm V_MAC_LEGACY_F32 : VOP2Inst <"v_mac_legacy_f32", VOP_F32_F32_F32>; -} // End SubtargetPredicate = isGFX6GFX7GFX10 let SubtargetPredicate = isGFX6GFX7 in { defm V_LSHR_B32 : VOP2Inst <"v_lshr_b32", VOP_PAT_GEN, srl>; defm V_ASHR_I32 : VOP2Inst <"v_ashr_i32", VOP_PAT_GEN, sra>; @@ -1322,7 +1322,6 @@ let SubtargetPredicate = isGFX6GFX7 in { defm V_ADD_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x003>; defm V_SUB_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x004>; defm V_SUBREV_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x005>; -let OtherPredicates = [HasMadMacF32Insts] in defm V_MAC_LEGACY_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x006>; defm V_MUL_LEGACY_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x007>; defm V_MUL_F32 : VOP2_Real_gfx6_gfx7_gfx10<0x008>; diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll index 3ebe19a156f099..ebe3ffd06ced33 100644 --- a/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll +++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/llvm.amdgcn.fmul.legacy.ll @@ -1,53 +1,196 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc -global-isel -march=amdgcn -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s -; RUN: llc -global-isel -march=amdgcn -mcpu=tonga -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s +; RUN: llc -global-isel -march=amdgcn -mcpu=tahiti -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,GFX6 %s +; RUN: llc -global-isel -march=amdgcn -mcpu=tonga -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,GFX8 %s +; RUN: llc -global-isel -march=amdgcn -mcpu=gfx900 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,GFX9 %s +; RUN: llc -global-isel -march=amdgcn -mcpu=gfx1010 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,GFX101 %s +; RUN: llc -global-isel -march=amdgcn -mcpu=gfx1030 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,GFX103 %s define float @v_mul_legacy_f32(float %a, float %b) { -; GCN-LABEL: v_mul_legacy_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float %a, float %b) ret float %result } define float @v_mul_legacy_undef0_f32(float %a) { -; GCN-LABEL: v_mul_legacy_undef0_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_undef0_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_undef0_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_undef0_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_undef0_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_undef0_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float undef, float %a) ret float %result } define float @v_mul_legacy_undef1_f32(float %a) { -; GCN-LABEL: v_mul_legacy_undef1_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_undef1_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_undef1_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_undef1_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_undef1_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_undef1_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, s4, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float %a, float undef) ret float %result } define float @v_mul_legacy_undef_f32() { -; GCN-LABEL: v_mul_legacy_undef_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_undef_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_undef_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_undef_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_undef_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_undef_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e64 v0, s4, s4 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float undef, float undef) ret float %result } define float @v_mul_legacy_fabs_f32(float %a, float %b) { -; GCN-LABEL: v_mul_legacy_fabs_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_fabs_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_fabs_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_fabs_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_fabs_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_fabs_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e64 v0, |v0|, |v1| +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %a.fabs = call float @llvm.fabs.f32(float %a) %b.fabs = call float @llvm.fabs.f32(float %b) %result = call float @llvm.amdgcn.fmul.legacy(float %a.fabs, float %b.fabs) @@ -55,76 +198,311 @@ define float @v_mul_legacy_fabs_f32(float %a, float %b) { } define float @v_mul_legacy_fneg_f32(float %a, float %b) { -; GCN-LABEL: v_mul_legacy_fneg_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 -; GCN-NEXT: s_setpc_b64 s[30:31] - %a.fabs = fneg float %a - %b.fabs = fneg float %b - %result = call float @llvm.amdgcn.fmul.legacy(float %a.fabs, float %b.fabs) +; GFX6-LABEL: v_mul_legacy_fneg_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_fneg_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_fneg_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_fneg_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_fneg_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] + %a.fneg = fneg float %a + %b.fneg = fneg float %b + %result = call float @llvm.amdgcn.fmul.legacy(float %a.fneg, float %b.fneg) ret float %result } -; TODO: Should match mac_legacy/mad_legacy define float @v_mad_legacy_f32(float %a, float %b, float %c) { -; GCN-LABEL: v_mad_legacy_f32: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 -; GCN-NEXT: v_add_f32_e32 v0, v0, v2 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mad_legacy_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mac_legacy_f32_e64 v2, v0, v1 +; GFX6-NEXT: v_mov_b32_e32 v0, v2 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mad_legacy_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mad_legacy_f32 v0, v0, v1, v2 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mad_legacy_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mad_legacy_f32 v0, v0, v1, v2 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mad_legacy_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mac_legacy_f32_e64 v2, v0, v1 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: v_mov_b32_e32 v0, v2 +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mad_legacy_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, v0, v1 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: v_add_f32_e32 v0, v0, v2 +; GFX103-NEXT: s_setpc_b64 s[30:31] %mul = call float @llvm.amdgcn.fmul.legacy(float %a, float %b) %add = fadd float %mul, %c ret float %add } +define float @v_mad_legacy_fneg_f32(float %a, float %b, float %c) { +; GFX6-LABEL: v_mad_legacy_fneg_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mad_legacy_f32 v0, -v0, -v1, v2 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mad_legacy_fneg_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mad_legacy_f32 v0, -v0, -v1, v2 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mad_legacy_fneg_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mad_legacy_f32 v0, -v0, -v1, v2 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mad_legacy_fneg_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mad_legacy_f32 v0, -v0, -v1, v2 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mad_legacy_fneg_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e64 v0, -v0, -v1 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: v_add_f32_e32 v0, v0, v2 +; GFX103-NEXT: s_setpc_b64 s[30:31] + %a.fneg = fneg float %a + %b.fneg = fneg float %b + %mul = call float @llvm.amdgcn.fmul.legacy(float %a.fneg, float %b.fneg) + %add = fadd float %mul, %c + ret float %add +} + define amdgpu_ps float @s_mul_legacy_f32(float inreg %a, float inreg %b) { -; GCN-LABEL: s_mul_legacy_f32: -; GCN: ; %bb.0: -; GCN-NEXT: v_mov_b32_e32 v0, s1 -; GCN-NEXT: v_mul_legacy_f32_e32 v0, s0, v0 -; GCN-NEXT: ; return to shader part epilog +; GFX6-LABEL: s_mul_legacy_f32: +; GFX6: ; %bb.0: +; GFX6-NEXT: v_mov_b32_e32 v0, s1 +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, s0, v0 +; GFX6-NEXT: ; return to shader part epilog +; +; GFX8-LABEL: s_mul_legacy_f32: +; GFX8: ; %bb.0: +; GFX8-NEXT: v_mov_b32_e32 v0, s1 +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, s0, v0 +; GFX8-NEXT: ; return to shader part epilog +; +; GFX9-LABEL: s_mul_legacy_f32: +; GFX9: ; %bb.0: +; GFX9-NEXT: v_mov_b32_e32 v0, s1 +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, s0, v0 +; GFX9-NEXT: ; return to shader part epilog +; +; GFX101-LABEL: s_mul_legacy_f32: +; GFX101: ; %bb.0: +; GFX101-NEXT: v_mul_legacy_f32_e64 v0, s0, s1 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: ; return to shader part epilog +; +; GFX103-LABEL: s_mul_legacy_f32: +; GFX103: ; %bb.0: +; GFX103-NEXT: v_mul_legacy_f32_e64 v0, s0, s1 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: ; return to shader part epilog %result = call float @llvm.amdgcn.fmul.legacy(float %a, float %b) ret float %result } define float @v_mul_legacy_f32_1.0(float %a) { -; GCN-LABEL: v_mul_legacy_f32_1.0: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_f32_1.0: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_f32_1.0: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_f32_1.0: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_f32_1.0: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_f32_1.0: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float %a, float 1.0) ret float %result } define float @v_mul_legacy_f32_1.0_swap(float %b) { -; GCN-LABEL: v_mul_legacy_f32_1.0_swap: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_f32_1.0_swap: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_f32_1.0_swap: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_f32_1.0_swap: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_f32_1.0_swap: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_f32_1.0_swap: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, 1.0, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float 1.0, float %b) ret float %result } define float @v_mul_legacy_f32_2.0(float %a) { -; GCN-LABEL: v_mul_legacy_f32_2.0: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_f32_2.0: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_f32_2.0: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_f32_2.0: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_f32_2.0: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_f32_2.0: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float %a, float 2.0) ret float %result } define float @v_mul_legacy_f32_2.0_swap(float %b) { -; GCN-LABEL: v_mul_legacy_f32_2.0_swap: -; GCN: ; %bb.0: -; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) -; GCN-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 -; GCN-NEXT: s_setpc_b64 s[30:31] +; GFX6-LABEL: v_mul_legacy_f32_2.0_swap: +; GFX6: ; %bb.0: +; GFX6-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX6-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX6-NEXT: s_setpc_b64 s[30:31] +; +; GFX8-LABEL: v_mul_legacy_f32_2.0_swap: +; GFX8: ; %bb.0: +; GFX8-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX8-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX8-NEXT: s_setpc_b64 s[30:31] +; +; GFX9-LABEL: v_mul_legacy_f32_2.0_swap: +; GFX9: ; %bb.0: +; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX9-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX9-NEXT: s_setpc_b64 s[30:31] +; +; GFX101-LABEL: v_mul_legacy_f32_2.0_swap: +; GFX101: ; %bb.0: +; GFX101-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX101-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX101-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX101-NEXT: ; implicit-def: $vcc_hi +; GFX101-NEXT: s_setpc_b64 s[30:31] +; +; GFX103-LABEL: v_mul_legacy_f32_2.0_swap: +; GFX103: ; %bb.0: +; GFX103-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; GFX103-NEXT: s_waitcnt_vscnt null, 0x0 +; GFX103-NEXT: v_mul_legacy_f32_e32 v0, 2.0, v0 +; GFX103-NEXT: ; implicit-def: $vcc_hi +; GFX103-NEXT: s_setpc_b64 s[30:31] %result = call float @llvm.amdgcn.fmul.legacy(float 2.0, float %b) ret float %result } diff --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll index be8462d09064ad..a91745b636d976 100644 --- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll +++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.fmul.legacy.ll @@ -1,9 +1,11 @@ -; RUN: llc -march=amdgcn -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s -; RUN: llc -march=amdgcn -mcpu=tonga -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s - +; RUN: llc -march=amdgcn -mcpu=tahiti -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,MADMACF32,GFX6 %s +; RUN: llc -march=amdgcn -mcpu=tonga -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,MADMACF32,GFX8 %s +; RUN: llc -march=amdgcn -mcpu=gfx900 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,MADMACF32,GFX9 %s +; RUN: llc -march=amdgcn -mcpu=gfx1010 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,MADMACF32,GFX101 %s +; RUN: llc -march=amdgcn -mcpu=gfx1030 -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,NOMADMACF32,GFX103 %s ; GCN-LABEL: {{^}}test_mul_legacy_f32: -; GCN: v_mul_legacy_f32_e32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} +; GCN: v_mul_legacy_f32_e{{(32|64)}} v{{[0-9]+}}, s{{[0-9]+}}, {{[sv][0-9]+}} define amdgpu_kernel void @test_mul_legacy_f32(float addrspace(1)* %out, float %a, float %b) #0 { %result = call float @llvm.amdgcn.fmul.legacy(float %a, float %b) store float %result, float addrspace(1)* %out, align 4 @@ -11,7 +13,7 @@ define amdgpu_kernel void @test_mul_legacy_f32(float addrspace(1)* %out, float % } ; GCN-LABEL: {{^}}test_mul_legacy_undef0_f32: -; GCN: v_mul_legacy_f32_e32 +; GCN: v_mul_legacy_f32_e{{(32|64)}} v{{[0-9]+}}, s{{[0-9]+}}, {{[sv][0-9]+}} define amdgpu_kernel void @test_mul_legacy_undef0_f32(float addrspace(1)* %out, float %a) #0 { %result = call float @llvm.amdgcn.fmul.legacy(float undef, float %a) store float %result, float addrspace(1)* %out, align 4 @@ -19,7 +21,7 @@ define amdgpu_kernel void @test_mul_legacy_undef0_f32(float addrspace(1)* %out, } ; GCN-LABEL: {{^}}test_mul_legacy_undef1_f32: -; GCN: v_mul_legacy_f32_e32 +; GCN: v_mul_legacy_f32_e{{(32|64)}} v{{[0-9]+}}, s{{[0-9]+}}, {{[sv][0-9]+}} define amdgpu_kernel void @test_mul_legacy_undef1_f32(float addrspace(1)* %out, float %a) #0 { %result = call float @llvm.amdgcn.fmul.legacy(float %a, float undef) store float %result, float addrspace(1)* %out, align 4 @@ -27,7 +29,7 @@ define amdgpu_kernel void @test_mul_legacy_undef1_f32(float addrspace(1)* %out, } ; GCN-LABEL: {{^}}test_mul_legacy_fabs_f32: -; GCN: v_mul_legacy_f32_e64 v{{[0-9]+}}, |s{{[0-9]+}}|, |v{{[0-9]+}}| +; GCN: v_mul_legacy_f32_e{{(32|64)}} v{{[0-9]+}}, |s{{[0-9]+}}|, |{{[sv][0-9]+}}| define amdgpu_kernel void @test_mul_legacy_fabs_f32(float addrspace(1)* %out, float %a, float %b) #0 { %a.fabs = call float @llvm.fabs.f32(float %a) %b.fabs = call float @llvm.fabs.f32(float %b) @@ -36,10 +38,13 @@ define amdgpu_kernel void @test_mul_legacy_fabs_f32(float addrspace(1)* %out, fl ret void } -; TODO: Should match mac_legacy/mad_legacy ; GCN-LABEL: {{^}}test_mad_legacy_f32: -; GCN: v_mul_legacy_f32_e32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} -; GCN: v_add_f32_e32 +; GFX6: v_mac_legacy_f32_e64 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} +; GFX8: v_mad_legacy_f32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} +; GFX9: v_mad_legacy_f32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} +; GFX101: v_mac_legacy_f32_e64 v{{[0-9]+}}, s{{[0-9]+}}, s{{[0-9]+}} +; GFX103: v_mul_legacy_f32_e64 v{{[0-9]+}}, s{{[0-9]+}}, s{{[0-9]+}} +; GFX103: v_add_f32_e32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} define amdgpu_kernel void @test_mad_legacy_f32(float addrspace(1)* %out, float %a, float %b, float %c) #0 { %mul = call float @llvm.amdgcn.fmul.legacy(float %a, float %b) %add = fadd float %mul, %c @@ -47,6 +52,19 @@ define amdgpu_kernel void @test_mad_legacy_f32(float addrspace(1)* %out, float % ret void } +; GCN-LABEL: {{^}}test_mad_legacy_fneg_f32: +; MADMACF32: v_mad_legacy_f32 v{{[0-9]+}}, -s{{[0-9]+}}, -{{[sv][0-9]+}}, v{{[0-9]+}} +; NOMADMACF32: v_mul_legacy_f32_e64 v{{[0-9]+}}, -s{{[0-9]+}}, -s{{[0-9]+}} +; NOMADMACF32: v_add_f32_e32 v{{[0-9]+}}, s{{[0-9]+}}, v{{[0-9]+}} +define amdgpu_kernel void @test_mad_legacy_fneg_f32(float addrspace(1)* %out, float %a, float %b, float %c) #0 { + %a.fneg = fneg float %a + %b.fneg = fneg float %b + %mul = call float @llvm.amdgcn.fmul.legacy(float %a.fneg, float %b.fneg) + %add = fadd float %mul, %c + store float %add, float addrspace(1)* %out, align 4 + ret void +} + declare float @llvm.fabs.f32(float) #1 declare float @llvm.amdgcn.fmul.legacy(float, float) #1 From 395963cbe63bba3f2d330dde76957cd900d21f42 Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Thu, 8 Oct 2020 09:35:25 -0400 Subject: [PATCH 5/9] [InstCombine] add vector splat tests for add of signmask; NFC --- llvm/test/Transforms/InstCombine/add.ll | 11 +++++++++++ .../InstCombine/fold-sub-of-not-to-inc-of-add.ll | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll index 26ce6d2a047573..564854c5c5c23e 100644 --- a/llvm/test/Transforms/InstCombine/add.ll +++ b/llvm/test/Transforms/InstCombine/add.ll @@ -421,6 +421,17 @@ define i32 @xor_sign_bit(i32 %x) { ret i32 %add } +define <2 x i32> @xor_sign_bit_vec_splat(<2 x i32> %x) { +; CHECK-LABEL: @xor_sign_bit_vec_splat( +; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i32> [[X:%.*]], +; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[XOR]], +; CHECK-NEXT: ret <2 x i32> [[ADD]] +; + %xor = xor <2 x i32> %x, + %add = add <2 x i32> %xor, + ret <2 x i32> %add +} + ; No-wrap info allows converting the add to 'or'. define i8 @add_nsw_signbit(i8 %x) { diff --git a/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll b/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll index 49d4ec43768bfd..d06651e5a07c27 100644 --- a/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll +++ b/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll @@ -93,3 +93,15 @@ define i32 @n5_is_not_not(i32 %x, i32 %y) { %t1 = sub i32 %y, %t0 ret i32 %t1 } + +define <2 x i32> @n5_is_not_not_vec_splat(<2 x i32> %x, <2 x i32> %y) { +; CHECK-LABEL: @n5_is_not_not_vec_splat( +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], +; CHECK-NEXT: [[T0_NEG:%.*]] = add <2 x i32> [[TMP1]], +; CHECK-NEXT: [[T1:%.*]] = add <2 x i32> [[T0_NEG]], [[Y:%.*]] +; CHECK-NEXT: ret <2 x i32> [[T1]] +; + %t0 = xor <2 x i32> %x, ; signmask, but not -1 + %t1 = sub <2 x i32> %y, %t0 + ret <2 x i32> %t1 +} From b57451b011d39f5a36a8cd6236a49c47692ee89c Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Thu, 8 Oct 2020 10:45:42 -0400 Subject: [PATCH 6/9] [InstCombine] allow vector splats for add+xor with signmask --- llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp | 9 ++++----- llvm/test/Transforms/InstCombine/add.ll | 3 +-- .../InstCombine/fold-sub-of-not-to-inc-of-add.ll | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp index 00e981686e797c..36f9a42581302c 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -924,6 +924,10 @@ Instruction *InstCombinerImpl::foldAddWithConstant(BinaryOperator &Add) { C2->isMinSignedValue() && C2->sext(Ty->getScalarSizeInBits()) == *C) return CastInst::Create(Instruction::SExt, X, Ty); + // (X ^ signmask) + C --> (X + (signmask ^ C)) + if (match(Op0, m_Xor(m_Value(X), m_APInt(C2))) && C2->isSignMask()) + return BinaryOperator::CreateAdd(X, ConstantInt::get(Ty, *C2 ^ *C)); + if (C->isOneValue() && Op0->hasOneUse()) { // add (sext i1 X), 1 --> zext (not X) // TODO: The smallest IR representation is (select X, 0, 1), and that would @@ -1300,11 +1304,6 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) { return BinaryOperator::CreateSub(ConstantExpr::getAdd(XorRHS, CI), XorLHS); } - // (X + signmask) + C could have gotten canonicalized to (X^signmask) + C, - // transform them into (X + (signmask ^ C)) - if (XorRHS->getValue().isSignMask()) - return BinaryOperator::CreateAdd(XorLHS, - ConstantExpr::getXor(XorRHS, CI)); } } diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll index 564854c5c5c23e..0363cfe3a0d019 100644 --- a/llvm/test/Transforms/InstCombine/add.ll +++ b/llvm/test/Transforms/InstCombine/add.ll @@ -423,8 +423,7 @@ define i32 @xor_sign_bit(i32 %x) { define <2 x i32> @xor_sign_bit_vec_splat(<2 x i32> %x) { ; CHECK-LABEL: @xor_sign_bit_vec_splat( -; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i32> [[X:%.*]], -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[XOR]], +; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[X:%.*]], ; CHECK-NEXT: ret <2 x i32> [[ADD]] ; %xor = xor <2 x i32> %x, diff --git a/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll b/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll index d06651e5a07c27..b7edab9d856738 100644 --- a/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll +++ b/llvm/test/Transforms/InstCombine/fold-sub-of-not-to-inc-of-add.ll @@ -96,8 +96,7 @@ define i32 @n5_is_not_not(i32 %x, i32 %y) { define <2 x i32> @n5_is_not_not_vec_splat(<2 x i32> %x, <2 x i32> %y) { ; CHECK-LABEL: @n5_is_not_not_vec_splat( -; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], -; CHECK-NEXT: [[T0_NEG:%.*]] = add <2 x i32> [[TMP1]], +; CHECK-NEXT: [[T0_NEG:%.*]] = add <2 x i32> [[X:%.*]], ; CHECK-NEXT: [[T1:%.*]] = add <2 x i32> [[T0_NEG]], [[Y:%.*]] ; CHECK-NEXT: ret <2 x i32> [[T1]] ; From db1988f038843ad047fdab5b9f818306b06ea80a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 7 Oct 2020 08:45:24 -0700 Subject: [PATCH 7/9] [ELF] Don't change binding to STB_WEAK for an undefined specified by -u Similar to D66992. In GNU ld, a -u specified symbol is a STB_DEFAULT undefined. It cannot be changed to STB_WEAK by a later STB_WEAK undefined in a regular object file. The behavior is consistent with our model because -u means "we need to fetch a lazy definition". It should not be altered just because there is also a STB_WEAK undefined. Note, our -u semantics are still different from GNU ld (https://github.com/ClangBuiltLinux/linux/issues/515): we don't force the specified symbol to appear in .symtab This is a deliberate decision. Reviewed By: grimar Differential Revision: https://reviews.llvm.org/D88945 --- lld/ELF/Driver.cpp | 2 +- lld/test/ELF/weak-undef-lib.s | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 6aca2306d1e965..30575f66d017f5 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1992,7 +1992,7 @@ template void LinkerDriver::link(opt::InputArgList &args) { // Handle -u/--undefined before input files. If both a.a and b.so define foo, // -u foo a.a b.so will fetch a.a. for (StringRef name : config->undefined) - addUnusedUndefined(name); + addUnusedUndefined(name)->referenced = true; // Add all files to the symbol table. This will add almost all // symbols that we need to the symbol table. This process might diff --git a/lld/test/ELF/weak-undef-lib.s b/lld/test/ELF/weak-undef-lib.s index 54e05dc7e98773..19b59fddaa7231 100644 --- a/lld/test/ELF/weak-undef-lib.s +++ b/lld/test/ELF/weak-undef-lib.s @@ -17,6 +17,17 @@ # CHECK-NEXT: Other: 0 # CHECK-NEXT: Section: Undefined +## -u specifies a STB_DEFAULT undefined symbol, so the definition from %t2.o is +## fetched. +# RUN: ld.lld -u foo %t1.o --start-lib %t2.o -o %t1 +# RUN: llvm-readobj --syms %t1 | FileCheck %s --check-prefix=CHECK-U + +# CHECK-U: Name: foo +# CHECK-U: Binding: +# CHECK-U-SAME: Global +# CHECK-U: Section: +# CHECK-U-SAME: .text + .weak foo call foo@PLT From b9225543e844bee5091aa16108e0c54bd2abe485 Mon Sep 17 00:00:00 2001 From: Geoff Levner Date: Thu, 8 Oct 2020 10:59:30 -0400 Subject: [PATCH 8/9] DeferredDiagnosticsEmitter crashes Patch VisitCXXDeleteExpr() in clang::UsedDeclVisitor to avoid it crashing when the expression's destroyed type is null. According to the comments in CXXDeleteExpr::getDestroyedType(), this can happen when the type to delete is a dependent type. Patch by Geoff Levner. Differential Revision: https://reviews.llvm.org/D88949 --- clang/lib/Sema/UsedDeclVisitor.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/UsedDeclVisitor.h b/clang/lib/Sema/UsedDeclVisitor.h index d207e07f451ad2..c33d30478e2abc 100644 --- a/clang/lib/Sema/UsedDeclVisitor.h +++ b/clang/lib/Sema/UsedDeclVisitor.h @@ -67,10 +67,13 @@ class UsedDeclVisitor : public EvaluatedExprVisitor { void VisitCXXDeleteExpr(CXXDeleteExpr *E) { if (E->getOperatorDelete()) asImpl().visitUsedDecl(E->getBeginLoc(), E->getOperatorDelete()); - QualType Destroyed = S.Context.getBaseElementType(E->getDestroyedType()); - if (const RecordType *DestroyedRec = Destroyed->getAs()) { - CXXRecordDecl *Record = cast(DestroyedRec->getDecl()); - asImpl().visitUsedDecl(E->getBeginLoc(), S.LookupDestructor(Record)); + QualType DestroyedOrNull = E->getDestroyedType(); + if (!DestroyedOrNull.isNull()) { + QualType Destroyed = S.Context.getBaseElementType(DestroyedOrNull); + if (const RecordType *DestroyedRec = Destroyed->getAs()) { + CXXRecordDecl *Record = cast(DestroyedRec->getDecl()); + asImpl().visitUsedDecl(E->getBeginLoc(), S.LookupDestructor(Record)); + } } Inherited::VisitCXXDeleteExpr(E); From 79809f58b02419a5d1bfb6c9a59dbd13cd038c77 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Thu, 8 Oct 2020 11:46:04 -0400 Subject: [PATCH 9/9] [LLDB] On Windows, fix tests This patch fixes a few issues seen when running `ninja check-lldb` in a Release build with VS2017: - Some binaries couldn't be found (such as lldb-vscode.exe), because .exe wasn't appended to the file name. - Many tests used to fail since our installed locale is in French - the OS error messages are not emitted in English. - Our codepage being Windows-1252, python failed to decode some error messages with accentuations. Differential Revision: https://reviews.llvm.org/D88975 --- .../Python/lldbsuite/test/decorators.py | 13 ++++++++++++ lldb/packages/Python/lldbsuite/test/dotest.py | 3 +++ .../lldbsuite/test_event/build_exception.py | 2 +- .../target/basic/TestTargetCommand.py | 2 ++ lldb/unittests/Utility/StatusTest.cpp | 20 +++++++++++++++---- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index 4e47165cdb1f64..bb4a459abe688e 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -3,6 +3,8 @@ # System modules from distutils.version import LooseVersion from functools import wraps +import ctypes +import locale import os import platform import re @@ -592,6 +594,17 @@ def skipIfWindows(func): """Decorate the item to skip tests that should be skipped on Windows.""" return skipIfPlatform(["windows"])(func) +def skipIfWindowsAndNonEnglish(func): + """Decorate the item to skip tests that should be skipped on non-English locales on Windows.""" + def is_Windows_NonEnglish(self): + if lldbplatformutil.getPlatform() != "windows": + return None + kernel = ctypes.windll.kernel32 + if locale.windows_locale[ kernel.GetUserDefaultUILanguage() ] == "en_US": + return None + return "skipping non-English Windows locale" + return skipTestIfFn(is_Windows_NonEnglish)(func) + def skipUnlessWindows(func): """Decorate the item to skip tests that should be skipped on any non-Windows platform.""" return skipUnlessPlatform(["windows"])(func) diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index 922d7c9377ee2f..0da60f11a609be 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -52,6 +52,9 @@ def is_exe(fpath): """Returns true if fpath is an executable.""" if fpath == None: return False + if sys.platform == 'win32': + if not fpath.endswith(".exe"): + fpath += ".exe" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) diff --git a/lldb/packages/Python/lldbsuite/test_event/build_exception.py b/lldb/packages/Python/lldbsuite/test_event/build_exception.py index 993214edd8711a..e08b632eb9a919 100644 --- a/lldb/packages/Python/lldbsuite/test_event/build_exception.py +++ b/lldb/packages/Python/lldbsuite/test_event/build_exception.py @@ -12,4 +12,4 @@ def __str__(self): @staticmethod def format_build_error(command, command_output): return "Error when building test subject.\n\nBuild Command:\n{}\n\nBuild Command Output:\n{}".format( - command, command_output.decode("utf-8")) + command, command_output.decode("utf-8", errors='ignore')) diff --git a/lldb/test/API/commands/target/basic/TestTargetCommand.py b/lldb/test/API/commands/target/basic/TestTargetCommand.py index be6eeb938ab8b2..74b6c1fdcaed7d 100644 --- a/lldb/test/API/commands/target/basic/TestTargetCommand.py +++ b/lldb/test/API/commands/target/basic/TestTargetCommand.py @@ -350,6 +350,7 @@ def test_target_create_multiple_args(self): self.expect("target create a b", error=True, substrs=["'target create' takes exactly one executable path"]) + @skipIfWindowsAndNonEnglish @no_debug_info_test def test_target_create_nonexistent_core_file(self): self.expect("target create -c doesntexist", error=True, @@ -365,6 +366,7 @@ def test_target_create_unreadable_core_file(self): self.expect("target create -c '" + tf.name + "'", error=True, substrs=["Cannot open '", "': Permission denied"]) + @skipIfWindowsAndNonEnglish @no_debug_info_test def test_target_create_nonexistent_sym_file(self): self.expect("target create -s doesntexist doesntexisteither", error=True, diff --git a/lldb/unittests/Utility/StatusTest.cpp b/lldb/unittests/Utility/StatusTest.cpp index 862c063b2e0618..9b9d870cd12a30 100644 --- a/lldb/unittests/Utility/StatusTest.cpp +++ b/lldb/unittests/Utility/StatusTest.cpp @@ -10,7 +10,7 @@ #include "gtest/gtest.h" #ifdef _WIN32 -#include +#include #endif using namespace lldb_private; @@ -71,14 +71,26 @@ TEST(StatusTest, ErrorWin32) { EXPECT_FALSE(success.ToError()); EXPECT_TRUE(success.Success()); + WCHAR name[128]{}; + ULONG nameLen = llvm::array_lengthof(name); + ULONG langs = 0; + GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &langs, + reinterpret_cast(&name), &nameLen); + // Skip the following tests on non-English, non-US, locales because the + // formatted messages will be different. + bool skip = wcscmp(L"en-US", name) != 0; + auto s = Status(ERROR_ACCESS_DENIED, ErrorType::eErrorTypeWin32); EXPECT_TRUE(s.Fail()); - EXPECT_STREQ("Access is denied. ", s.AsCString()); + if (!skip) + EXPECT_STREQ("Access is denied. ", s.AsCString()); s.SetError(ERROR_IPSEC_IKE_TIMED_OUT, ErrorType::eErrorTypeWin32); - EXPECT_STREQ("Negotiation timed out ", s.AsCString()); + if (!skip) + EXPECT_STREQ("Negotiation timed out ", s.AsCString()); s.SetError(16000, ErrorType::eErrorTypeWin32); - EXPECT_STREQ("unknown error", s.AsCString()); + if (!skip) + EXPECT_STREQ("unknown error", s.AsCString()); } #endif