summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/drccache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/drccache.cpp')
-rw-r--r--src/devices/cpu/drccache.cpp186
1 files changed, 123 insertions, 63 deletions
diff --git a/src/devices/cpu/drccache.cpp b/src/devices/cpu/drccache.cpp
index 5906a641fee..edee380a61f 100644
--- a/src/devices/cpu/drccache.cpp
+++ b/src/devices/cpu/drccache.cpp
@@ -11,15 +11,22 @@
#include "emu.h"
#include "drccache.h"
+#include <algorithm>
-//**************************************************************************
-// MACROS
-//**************************************************************************
+namespace {
+
+template <typename T, typename U> constexpr T *ALIGN_PTR_UP(T *p, U align)
+{
+ return reinterpret_cast<T *>((uintptr_t(p) + (align - 1)) & ~uintptr_t(align - 1));
+}
-// ensure that all memory allocated is aligned to an 8-byte boundary
-#define ALIGN_PTR_UP(p) ((void *)(((uintptr_t)(p) + (CACHE_ALIGNMENT - 1)) & ~(CACHE_ALIGNMENT - 1)))
-#define ALIGN_PTR_DOWN(p) ((void *)((uintptr_t)(p) & ~(CACHE_ALIGNMENT - 1)))
+template <typename T, typename U> constexpr T *ALIGN_PTR_DOWN(T *p, U align)
+{
+ return reinterpret_cast<T *>(uintptr_t(p) & ~uintptr_t(align - 1));
+}
+
+} // anonymous namespace
@@ -31,17 +38,46 @@
// drc_cache - constructor
//-------------------------------------------------
-drc_cache::drc_cache(size_t bytes)
- : m_near((drccodeptr)osd_alloc_executable(bytes)),
- m_neartop(m_near),
- m_base(m_near + NEAR_CACHE_SIZE),
- m_top(m_base),
- m_end(m_near + bytes),
- m_codegen(nullptr),
- m_size(bytes)
+drc_cache::drc_cache(size_t bytes) :
+ m_cache({ NEAR_CACHE_SIZE, bytes - NEAR_CACHE_SIZE }, osd::virtual_memory_allocation::READ_WRITE_EXECUTE),
+ m_near(reinterpret_cast<drccodeptr>(m_cache.get())),
+ m_neartop(m_near),
+ m_base(ALIGN_PTR_UP(m_near + NEAR_CACHE_SIZE, m_cache.page_size())),
+ m_top(m_base),
+ m_limit(m_near + m_cache.size()),
+ m_end(m_limit),
+ m_codegen(nullptr),
+ m_size(m_cache.size()),
+ m_executable(false),
+ m_rwx(false)
{
- memset(m_free, 0, sizeof(m_free));
- memset(m_nearfree, 0, sizeof(m_nearfree));
+ // alignment and page size must be powers of two, cache must be page-aligned
+ assert(!(CACHE_ALIGNMENT & (CACHE_ALIGNMENT - 1)));
+ assert(!(m_cache.page_size() & (m_cache.page_size() - 1)));
+ assert(!(uintptr_t(m_near) & (m_cache.page_size() - 1)));
+ assert(m_cache.page_size() >= CACHE_ALIGNMENT);
+
+ std::fill(std::begin(m_free), std::end(m_free), nullptr);
+ std::fill(std::begin(m_nearfree), std::end(m_nearfree), nullptr);
+
+ if (!m_cache)
+ {
+ throw emu_fatalerror("drc_cache: Error allocating virtual memory");
+ }
+ else if (!m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE))
+ {
+ throw emu_fatalerror("drc_cache: Error marking cache read/write");
+ }
+ else if (m_cache.set_access(m_base - m_near, m_end - m_base, osd::virtual_memory_allocation::READ_WRITE_EXECUTE))
+ {
+ osd_printf_verbose("drc_cache: RWX pages supported\n");
+ m_rwx = true;
+ }
+ else
+ {
+ osd_printf_verbose("drc_cache: Using W^X mode\n");
+ m_rwx = false;
+ }
}
@@ -51,8 +87,6 @@ drc_cache::drc_cache(size_t bytes)
drc_cache::~drc_cache()
{
- // release the memory
- osd_free_executable(m_near, m_size);
}
@@ -64,10 +98,11 @@ drc_cache::~drc_cache()
void drc_cache::flush()
{
// can't flush in the middle of codegen
- assert(m_codegen == nullptr);
+ assert(!m_codegen);
// just reset the top back to the base and re-seed
m_top = m_base;
+ codegen_init();
}
@@ -83,9 +118,9 @@ void *drc_cache::alloc(size_t bytes)
// pick first from the free list
if (bytes < MAX_PERMANENT_ALLOC)
{
- free_link **linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
- free_link *link = *linkptr;
- if (link != nullptr)
+ free_link **const linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
+ free_link *const link = *linkptr;
+ if (link)
{
*linkptr = link->m_next;
return link;
@@ -93,11 +128,13 @@ void *drc_cache::alloc(size_t bytes)
}
// if no space, we just fail
- drccodeptr ptr = (drccodeptr)ALIGN_PTR_DOWN(m_end - bytes);
- if (m_top > ptr)
+ drccodeptr const ptr = ALIGN_PTR_DOWN(m_end - bytes, CACHE_ALIGNMENT);
+ drccodeptr const limit = ALIGN_PTR_DOWN(ptr, m_cache.page_size());
+ if (m_top > limit)
return nullptr;
// otherwise update the end of the cache
+ m_limit = limit;
m_end = ptr;
return ptr;
}
@@ -115,9 +152,9 @@ void *drc_cache::alloc_near(size_t bytes)
// pick first from the free list
if (bytes < MAX_PERMANENT_ALLOC)
{
- free_link **linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
- free_link *link = *linkptr;
- if (link != nullptr)
+ free_link **const linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
+ free_link *const link = *linkptr;
+ if (link)
{
*linkptr = link->m_next;
return link;
@@ -125,8 +162,8 @@ void *drc_cache::alloc_near(size_t bytes)
}
// if no space, we just fail
- drccodeptr ptr = (drccodeptr)ALIGN_PTR_UP(m_neartop);
- if (ptr + bytes > m_base)
+ drccodeptr const ptr = ALIGN_PTR_UP(m_neartop, CACHE_ALIGNMENT);
+ if ((ptr + bytes) > m_base)
return nullptr;
// otherwise update the top of the near part of the cache
@@ -143,15 +180,16 @@ void *drc_cache::alloc_near(size_t bytes)
void *drc_cache::alloc_temporary(size_t bytes)
{
// can't allocate in the middle of codegen
- assert(m_codegen == nullptr);
+ assert(!m_codegen);
// if no space, we just fail
- drccodeptr ptr = m_top;
- if (ptr + bytes >= m_end)
+ drccodeptr const ptr = m_top;
+ if ((ptr + bytes) > m_limit)
return nullptr;
// otherwise, update the cache top
- m_top = (drccodeptr)ALIGN_PTR_UP(ptr + bytes);
+ codegen_init();
+ m_top = ALIGN_PTR_UP(ptr + bytes, CACHE_ALIGNMENT);
return ptr;
}
@@ -163,23 +201,42 @@ void *drc_cache::alloc_temporary(size_t bytes)
void drc_cache::dealloc(void *memory, size_t bytes)
{
+ drccodeptr const mem = reinterpret_cast<drccodeptr>(memory);
assert(bytes < MAX_PERMANENT_ALLOC);
- assert(((drccodeptr)memory >= m_near && (drccodeptr)memory < m_base) || ((drccodeptr)memory >= m_end && (drccodeptr)memory < m_near + m_size));
+ assert(((mem >= m_near) && (mem < m_base)) || ((mem >= m_end) && (mem < (m_near + m_size))));
// determine which free list to add to
- free_link **linkptr;
- if ((drccodeptr)memory < m_base)
- linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
- else
- linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
+ free_link **const linkptr = &((mem < m_base) ? m_nearfree : m_free)[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT];
// link is into the free list for our size
- free_link *link = (free_link *)memory;
+ free_link *const link = reinterpret_cast<free_link *>(memory);
link->m_next = *linkptr;
*linkptr = link;
}
+void drc_cache::codegen_init()
+{
+ if (m_executable)
+ {
+ if (!m_rwx)
+ m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE);
+ m_executable = false;
+ }
+}
+
+
+void drc_cache::codegen_complete()
+{
+ if (!m_executable)
+ {
+ if (!m_rwx)
+ m_cache.set_access(m_base - m_near, ALIGN_PTR_UP(m_top, m_cache.page_size()) - m_base, osd::virtual_memory_allocation::READ_EXECUTE);
+ m_executable = true;
+ }
+}
+
+
//-------------------------------------------------
// begin_codegen - begin code generation
//-------------------------------------------------
@@ -187,15 +244,15 @@ void drc_cache::dealloc(void *memory, size_t bytes)
drccodeptr *drc_cache::begin_codegen(uint32_t reserve_bytes)
{
// can't restart in the middle of codegen
- assert(m_codegen == nullptr);
- assert(m_ooblist.first() == nullptr);
+ assert(!m_codegen);
+ assert(m_oob_list.empty());
- // if still no space, we just fail
- drccodeptr ptr = m_top;
- if (ptr + reserve_bytes >= m_end)
+ // if no space, we just fail
+ if ((m_top + reserve_bytes) > m_limit)
return nullptr;
// otherwise, return a pointer to the cache top
+ codegen_init();
m_codegen = m_top;
return &m_top;
}
@@ -207,23 +264,22 @@ drccodeptr *drc_cache::begin_codegen(uint32_t reserve_bytes)
drccodeptr drc_cache::end_codegen()
{
- drccodeptr result = m_codegen;
+ drccodeptr const result = m_codegen;
// run the OOB handlers
- oob_handler *oob;
- while ((oob = m_ooblist.detach_head()) != nullptr)
+ while (!m_oob_list.empty())
{
// call the callback
- oob->m_callback(&m_top, oob->m_param1, oob->m_param2);
- assert(m_top - m_codegen < CODEGEN_MAX_BYTES);
+ m_oob_list.front().m_callback(&m_top, m_oob_list.front().m_param1, m_oob_list.front().m_param2);
+ assert((m_top - m_codegen) < CODEGEN_MAX_BYTES);
- // release our memory
- oob->~oob_handler();
- dealloc(oob, sizeof(*oob));
+ // add it to the free list
+ m_oob_free.splice(m_oob_free.begin(), m_oob_list, m_oob_list.begin());
}
// update the cache top
- m_top = (drccodeptr)ALIGN_PTR_UP(m_top);
+ osd::invalidate_instruction_cache(m_codegen, m_top - m_codegen);
+ m_top = ALIGN_PTR_UP(m_top, CACHE_ALIGNMENT);
m_codegen = nullptr;
return result;
@@ -235,20 +291,24 @@ drccodeptr drc_cache::end_codegen()
// out-of-band codegen
//-------------------------------------------------
-void drc_cache::request_oob_codegen(drc_oob_delegate callback, void *param1, void *param2)
+void drc_cache::request_oob_codegen(drc_oob_delegate &&callback, void *param1, void *param2)
{
- assert(m_codegen != nullptr);
+ assert(m_codegen);
// pull an item from the free list
- oob_handler *oob = (oob_handler *)alloc(sizeof(*oob));
- assert(oob != nullptr);
- new (oob) oob_handler();
+ std::list<oob_handler>::iterator oob;
+ if (m_oob_free.empty())
+ {
+ oob = m_oob_list.emplace(m_oob_list.end());
+ }
+ else
+ {
+ oob = m_oob_free.begin();
+ m_oob_list.splice(m_oob_list.end(), m_oob_free, oob);
+ }
// fill it in
- oob->m_callback = callback;
+ oob->m_callback = std::move(callback);
oob->m_param1 = param1;
oob->m_param2 = param2;
-
- // add to the tail
- m_ooblist.append(*oob);
}