Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DolphinQt: JIT Widget Refresh #12714

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions Externals/rangeset/include/rangeset/rangeset.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cassert>
#include <cstddef>
#include <map>
#include <utility>

namespace HyoutaUtilities {
template <typename T> class RangeSet {
Expand Down Expand Up @@ -254,7 +255,31 @@ template <typename T> class RangeSet {
return !(*this == other);
}

// Get free size and fragmentation ratio
std::pair<std::size_t, double> get_stats() const {
std::size_t free_total = 0;
if (begin() == end())
return {free_total, 1.0};
std::size_t largest_size = 0;
for (auto iter = begin(); iter != end(); ++iter) {
const std::size_t size = calc_size(iter.from(), iter.to());
if (size > largest_size)
largest_size = size;
free_total += size;
}
return {free_total, static_cast<double>(free_total - largest_size) / free_total};
}

private:
static std::size_t calc_size(T from, T to) {
if constexpr (std::is_pointer_v<T>) {
// For pointers we don't want pointer arithmetic here, else void* breaks.
return reinterpret_cast<std::size_t>(to) - reinterpret_cast<std::size_t>(from);
} else {
return static_cast<std::size_t>(to - from);
}
}

// Assumptions that can be made about the data:
// - Range are stored in the form [from, to[
// That is, the starting value is inclusive, and the end value is exclusive.
Expand Down
11 changes: 11 additions & 0 deletions Externals/rangeset/include/rangeset/rangesizeset.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cstddef>
#include <map>
#include <type_traits>
#include <utility>

namespace HyoutaUtilities {
// Like RangeSet, but additionally stores a map of the ranges sorted by their size, for quickly finding the largest or
Expand Down Expand Up @@ -398,6 +399,16 @@ template <typename T> class RangeSizeSet {
return !(*this == other);
}

// Get free size and fragmentation ratio
std::pair<std::size_t, double> get_stats() const {
std::size_t free_total = 0;
if (begin() == end())
return {free_total, 1.0};
for (auto iter = begin(); iter != end(); ++iter)
free_total += calc_size(iter.from(), iter.to());
return {free_total, static_cast<double>(free_total - Sizes.begin()->first) / free_total};
}

private:
static SizeT calc_size(T from, T to) {
if constexpr (std::is_pointer_v<T>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ public static native void Run(String[] path, boolean riivolution, String savesta

public static native boolean IsRunningAndUnpaused();

/**
* Re-initialize software JitBlock profiling data
*/
public static native void WipeJitBlockProfilingData();

/**
* Writes out the JitBlock Cache log dump
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,16 @@ class SettingsFragmentPresenter(
0
)
)
sl.add(
RunRunnable(
context,
R.string.debug_jit_wipe_block_profiling_data,
0,
R.string.debug_jit_wipe_block_profiling_data_alert,
0,
true
) { NativeLibrary.WipeJitBlockProfilingData() }
)
sl.add(
RunRunnable(
context,
Expand Down
2 changes: 2 additions & 0 deletions Source/Android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@
<string name="debug_large_entry_points_map">Disable Large Entry Points Map</string>
<string name="debug_jit_profiling_header">Jit Profiling</string>
<string name="debug_jit_enable_block_profiling">Enable Jit Block Profiling</string>
<string name="debug_jit_wipe_block_profiling_data">Wipe Jit Block Profiling Data</string>
<string name="debug_jit_wipe_block_profiling_data_alert">Re-initialize JIT block profiling data?</string>
<string name="debug_jit_write_block_log_dump">Write Jit Block Log Dump</string>
<string name="debug_jit_header">Jit</string>
<string name="debug_jitoff">Jit Disabled</string>
Expand Down
24 changes: 24 additions & 0 deletions Source/Android/jni/MainAndroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ void Host_UpdateDisasmDialog()
{
}

void Host_JitCacheCleared()
{
}

void Host_JitProfileDataWiped()
{
}

void Host_UpdateMainFrame()
{
}
Expand Down Expand Up @@ -406,6 +414,22 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetMaxLogLev
return static_cast<jint>(Common::Log::MAX_LOGLEVEL);
}

JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WipeJitBlockProfilingData(
JNIEnv* env, jclass native_library_class)
{
HostThreadLock guard;
auto& system = Core::System::GetInstance();
auto& jit_interface = system.GetJitInterface();
if (jit_interface.GetCore() == nullptr)
{
env->CallStaticVoidMethod(native_library_class, IDCache::GetDisplayToastMsg(),
ToJString(env, Common::GetStringT("JIT is not active")),
static_cast<jboolean>(false));
return;
}
jit_interface.WipeBlockProfilingData(Core::CPUThreadGuard{system});
}

JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteJitBlockLogDump(
JNIEnv* env, jclass native_library_class)
{
Expand Down
28 changes: 28 additions & 0 deletions Source/Core/Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ add_library(common
Hash.cpp
Hash.h
HookableEvent.h
HostDisassembler.cpp
HostDisassembler.h
HttpRequest.cpp
HttpRequest.h
Image.cpp
Expand Down Expand Up @@ -143,6 +145,7 @@ add_library(common
TraversalClient.h
TraversalProto.h
TypeUtils.h
Unreachable.h
UPnP.cpp
UPnP.h
VariantUtil.h
Expand Down Expand Up @@ -178,6 +181,11 @@ PRIVATE
${VTUNE_LIBRARIES}
)

if ((DEFINED CMAKE_ANDROID_ARCH_ABI AND CMAKE_ANDROID_ARCH_ABI MATCHES "x86|x86_64") OR
(NOT DEFINED CMAKE_ANDROID_ARCH_ABI AND _M_X86_64))
target_link_libraries(common PRIVATE bdisasm)
endif()

if (APPLE)
target_link_libraries(common
PRIVATE
Expand Down Expand Up @@ -328,6 +336,26 @@ if(OPROFILE_FOUND)
target_link_libraries(common PRIVATE OProfile::OProfile)
endif()

if(ENABLE_LLVM)
find_package(LLVM CONFIG)
if(LLVM_FOUND)
message(STATUS "LLVM found, enabling LLVM support in disassembler")
# Minimal documentation about LLVM's CMake functions is available here:
# https://releases.llvm.org/16.0.0/docs/CMake.html#embedding-llvm-in-your-project
# https://groups.google.com/g/llvm-dev/c/YeEVe7HTasQ?pli=1
#
# However, you have to read the source code in any case.
# Look for LLVM-Config.cmake in your (Unix) system:
# $ find /usr -name LLVM-Config\\.cmake 2>/dev/null
llvm_expand_pseudo_components(LLVM_EXPAND_COMPONENTS
AllTargetsInfos AllTargetsDisassemblers AllTargetsCodeGens
)
llvm_config(common USE_SHARED
mcdisassembler target ${LLVM_EXPAND_COMPONENTS}
)
endif()
endif()

if(UNIX)
# Posix networking code needs to be fixed for Windows
add_executable(traversal_server TraversalServer.cpp)
Expand Down
7 changes: 5 additions & 2 deletions Source/Core/Common/CodeBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Common
// having to prefix them with gen-> or something similar.
// Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {}
template <class T>
template <class T, bool executable = true>
class CodeBlock : public T
{
private:
Expand Down Expand Up @@ -53,7 +53,10 @@ class CodeBlock : public T
{
region_size = size;
total_region_size = size;
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
if constexpr (executable)
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
else
region = static_cast<u8*>(Common::AllocateMemoryPages(total_region_size));
T::SetCodePtr(region, region + size);
}

Expand Down
177 changes: 177 additions & 0 deletions Source/Core/Common/HostDisassembler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "Common/HostDisassembler.h"

#include <cstdint>
#include <span>
#include <sstream>

#include <fmt/format.h>
#include <fmt/ostream.h>

#if defined(HAVE_LLVM)
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
#elif defined(_M_X86_64)
#include <disasm.h> // Bochs
#endif

#if defined(HAVE_LLVM)
class HostDisassemblerLLVM final : public HostDisassembler
{
public:
explicit HostDisassemblerLLVM(const char* host_disasm, const char* cpu = "",
std::size_t inst_size = 0);
~HostDisassemblerLLVM();

private:
LLVMDisasmContextRef m_llvm_context;
std::size_t m_instruction_size;

void Disassemble(const u8* begin, const u8* end, std::ostream& stream,
std::size_t& instruction_count) override;
};

HostDisassemblerLLVM::HostDisassemblerLLVM(const char* host_disasm, const char* cpu,
std::size_t inst_size)
: m_instruction_size(inst_size)
{
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();

m_llvm_context = LLVMCreateDisasmCPU(host_disasm, cpu, nullptr, 0, nullptr, nullptr);

// Couldn't create llvm context
if (!m_llvm_context)
return;

LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant |
LLVMDisassembler_Option_PrintLatency);
}

HostDisassemblerLLVM::~HostDisassemblerLLVM()
{
if (m_llvm_context)
LLVMDisasmDispose(m_llvm_context);
}

void HostDisassemblerLLVM::Disassemble(const u8* begin, const u8* end, std::ostream& stream,
std::size_t& instruction_count)
{
instruction_count = 0;
if (!m_llvm_context)
return;

while (begin < end)
{
char inst_disasm[256];

const auto inst_size = LLVMDisasmInstruction(
m_llvm_context, const_cast<u8*>(begin), static_cast<u64>(end - begin),
reinterpret_cast<std::uintptr_t>(begin), inst_disasm, sizeof(inst_disasm));
if (inst_size == 0)
{
if (m_instruction_size != 0)
{
// If we are on an architecture that has a fixed instruction
// size, we can continue onward past this bad instruction.
fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
fmt::join(std::span{begin, m_instruction_size}, ""));
begin += m_instruction_size;
}
else
{
// We can't continue if we are on an architecture that has flexible
// instruction sizes. Dump the rest of the block instead.
fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
fmt::join(std::span{begin, end}, ""));
break;
}
}
else
{
fmt::println(stream, "{}{}", fmt::ptr(begin), inst_disasm);
begin += inst_size;
}

++instruction_count;
}
}
#elif defined(_M_X86_64)
class HostDisassemblerBochs final : public HostDisassembler
{
public:
explicit HostDisassemblerBochs();
~HostDisassemblerBochs() = default;

private:
disassembler m_disasm;

void Disassemble(const u8* begin, const u8* end, std::ostream& stream,
std::size_t& instruction_count) override;
};

HostDisassemblerBochs::HostDisassemblerBochs()
{
m_disasm.set_syntax_intel();
}

void HostDisassemblerBochs::Disassemble(const u8* begin, const u8* end, std::ostream& stream,
std::size_t& instruction_count)
{
instruction_count = 0;

while (begin < end)
{
char inst_disasm[256];
const auto inst_size = m_disasm.disasm64(reinterpret_cast<std::uintptr_t>(begin),
reinterpret_cast<std::uintptr_t>(begin),
const_cast<u8*>(begin), inst_disasm);
fmt::println(stream, "{}\t{}", fmt::ptr(begin), inst_disasm);
begin += inst_size;
++instruction_count;
}
}
#endif

std::unique_ptr<HostDisassembler> HostDisassembler::Factory(Platform arch)
{
switch (arch)
{
#if defined(HAVE_LLVM)
case Platform::x86_64:
return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown");
case Platform::aarch64:
return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", "cortex-a57", 4);
#elif defined(_M_X86_64)
case Platform::x86_64:
return std::make_unique<HostDisassemblerBochs>();
#else
case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels"
#endif
default:
return std::make_unique<HostDisassembler>();
}
}

void HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream,
std::size_t& instruction_count)
{
instruction_count = 0;
return fmt::println(stream, "{}\t{:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, ""));
}

void HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
{
std::size_t instruction_count;
Disassemble(begin, end, stream, instruction_count);
}

std::string HostDisassembler::Disassemble(const u8* begin, const u8* end)
{
std::ostringstream stream;
Disassemble(begin, end, stream);
return std::move(stream).str();
}