diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e07e9cdca..5b36005d5 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -51,6 +51,7 @@ set (SRCS Interface/Config/Config.cpp Interface/Context/Context.cpp Interface/Core/BlockCache.cpp + Interface/Core/BlockSamplingData.cpp Interface/Core/Core.cpp Interface/Core/CPUID.cpp Interface/Core/Frontend.cpp diff --git a/Source/Interface/Context/Context.h b/Source/Interface/Context/Context.h index cac693cca..d0fb3c931 100644 --- a/Source/Interface/Context/Context.h +++ b/Source/Interface/Context/Context.h @@ -15,6 +15,8 @@ namespace FEXCore { class SyscallHandler; +class BlockSamplingData; + namespace CPU { class JITCore; } @@ -65,6 +67,10 @@ namespace FEXCore::Context { CustomCPUFactoryType CustomCPUFactory; CustomCPUFactoryType FallbackCPUFactory; +#ifdef BLOCKSTATS + std::unique_ptr BlockData; +#endif + Context(); ~Context(); diff --git a/Source/Interface/Core/BlockSamplingData.cpp b/Source/Interface/Core/BlockSamplingData.cpp new file mode 100644 index 000000000..0c1d84ca1 --- /dev/null +++ b/Source/Interface/Core/BlockSamplingData.cpp @@ -0,0 +1,51 @@ +#include "Interface/Core/BlockSamplingData.h" +#include "LogManager.h" +#include +#include + +namespace FEXCore { + void BlockSamplingData::DumpBlockData() { + std::fstream Output; + Output.open("output.csv", std::fstream::out | std::fstream::binary); + + if (!Output.is_open()) + return; + + Output << "Entry, Min, Max, Total, Calls, Average" << std::endl; + + for (auto it : SamplingMap) { + if (!it.second->TotalCalls) + continue; + + Output << "0x" << std::hex << it.first + << ", " << std::dec << it.second->Min + << ", " << std::dec << it.second->Max + << ", " << std::dec << it.second->TotalTime + << ", " << std::dec << it.second->TotalCalls + << ", " << std::dec << ((double)it.second->TotalTime / (double)it.second->TotalCalls) + << std::endl; + } + Output.close(); + LogMan::Msg::D("Dumped %d blocks of sampling data", SamplingMap.size()); + } + + BlockSamplingData::BlockData *BlockSamplingData::GetBlockData(uint64_t RIP) { + auto it = SamplingMap.find(RIP); + if (it != SamplingMap.end()) { + return it->second; + } + BlockData *NewData = new BlockData{}; + memset(NewData, 0, sizeof(BlockData)); + NewData->Min = ~0ULL; + SamplingMap[RIP] = NewData; + return NewData; + } + + BlockSamplingData::~BlockSamplingData() { + DumpBlockData(); + for (auto it : SamplingMap) { + delete it.second; + } + SamplingMap.clear(); + } +} diff --git a/Source/Interface/Core/BlockSamplingData.h b/Source/Interface/Core/BlockSamplingData.h new file mode 100644 index 000000000..8ac4257bc --- /dev/null +++ b/Source/Interface/Core/BlockSamplingData.h @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace FEXCore { +class BlockSamplingData { +public: + struct BlockData { + uint64_t Start, End; + uint64_t Min, Max; + uint64_t TotalTime; + uint64_t TotalCalls; + }; + + BlockData *GetBlockData(uint64_t RIP); + ~BlockSamplingData(); + + void DumpBlockData(); + +private: + std::unordered_map SamplingMap; +}; +} diff --git a/Source/Interface/Core/Core.cpp b/Source/Interface/Core/Core.cpp index 7fc575731..4b6e389bb 100644 --- a/Source/Interface/Core/Core.cpp +++ b/Source/Interface/Core/Core.cpp @@ -3,6 +3,7 @@ #include "Interface/Context/Context.h" #include "Interface/Core/BlockCache.h" +#include "Interface/Core/BlockSamplingData.h" #include "Interface/Core/Core.h" #include "Interface/Core/DebugData.h" #include "Interface/Core/OpcodeDispatcher.h" @@ -102,6 +103,9 @@ namespace FEXCore::Context { FallbackCPUFactory = FEXCore::Core::DefaultFallbackCore::CPUCreationFactory; PassManager.AddDefaultPasses(); PassManager.AddDefaultValidationPasses(); +#ifdef BLOCKSTATS + BlockData = std::make_unique(); +#endif } bool Context::GetFilenameHash(std::string const &Filename, std::string &Hash) { diff --git a/Source/Interface/Core/JIT/x86_64/JIT.cpp b/Source/Interface/Core/JIT/x86_64/JIT.cpp index a5563640f..fd3fb9bd7 100644 --- a/Source/Interface/Core/JIT/x86_64/JIT.cpp +++ b/Source/Interface/Core/JIT/x86_64/JIT.cpp @@ -1,4 +1,5 @@ #include "Interface/Context/Context.h" +#include "Interface/Core/BlockSamplingData.h" #include "Interface/Core/InternalThreadState.h" #include "Interface/IR/Passes/RegisterAllocationPass.h" @@ -99,6 +100,10 @@ private: using CustomDispatch = void(*)(FEXCore::Core::InternalThreadState *Thread); CustomDispatch DispatchPtr{}; IR::RegisterAllocationPass *RAPass; + +#ifdef BLOCKSTATS + bool GetSamplingData {true}; +#endif }; JITCore::JITCore(FEXCore::Context::Context *ctx) @@ -226,6 +231,49 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const auto HeaderOp = HeaderNode->Op(DataBegin)->CW(); LogMan::Throw::A(HeaderOp->Header.Op == IR::OP_IRHEADER, "First op wasn't IRHeader"); +#ifdef BLOCKSTATS + BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(HeaderOp->Entry); + if (GetSamplingData) { + mov(rcx, reinterpret_cast(SamplingData)); + rdtsc(); + shl(rdx, 32); + or(rax, rdx); + mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Start)], rax); + } + + auto ExitBlock = [&]() { + if (GetSamplingData) { + mov(rcx, reinterpret_cast(SamplingData)); + // Get time + rdtsc(); + shl(rdx, 32); + or(rax, rdx); + + // Calculate time spent in block + mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Start)]); + sub(rax, rdx); + + // Add time to total time + add(qword [rcx + offsetof(BlockSamplingData::BlockData, TotalTime)], rax); + + // Increment call count + inc(qword [rcx + offsetof(BlockSamplingData::BlockData, TotalCalls)]); + + // Calculate min + mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Min)]); + cmp(rdx, rax); + cmova(rdx, rax); + mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Min)], rdx); + + // Calculate max + mov(rdx, qword [rcx + offsetof(BlockSamplingData::BlockData, Max)]); + cmp(rdx, rax); + cmovb(rdx, rax); + mov(qword [rcx + offsetof(BlockSamplingData::BlockData, Max)], rdx); + } + }; +#endif + IR::OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin); while (1) { using namespace FEXCore::IR; @@ -317,6 +365,9 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const pop(rbp); pop(rbx); +#ifdef BLOCKSTATS + ExitBlock(); +#endif ret(); break; } @@ -342,6 +393,10 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const pop(r12); pop(rbp); pop(rbx); + +#ifdef BLOCKSTATS + ExitBlock(); +#endif ret(); break; default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);