diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index ec2126366..126102347 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -42,7 +42,6 @@ X64Backend::~X64Backend() { processor()->memory()->SystemHeapFree(emitter_data_); emitter_data_ = 0; } - delete code_cache_; } bool X64Backend::Initialize() { @@ -70,8 +69,8 @@ bool X64Backend::Initialize() { X64Emitter::XMM_COUNT, }; - code_cache_ = new X64CodeCache(); - Backend::code_cache_ = code_cache_; + code_cache_ = X64CodeCache::Create(); + Backend::code_cache_ = code_cache_.get(); if (!code_cache_->Initialize()) { return false; } diff --git a/src/xenia/cpu/backend/x64/x64_backend.h b/src/xenia/cpu/backend/x64/x64_backend.h index dc390d103..50b691935 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.h +++ b/src/xenia/cpu/backend/x64/x64_backend.h @@ -12,6 +12,8 @@ #include +#include + #include "xenia/cpu/backend/backend.h" DECLARE_bool(enable_haswell_instructions); @@ -36,7 +38,7 @@ class X64Backend : public Backend { X64Backend(Processor* processor); ~X64Backend() override; - X64CodeCache* code_cache() const { return code_cache_; } + X64CodeCache* code_cache() const { return code_cache_.get(); } uint32_t emitter_data() const { return emitter_data_; } // Call a generated function, saving all stack parameters. @@ -55,7 +57,7 @@ class X64Backend : public Backend { std::unique_ptr CreateAssembler() override; private: - X64CodeCache* code_cache_; + std::unique_ptr code_cache_; uint32_t emitter_data_; diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.cc b/src/xenia/cpu/backend/x64/x64_code_cache.cc index e06b772fb..81e2f5975 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.cc +++ b/src/xenia/cpu/backend/x64/x64_code_cache.cc @@ -18,19 +18,11 @@ #include "xenia/base/math.h" #include "xenia/base/memory.h" -// When enabled, this will use Windows 8 APIs to get unwind info. -// TODO(benvanik): figure out why the callback variant doesn't work. -#define USE_GROWABLE_FUNCTION_TABLE - namespace xe { namespace cpu { namespace backend { namespace x64 { -// Size of unwind info per function. -// TODO(benvanik): move this to emitter. -const static uint32_t kUnwindInfoSize = 4 + (2 * 1 + 2 + 2); - X64CodeCache::X64CodeCache() = default; X64CodeCache::~X64CodeCache() { @@ -39,17 +31,6 @@ X64CodeCache::~X64CodeCache() { xe::memory::DeallocationType::kRelease); } -#ifdef USE_GROWABLE_FUNCTION_TABLE - if (unwind_table_handle_) { - RtlDeleteGrowableFunctionTable(unwind_table_handle_); - } -#else - if (generated_code_base_) { - RtlDeleteFunctionTable(reinterpret_cast( - reinterpret_cast(generated_code_base_) | 0x3)); - } -#endif // USE_GROWABLE_FUNCTION_TABLE - // Unmap all views and close mapping. if (mapping_) { xe::memory::UnmapFileView(mapping_, generated_code_base_, @@ -97,38 +78,6 @@ bool X64CodeCache::Initialize() { return false; } - // Compute total number of unwind entries we should allocate. - // We don't support reallocing right now, so this should be high. - unwind_table_.resize(30000); - -#ifdef USE_GROWABLE_FUNCTION_TABLE - // Create table and register with the system. It's empty now, but we'll grow - // it as functions are added. - if (RtlAddGrowableFunctionTable( - &unwind_table_handle_, unwind_table_.data(), unwind_table_count_, - DWORD(unwind_table_.size()), - reinterpret_cast(generated_code_base_), - reinterpret_cast(generated_code_base_ + - kGeneratedCodeSize))) { - XELOGE("Unable to create unwind function table"); - return false; - } -#else - // Install a callback that the debugger will use to lookup unwind info on - // demand. - if (!RtlInstallFunctionTableCallback( - reinterpret_cast(generated_code_base_) | 0x3, - reinterpret_cast(generated_code_base_), - kGeneratedCodeSize, [](uintptr_t control_pc, void* context) { - auto code_cache = reinterpret_cast(context); - return reinterpret_cast( - code_cache->LookupUnwindEntry(control_pc)); - }, this, nullptr)) { - XELOGE("Unable to install function table callback"); - return false; - } -#endif // USE_GROWABLE_FUNCTION_TABLE - return true; } @@ -165,8 +114,7 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, size_t low_mark; size_t high_mark; uint8_t* code_address = nullptr; - uint8_t* unwind_entry_address = nullptr; - size_t unwind_table_slot = 0; + UnwindReservation unwind_reservation; { std::lock_guard allocation_lock(allocation_mutex_); @@ -180,9 +128,9 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, // Reserve unwind info. // We go on the high size of the unwind info as we don't know how big we // need it, and a few extra bytes of padding isn't the worst thing. - unwind_entry_address = generated_code_base_ + generated_code_offset_; - generated_code_offset_ += xe::round_up(kUnwindInfoSize, 16); - unwind_table_slot = ++unwind_table_count_; + unwind_reservation = + RequestUnwindReservation(generated_code_base_ + generated_code_offset_); + generated_code_offset_ += unwind_reservation.data_size; high_mark = generated_code_offset_; } @@ -203,18 +151,9 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, // Copy code. std::memcpy(code_address, machine_code, code_size); - // Add unwind info. - InitializeUnwindEntry(unwind_entry_address, unwind_table_slot, code_address, - code_size, stack_size); - -#ifdef USE_GROWABLE_FUNCTION_TABLE - // Notify that the unwind table has grown. - // We do this outside of the lock, but with the latest total count. - RtlGrowFunctionTable(unwind_table_handle_, unwind_table_count_); -#endif // USE_GROWABLE_FUNCTION_TABLE - - // This isn't needed on x64 (probably), but is convention. - FlushInstructionCache(GetCurrentProcess(), code_address, code_size); + // Notify subclasses of placed code. + PlaceCode(guest_address, machine_code, code_size, stack_size, code_address, + unwind_reservation); // Now that everything is ready, fix up the indirection table. // Note that we do support code that doesn't have an indirection fixup, so @@ -228,146 +167,6 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, return code_address; } -// http://msdn.microsoft.com/en-us/library/ssa62fwe.aspx -typedef enum _UNWIND_OP_CODES { - UWOP_PUSH_NONVOL = 0, /* info == register number */ - UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ - UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ - UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ - UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ - UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ - UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ - UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ - UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ -} UNWIND_CODE_OPS; -class UNWIND_REGISTER { - public: - enum _ { - RAX = 0, - RCX = 1, - RDX = 2, - RBX = 3, - RSP = 4, - RBP = 5, - RSI = 6, - RDI = 7, - R8 = 8, - R9 = 9, - R10 = 10, - R11 = 11, - R12 = 12, - R13 = 13, - R14 = 14, - R15 = 15, - }; -}; - -typedef union _UNWIND_CODE { - struct { - uint8_t CodeOffset; - uint8_t UnwindOp : 4; - uint8_t OpInfo : 4; - }; - USHORT FrameOffset; -} UNWIND_CODE, *PUNWIND_CODE; - -typedef struct _UNWIND_INFO { - uint8_t Version : 3; - uint8_t Flags : 5; - uint8_t SizeOfProlog; - uint8_t CountOfCodes; - uint8_t FrameRegister : 4; - uint8_t FrameOffset : 4; - UNWIND_CODE UnwindCode[1]; - /* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; - * union { - * OPTIONAL ULONG ExceptionHandler; - * OPTIONAL ULONG FunctionEntry; - * }; - * OPTIONAL ULONG ExceptionData[]; */ -} UNWIND_INFO, *PUNWIND_INFO; - -void X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address, - size_t unwind_table_slot, - uint8_t* code_address, - size_t code_size, size_t stack_size) { - auto unwind_info = reinterpret_cast(unwind_entry_address); - - if (!stack_size) { - // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx - unwind_info->Version = 1; - unwind_info->Flags = 0; - unwind_info->SizeOfProlog = 0; - unwind_info->CountOfCodes = 0; - unwind_info->FrameRegister = 0; - unwind_info->FrameOffset = 0; - } else if (stack_size <= 128) { - uint8_t prolog_size = 4; - - // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx - unwind_info->Version = 1; - unwind_info->Flags = 0; - unwind_info->SizeOfProlog = prolog_size; - unwind_info->CountOfCodes = 1; - unwind_info->FrameRegister = 0; - unwind_info->FrameOffset = 0; - - // http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx - size_t co = 0; - auto& unwind_code = unwind_info->UnwindCode[co++]; - unwind_code.CodeOffset = - 14; // end of instruction + 1 == offset of next instruction - unwind_code.UnwindOp = UWOP_ALLOC_SMALL; - unwind_code.OpInfo = stack_size / 8 - 1; - } else { - // TODO(benvanik): take as parameters? - uint8_t prolog_size = 7; - - // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx - unwind_info->Version = 1; - unwind_info->Flags = 0; - unwind_info->SizeOfProlog = prolog_size; - unwind_info->CountOfCodes = 2; - unwind_info->FrameRegister = 0; - unwind_info->FrameOffset = 0; - - // http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx - size_t co = 0; - auto& unwind_code = unwind_info->UnwindCode[co++]; - unwind_code.CodeOffset = - 7; // end of instruction + 1 == offset of next instruction - unwind_code.UnwindOp = UWOP_ALLOC_LARGE; - unwind_code.OpInfo = 0; - unwind_code = unwind_info->UnwindCode[co++]; - unwind_code.FrameOffset = (USHORT)(stack_size) / 8; - } - - // Add entry. - auto& fn_entry = unwind_table_[unwind_table_slot]; - fn_entry.BeginAddress = (DWORD)(code_address - generated_code_base_); - fn_entry.EndAddress = (DWORD)(fn_entry.BeginAddress + code_size); - fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_); -} - -void* X64CodeCache::LookupUnwindEntry(uintptr_t host_address) { - void* fn_entry = std::bsearch( - &host_address, unwind_table_.data(), unwind_table_count_ + 1, - sizeof(RUNTIME_FUNCTION), - [](const void* key_ptr, const void* element_ptr) { - auto key = - *reinterpret_cast(key_ptr) - kGeneratedCodeBase; - auto element = reinterpret_cast(element_ptr); - if (key < element->BeginAddress) { - return -1; - } else if (key > element->EndAddress) { - return 1; - } else { - return 0; - } - }); - return reinterpret_cast(fn_entry); -} - uint32_t X64CodeCache::PlaceData(const void* data, size_t length) { // Hold a lock while we bump the pointers up. size_t high_mark; diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.h b/src/xenia/cpu/backend/x64/x64_code_cache.h index ed88d8c2e..a60993fd1 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.h +++ b/src/xenia/cpu/backend/x64/x64_code_cache.h @@ -11,13 +11,13 @@ #define XENIA_BACKEND_X64_X64_CODE_CACHE_H_ #include +#include #include #include #include #include "xenia/base/memory.h" #include "xenia/base/mutex.h" -#include "xenia/base/platform_win.h" #include "xenia/cpu/backend/code_cache.h" namespace xe { @@ -27,10 +27,11 @@ namespace x64 { class X64CodeCache : public CodeCache { public: - X64CodeCache(); ~X64CodeCache() override; - bool Initialize(); + static std::unique_ptr Create(); + + virtual bool Initialize(); std::wstring file_name() const override { return file_name_; } uint32_t base_address() const override { return kGeneratedCodeBase; } @@ -50,16 +51,27 @@ class X64CodeCache : public CodeCache { uint32_t PlaceData(const void* data, size_t length); - private: + protected: const static uint64_t kIndirectionTableBase = 0x80000000; const static uint64_t kIndirectionTableSize = 0x1FFFFFFF; const static uint64_t kGeneratedCodeBase = 0xA0000000; const static uint64_t kGeneratedCodeSize = 0x0FFFFFFF; - void InitializeUnwindEntry(uint8_t* unwind_entry_address, - size_t unwind_table_slot, uint8_t* code_address, - size_t code_size, size_t stack_size); - void* LookupUnwindEntry(uintptr_t host_address); + struct UnwindReservation { + size_t data_size = 0; + size_t table_slot = 0; + uint8_t* entry_address = 0; + }; + + X64CodeCache(); + + virtual UnwindReservation RequestUnwindReservation(uint8_t* entry_address) { + return UnwindReservation(); + } + virtual void PlaceCode(uint32_t guest_address, void* machine_code, + size_t code_size, size_t stack_size, + void* code_address, + UnwindReservation unwind_reservation) {} std::wstring file_name_; xe::memory::FileMappingHandle mapping_ = nullptr; @@ -82,13 +94,6 @@ class X64CodeCache : public CodeCache { size_t generated_code_offset_ = 0; // Current high water mark of COMMITTED code. std::atomic generated_code_commit_mark_ = {0}; - - // Growable function table system handle. - void* unwind_table_handle_ = nullptr; - // Actual unwind table entries. - std::vector unwind_table_; - // Current number of entries in the table. - std::atomic unwind_table_count_ = {0}; }; } // namespace x64 diff --git a/src/xenia/cpu/backend/x64/x64_code_cache_win.cc b/src/xenia/cpu/backend/x64/x64_code_cache_win.cc new file mode 100644 index 000000000..7ae55cab7 --- /dev/null +++ b/src/xenia/cpu/backend/x64/x64_code_cache_win.cc @@ -0,0 +1,293 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/cpu/backend/x64/x64_code_cache.h" + +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/clock.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/memory.h" +#include "xenia/base/platform_win.h" + +// When enabled, this will use Windows 8 APIs to get unwind info. +// TODO(benvanik): figure out why the callback variant doesn't work. +#define USE_GROWABLE_FUNCTION_TABLE + +namespace xe { +namespace cpu { +namespace backend { +namespace x64 { + +// Size of unwind info per function. +// TODO(benvanik): move this to emitter. +const static uint32_t kUnwindInfoSize = 4 + (2 * 1 + 2 + 2); + +class Win32X64CodeCache : public X64CodeCache { + public: + Win32X64CodeCache(); + ~Win32X64CodeCache() override; + + bool Initialize() override; + + private: + UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override; + void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size, + size_t stack_size, void* code_address, + UnwindReservation unwind_reservation) override; + + void InitializeUnwindEntry(uint8_t* unwind_entry_address, + size_t unwind_table_slot, void* code_address, + size_t code_size, size_t stack_size); + void* LookupUnwindEntry(uintptr_t host_address); + + // Growable function table system handle. + void* unwind_table_handle_ = nullptr; + // Actual unwind table entries. + std::vector unwind_table_; + // Current number of entries in the table. + std::atomic unwind_table_count_ = {0}; +}; + +std::unique_ptr X64CodeCache::Create() { + return std::make_unique(); +} + +Win32X64CodeCache::Win32X64CodeCache() = default; + +Win32X64CodeCache::~Win32X64CodeCache() { +#ifdef USE_GROWABLE_FUNCTION_TABLE + if (unwind_table_handle_) { + RtlDeleteGrowableFunctionTable(unwind_table_handle_); + } +#else + if (generated_code_base_) { + RtlDeleteFunctionTable(reinterpret_cast( + reinterpret_cast(generated_code_base_) | 0x3)); + } +#endif // USE_GROWABLE_FUNCTION_TABLE +} + +bool Win32X64CodeCache::Initialize() { + if (!X64CodeCache::Initialize()) { + return false; + } + + // Compute total number of unwind entries we should allocate. + // We don't support reallocing right now, so this should be high. + unwind_table_.resize(30000); + +#ifdef USE_GROWABLE_FUNCTION_TABLE + // Create table and register with the system. It's empty now, but we'll grow + // it as functions are added. + if (RtlAddGrowableFunctionTable( + &unwind_table_handle_, unwind_table_.data(), unwind_table_count_, + DWORD(unwind_table_.size()), + reinterpret_cast(generated_code_base_), + reinterpret_cast(generated_code_base_ + + kGeneratedCodeSize))) { + XELOGE("Unable to create unwind function table"); + return false; + } +#else + // Install a callback that the debugger will use to lookup unwind info on + // demand. + if (!RtlInstallFunctionTableCallback( + reinterpret_cast(generated_code_base_) | 0x3, + reinterpret_cast(generated_code_base_), + kGeneratedCodeSize, [](uintptr_t control_pc, void* context) { + auto code_cache = reinterpret_cast(context); + return reinterpret_cast( + code_cache->LookupUnwindEntry(control_pc)); + }, this, nullptr)) { + XELOGE("Unable to install function table callback"); + return false; + } +#endif // USE_GROWABLE_FUNCTION_TABLE + + return true; +} + +Win32X64CodeCache::UnwindReservation +Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) { + UnwindReservation unwind_reservation; + unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16); + unwind_reservation.table_slot = ++unwind_table_count_; + unwind_reservation.entry_address = entry_address; + return unwind_reservation; +} + +void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, + size_t code_size, size_t stack_size, + void* code_address, + UnwindReservation unwind_reservation) { + // Add unwind info. + InitializeUnwindEntry(unwind_reservation.entry_address, + unwind_reservation.table_slot, code_address, code_size, + stack_size); + +#ifdef USE_GROWABLE_FUNCTION_TABLE + // Notify that the unwind table has grown. + // We do this outside of the lock, but with the latest total count. + RtlGrowFunctionTable(unwind_table_handle_, unwind_table_count_); +#endif // USE_GROWABLE_FUNCTION_TABLE + + // This isn't needed on x64 (probably), but is convention. + FlushInstructionCache(GetCurrentProcess(), code_address, code_size); +} + +// http://msdn.microsoft.com/en-us/library/ssa62fwe.aspx +typedef enum _UNWIND_OP_CODES { + UWOP_PUSH_NONVOL = 0, /* info == register number */ + UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ + UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ + UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ + UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ + UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ + UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ + UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ +} UNWIND_CODE_OPS; +class UNWIND_REGISTER { + public: + enum _ { + RAX = 0, + RCX = 1, + RDX = 2, + RBX = 3, + RSP = 4, + RBP = 5, + RSI = 6, + RDI = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + }; +}; + +typedef union _UNWIND_CODE { + struct { + uint8_t CodeOffset; + uint8_t UnwindOp : 4; + uint8_t OpInfo : 4; + }; + USHORT FrameOffset; +} UNWIND_CODE, *PUNWIND_CODE; + +typedef struct _UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + UNWIND_CODE UnwindCode[1]; + /* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; + * union { + * OPTIONAL ULONG ExceptionHandler; + * OPTIONAL ULONG FunctionEntry; + * }; + * OPTIONAL ULONG ExceptionData[]; */ +} UNWIND_INFO, *PUNWIND_INFO; + +void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address, + size_t unwind_table_slot, + void* code_address, + size_t code_size, + size_t stack_size) { + auto unwind_info = reinterpret_cast(unwind_entry_address); + + if (!stack_size) { + // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx + unwind_info->Version = 1; + unwind_info->Flags = 0; + unwind_info->SizeOfProlog = 0; + unwind_info->CountOfCodes = 0; + unwind_info->FrameRegister = 0; + unwind_info->FrameOffset = 0; + } else if (stack_size <= 128) { + uint8_t prolog_size = 4; + + // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx + unwind_info->Version = 1; + unwind_info->Flags = 0; + unwind_info->SizeOfProlog = prolog_size; + unwind_info->CountOfCodes = 1; + unwind_info->FrameRegister = 0; + unwind_info->FrameOffset = 0; + + // http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx + size_t co = 0; + auto& unwind_code = unwind_info->UnwindCode[co++]; + unwind_code.CodeOffset = + 14; // end of instruction + 1 == offset of next instruction + unwind_code.UnwindOp = UWOP_ALLOC_SMALL; + unwind_code.OpInfo = stack_size / 8 - 1; + } else { + // TODO(benvanik): take as parameters? + uint8_t prolog_size = 7; + + // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx + unwind_info->Version = 1; + unwind_info->Flags = 0; + unwind_info->SizeOfProlog = prolog_size; + unwind_info->CountOfCodes = 2; + unwind_info->FrameRegister = 0; + unwind_info->FrameOffset = 0; + + // http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx + size_t co = 0; + auto& unwind_code = unwind_info->UnwindCode[co++]; + unwind_code.CodeOffset = + 7; // end of instruction + 1 == offset of next instruction + unwind_code.UnwindOp = UWOP_ALLOC_LARGE; + unwind_code.OpInfo = 0; + unwind_code = unwind_info->UnwindCode[co++]; + unwind_code.FrameOffset = (USHORT)(stack_size) / 8; + } + + // Add entry. + auto& fn_entry = unwind_table_[unwind_table_slot]; + fn_entry.BeginAddress = + (DWORD)(reinterpret_cast(code_address) - generated_code_base_); + fn_entry.EndAddress = (DWORD)(fn_entry.BeginAddress + code_size); + fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_); +} + +void* Win32X64CodeCache::LookupUnwindEntry(uintptr_t host_address) { + void* fn_entry = std::bsearch( + &host_address, unwind_table_.data(), unwind_table_count_ + 1, + sizeof(RUNTIME_FUNCTION), + [](const void* key_ptr, const void* element_ptr) { + auto key = + *reinterpret_cast(key_ptr) - kGeneratedCodeBase; + auto element = reinterpret_cast(element_ptr); + if (key < element->BeginAddress) { + return -1; + } else if (key > element->EndAddress) { + return 1; + } else { + return 0; + } + }); + return reinterpret_cast(fn_entry); +} + +} // namespace x64 +} // namespace backend +} // namespace cpu +} // namespace xe