diff --git a/core/DebugReporter.cpp b/core/DebugReporter.cpp index 9452f560..d32e98cb 100644 --- a/core/DebugReporter.cpp +++ b/core/DebugReporter.cpp @@ -41,6 +41,18 @@ void DebugReport::OnSourceModAllInitialized() g_pSourcePawn->SetDebugListener(this); } +void DebugReport::OnDebugSpew(const char *msg, ...) +{ + va_list ap; + char buffer[512]; + + va_start(ap, msg); + UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + g_Logger.LogMessage("[SM] %s", buffer); +} + void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...) { va_list ap; diff --git a/core/DebugReporter.h b/core/DebugReporter.h index 9fa37aae..077e9b7d 100644 --- a/core/DebugReporter.h +++ b/core/DebugReporter.h @@ -43,6 +43,8 @@ public: // SMGlobalClass void OnSourceModAllInitialized(); public: // IDebugListener void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error); + void OnDebugSpew(const char *msg, ...); +public: void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...); void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...); private: diff --git a/core/NativeOwner.cpp b/core/NativeOwner.cpp index 3216eca4..a650438d 100644 --- a/core/NativeOwner.cpp +++ b/core/NativeOwner.cpp @@ -2,6 +2,10 @@ #include "ShareSys.h" #include "PluginSys.h" +CNativeOwner::CNativeOwner() : m_nMarkSerial(0) +{ +} + void CNativeOwner::SetMarkSerial(unsigned int serial) { m_nMarkSerial = serial; diff --git a/core/NativeOwner.h b/core/NativeOwner.h index c1a8dc46..2ba3c6ae 100644 --- a/core/NativeOwner.h +++ b/core/NativeOwner.h @@ -30,6 +30,8 @@ using namespace SourceHook; class CNativeOwner { +public: + CNativeOwner(); public: virtual void DropEverything(); public: diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 52e6f3aa..9a3987ad 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -76,6 +76,11 @@ void ShutdownJIT() notify(); } + if (g_pSourcePawn2 != NULL) + { + g_pSourcePawn2->Shutdown(); + } + g_pJIT->CloseLibrary(); } @@ -188,6 +193,26 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late g_pSourcePawn = getv1(); g_pSourcePawn2 = getv2(); + if (g_pSourcePawn2->GetAPIVersion() < 2) + { + g_pSourcePawn2 = NULL; + if (error && maxlength) + { + snprintf(error, maxlength, "JIT version is out of date"); + } + return false; + } + + if (!g_pSourcePawn2->Initialize()) + { + g_pSourcePawn2 = NULL; + if (error && maxlength) + { + snprintf(error, maxlength, "JIT could not be initialized"); + } + return false; + } + g_pSourcePawn2->SetDebugListener(&g_DbgReporter); g_pSourcePawn2->SetProfiler(&g_Profiler); diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index e75e0a27..6fb2ca90 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -447,7 +447,7 @@ bool CPlugin::IsDebugging() return false; } - return m_pRuntime->IsDebugging(); + return true; } void CPlugin::LibraryActions(bool dropping) @@ -507,61 +507,6 @@ IdentityToken_t *CPlugin::GetIdentity() return m_ident; } -bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength) -{ - int err; - - if (!IsRunnable()) - { - if (error) - { - snprintf(error, maxlength, "Plugin is not runnable."); - } - return false; - } - - if (debug && IsDebugging()) - { - if (error) - { - snprintf(error, maxlength, "Plugin is already in debug mode."); - } - return false; - } - else if (!debug && !IsDebugging()) - { - if (error) - { - snprintf(error, maxlength, "Plugins is already in production mode."); - } - return false; - } - - ICompilation *co = g_pSourcePawn2->StartCompilation(); - - if (!co->SetOption("debug", (debug) ? "1" : "0")) - { - if (error) - { - snprintf(error, maxlength, "Failed to change plugin mode (JIT failure)."); - } - return false; - } - - if ((err = m_pRuntime->ApplyCompilationOptions(co)) != SP_ERROR_NONE) - { - if (error) - { - snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err); - } - return false; - } - - UpdateInfo(); - - return true; -} - bool CPlugin::IsRunnable() { return (m_status <= Plugin_Paused) ? true : false; @@ -1057,7 +1002,7 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ LoadRes res; *wasloaded = false; - if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure) + if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure) { delete pl; return NULL; @@ -2195,14 +2140,7 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c { if (pl->GetStatus() == Plugin_Running) { - if (pl->IsDebugging()) - { - g_RootMenu.ConsolePrint(" Status: running, debugging"); - } - else - { - g_RootMenu.ConsolePrint(" Status: running"); - } + g_RootMenu.ConsolePrint(" Status: running"); } else { @@ -2245,83 +2183,6 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c return; } - else if (strcmp(cmd, "debug") == 0) - { - if (argcount < 5) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]"); - return; - } - - CPlugin *pl; - char *end; - const char *arg = command.Arg(3); - int id = strtol(arg, &end, 10); - - if (*end == '\0') - { - pl = GetPluginByOrder(id); - if (!pl) - { - g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); - return; - } - } - else - { - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); - - if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); - return; - } - } - - int res; - const char *mode = command.Arg(4); - if ((res=strcmp("on", mode)) && strcmp("off", mode)) - { - g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\""); - return; - } - - bool debug; - if (!res) - { - debug = true; - } - else - { - debug = false; - } - - if (debug && pl->IsDebugging()) - { - g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode."); - return; - } - else if (!debug && !pl->IsDebugging()) - { - g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin."); - return; - } - - char error[256]; - if (pl->ToggleDebugMode(debug, error, sizeof(error))) - { - g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename()); - return; - } - else - { - g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename()); - g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error); - return; - } - } else if (strcmp(cmd, "refresh") == 0) { g_SourceMod.DoGlobalPluginLoads(); @@ -2382,7 +2243,6 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c /* Draw the main menu */ g_RootMenu.ConsolePrint("SourceMod Plugins Menu:"); - g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin"); g_RootMenu.DrawGenericOption("info", "Information about a plugin"); g_RootMenu.DrawGenericOption("list", "Show loaded plugins"); g_RootMenu.DrawGenericOption("load", "Load a plugin"); @@ -2398,13 +2258,12 @@ bool CPluginManager::ReloadPlugin(CPlugin *pl) { List::iterator iter; char filename[PLATFORM_MAX_PATH]; - bool debug, wasloaded; + bool wasloaded; PluginType ptype; IPlugin *newpl; int id = 1; strcpy(filename, pl->m_filename); - debug = pl->IsDebugging(); ptype = pl->GetType(); for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++) @@ -2419,7 +2278,7 @@ bool CPluginManager::ReloadPlugin(CPlugin *pl) { return false; } - if (!(newpl=LoadPlugin(filename, debug, ptype, NULL, 0, &wasloaded))) + if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded))) { return false; } diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 1bb41141..ea662b39 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -198,11 +198,6 @@ public: */ void Call_OnAllPluginsLoaded(); - /** - * Toggles debug mode in the plugin - */ - bool ToggleDebugMode(bool debug, char *error, size_t maxlength); - /** * Returns true if a plugin is usable. */ diff --git a/core/systems/ShareSys.cpp b/core/systems/ShareSys.cpp index f5467fd3..eed57fac 100644 --- a/core/systems/ShareSys.cpp +++ b/core/systems/ShareSys.cpp @@ -413,9 +413,9 @@ void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, else { /* See if this has already been marked as a dependent. - * If it has, it means this relationship has already occurred, - * and there is no reason to do it again. - */ + * If it has, it means this relationship has already occurred, + * and there is no reason to do it again. + */ if (pEntry->owner != pPlugin && pEntry->owner->GetMarkSerial() != g_mark_serial) { diff --git a/knight/shared/KeCodeAllocator.cpp b/knight/shared/KeCodeAllocator.cpp new file mode 100644 index 00000000..c89aa126 --- /dev/null +++ b/knight/shared/KeCodeAllocator.cpp @@ -0,0 +1,428 @@ +#include +#include +#include + +#if defined KE_PLATFORM_WINDOWS +#include +#elif defined KE_PLATFORM_POSIX +#include +#include +#include +#else +#error "TODO" +#endif + +#include "KeCodeAllocator.h" + +#define ALIGNMENT 16 + +using namespace Knight; + +struct KeFreedCode; + +/** + * Defines a region of memory that is made of pages. + */ +struct KeCodeRegion +{ + KeCodeRegion *next; + unsigned char *block_start; + unsigned char *block_pos; + KeFreedCode *free_list; + size_t total_size; + size_t end_free; + size_t total_free; +}; + +/** + * Defines freed code. We keep the size here because + * when we touch the linked list we don't want to dirty pages. + */ +struct KeFreedCode +{ + KeCodeRegion *region; + unsigned char *block_start; + size_t size; + KeFreedCode *next; +}; + +struct KeSecret +{ + KeCodeRegion *region; + size_t size; +}; + +class Knight::KeCodeCache +{ +public: + /** + * First region that is live for use. + */ + KeCodeRegion *first_live; + + /** + * First region that is full but has free entries. + */ + KeCodeRegion *first_partial; + + /** + * First region that is full. + */ + KeCodeRegion *first_full; + + /** + * Page granularity and size. + */ + unsigned int page_size; + unsigned int page_granularity; + + /** + * This isn't actually for code, this is the node cache. + */ + KeCodeRegion *node_cache; + KeFreedCode *free_node_list; +}; + +KeCodeCache *Knight::KE_CreateCodeCache() +{ + KeCodeCache *cache; + + cache = new KeCodeCache; + + memset(cache, 0, sizeof(KeCodeCache)); + +#if defined KE_PLATFORM_WINDOWS + SYSTEM_INFO info; + + GetSystemInfo(&info); + cache->page_size = info.dwPageSize; + cache->page_granularity = info.dwAllocationGranularity; +#else + cache->page_size = cache->page_granularity = sysconf(_SC_PAGESIZE); +#endif + + return cache; +} + +inline size_t MinAllocSize() +{ + size_t size; + + size = sizeof(KeSecret); + size += ALIGNMENT; + size -= size % ALIGNMENT; + + return size; +} + +inline size_t ke_GetAllocSize(size_t size) +{ + size += sizeof(KeSecret); + size += ALIGNMENT; + size -= size % ALIGNMENT; + + return size; +} + +void *ke_AllocInRegion(KeCodeCache *cache, + KeCodeRegion **prev, + KeCodeRegion *region, + unsigned char *ptr, + size_t alloc_size, + bool is_live) +{ + KeSecret *secret; + + /* Squirrel some info in the alloc. */ + secret = (KeSecret *)ptr; + secret->region = region; + secret->size = alloc_size; + ptr += sizeof(KeSecret); + + region->total_free -= alloc_size; + + /* Check if we can't use the fast-path anymore. */ + if ((is_live && region->end_free < MinAllocSize()) + || (!is_live && region->total_free < MinAllocSize())) + { + KeCodeRegion **start; + + *prev = region->next; + + /* Select the appropriate arena. */ + if (is_live) + { + if (region->total_free < MinAllocSize()) + { + start = &cache->first_full; + } + else + { + start = &cache->first_partial; + } + } + else + { + start = &cache->first_full; + } + + region->next = *start; + *start = region; + } + + return ptr; +} + +void *ke_AllocFromLive(KeCodeCache *cache, size_t size) +{ + void *ptr; + size_t alloc_size; + KeCodeRegion *region, **prev; + + region = cache->first_live; + prev = &cache->first_live; + alloc_size = ke_GetAllocSize(size); + + while (region != NULL) + { + if (region->end_free >= alloc_size) + { + /* Yay! We can do a simple alloc here. */ + ptr = ke_AllocInRegion(cache, prev, region, region->block_pos, alloc_size, true); + + /* Update our counters. */ + region->block_pos += alloc_size; + region->end_free -= alloc_size; + + return ptr; + } + prev = ®ion->next; + region = region->next; + } + + return NULL; +} + +void *ke_AllocFromPartial(KeCodeCache *cache, size_t size) +{ + void *ptr; + size_t alloc_size; + KeCodeRegion *region, **prev; + + region = cache->first_partial; + prev = &cache->first_partial; + alloc_size = ke_GetAllocSize(size); + + while (region != NULL) + { + if (region->total_free >= alloc_size) + { + KeFreedCode *node, **last; + + assert(region->free_list != NULL); + + last = ®ion->free_list; + node = region->free_list; + while (node != NULL) + { + if (node->size >= alloc_size) + { + /* Use this node */ + ptr = ke_AllocInRegion(cache, prev, region, node->block_start, alloc_size, false); + + region->total_free -= node->size; + *last = node->next; + + /* Make sure bookkeepping is correct. */ + assert((region->free_list == NULL && region->total_free == 0) + || (region->free_list != NULL && region->total_free != 0)); + + /* Link us back into the free node list. */ + node->next = cache->free_node_list; + cache->free_node_list = node->next; + + return ptr; + } + last = &node->next; + node = node->next; + } + } + prev = ®ion->next; + region = region->next; + } + + return NULL; +} + +KeCodeRegion *ke_AddRegionForSize(KeCodeCache *cache, size_t size) +{ + KeCodeRegion *region; + + region = new KeCodeRegion; + + size = ke_GetAllocSize(size); + size += cache->page_granularity * 2; + size -= size % cache->page_granularity; + +#if defined KE_PLATFORM_WINDOWS + region->block_start = (unsigned char *)VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#elif defined KE_PLATFORM_POSIX + region->block_start = (unsigned char *)valloc(size); + if (mprotect(region->block_start, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1) + { + free(region->block_start); + delete region; + return NULL; + } +#else +#error "TODO" +#endif + + if (region->block_start == NULL) + { + delete region; + return NULL; + } + + region->block_pos = region->block_start; + region->end_free = region->total_free = region->total_size = size; + region->next = cache->first_live; + cache->first_live = region; + region->free_list = NULL; + + return region; +} + +void *Knight::KE_AllocCode(KeCodeCache *cache, size_t size) +{ + void *ptr; + + /* Check live easy-adds */ + if (cache->first_live != NULL) + { + if ((ptr = ke_AllocFromLive(cache, size)) != NULL) + { + return ptr; + } + } + + /* Try looking in the free lists */ + if (cache->first_partial != NULL) + { + if ((ptr = ke_AllocFromPartial(cache, size)) != NULL) + { + return ptr; + } + } + + /* Create a new region */ + if (ke_AddRegionForSize(cache, size) == NULL) + { + return NULL; + } + + return ke_AllocFromLive(cache, size); +} + +KeFreedCode *ke_GetFreeNode(KeCodeCache *cache) +{ + KeFreedCode *ret; + + if (cache->free_node_list != NULL) + { + ret = cache->free_node_list; + cache->free_node_list = ret->next; + + return ret; + } + + /* See if the current free node region has space. */ + if (cache->node_cache != NULL + && cache->node_cache->end_free >= sizeof(KeFreedCode)) + { + ret = (KeFreedCode *)cache->node_cache->block_pos; + cache->node_cache->block_pos += sizeof(KeFreedCode); + cache->node_cache->total_free -= sizeof(KeFreedCode); + cache->node_cache->end_free -= sizeof(KeFreedCode); + + return ret; + } + + /* Otherwise, we need to alloc a new region. */ + KeCodeRegion *region = new KeCodeRegion; + + region->block_start = new unsigned char[cache->page_size / sizeof(KeFreedCode)]; + region->block_pos = region->block_start + sizeof(KeFreedCode); + region->total_size = cache->page_size / sizeof(KeFreedCode); + region->total_free = region->end_free = (region->total_size - sizeof(KeFreedCode)); + region->free_list = NULL; + region->next = cache->node_cache; + cache->node_cache = region; + + return (KeFreedCode *)region->block_start; +} + +void Knight::KE_FreeCode(KeCodeCache *cache, void *code) +{ + KeSecret *secret; + KeFreedCode *node; + unsigned char *ptr; + KeCodeRegion *region; + + ptr = (unsigned char *)code; + secret = (KeSecret *)(ptr - sizeof(KeSecret)); + region = secret->region; + node = ke_GetFreeNode(cache); + node->block_start = (unsigned char *)code; + node->next = region->free_list; + region->free_list = node; + node->region = region; + node->size = secret->size; +} + +KeCodeRegion *ke_DestroyRegion(KeCodeRegion *region) +{ + KeCodeRegion *next; + + next = region->next; + +#if defined KE_PLATFORM_WINDOWS + VirtualFree(region->block_start, 0, MEM_RELEASE); +#else + free(region->block_start); +#endif + + delete region; + + return next; +} + +void ke_DestroyRegionChain(KeCodeRegion *first) +{ + while (first != NULL) + { + first = ke_DestroyRegion(first); + } +} + +void Knight::KE_DestroyCodeCache(KeCodeCache *cache) +{ + /* Destroy every region and call it a day. */ + ke_DestroyRegionChain(cache->first_full); + ke_DestroyRegionChain(cache->first_live); + ke_DestroyRegionChain(cache->first_partial); + + /* We use normal malloc for node cache regions */ + KeCodeRegion *region, *next; + + region = cache->node_cache; + while (region != NULL) + { + next = region->next; + delete [] region->block_start; + delete region; + region = next; + } + + delete cache; +} diff --git a/knight/shared/KeCodeAllocator.h b/knight/shared/KeCodeAllocator.h new file mode 100644 index 00000000..5bd05b3b --- /dev/null +++ b/knight/shared/KeCodeAllocator.h @@ -0,0 +1,47 @@ +#ifndef _INCLUDE_KNIGHT_KE_CODE_ALLOCATOR_H_ +#define _INCLUDE_KNIGHT_KE_CODE_ALLOCATOR_H_ + +#include +#include + +namespace Knight +{ + class KeCodeCache; + + /** + * @brief Creates a new code cache/allocator. + * + * @return New code cache allocator. + */ + extern KeCodeCache *KE_CreateCodeCache(); + + /** + * @brief Destroys a code cache allocator. + * + * @param cache Code cache object. + */ + extern void KE_DestroyCodeCache(KeCodeCache *cache); + + /** + * @brief Allocates code memory that is readable, writable, + * and executable. + * + * The address returned wlil be aligned, minimally, on a 16-byte + * boundary. + * + * @param cache Code cache object. + * @param size Amount of memory needed. + * @return Address pointing to the memory. + */ + extern void *KE_AllocCode(KeCodeCache *cache, size_t size); + + /** + * @brief Frees code memory. + * + * @param cache Code cache object. + * @param code Address of code memory. + */ + extern void KE_FreeCode(KeCodeCache *cache, void *code); +} + +#endif //_INCLUDE_KNIGHT_KE_CODE_ALLOCATOR_H_ diff --git a/knight/shared/KeCommon.cpp b/knight/shared/KeCommon.cpp new file mode 100644 index 00000000..ce23e43b --- /dev/null +++ b/knight/shared/KeCommon.cpp @@ -0,0 +1,33 @@ +#include +#include "KeCommon.h" + +using namespace Knight; + +size_t Knight::KE_PFormat(char *buffer, size_t maxlength, const char *fmt, ...) +{ + size_t len; + + va_list ap; + va_start(ap, fmt); + len = KE_PFormatArgs(buffer, maxlength, fmt, ap); + va_end(ap); + + return len; +} + +size_t Knight::KE_PFormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap) +{ + size_t len; + + len = vsnprintf(buffer, maxlength, fmt, ap); + + if (len >= maxlength) + { + buffer[maxlength - 1] = '\0'; + return (maxlength - 1); + } + else + { + return len; + } +} diff --git a/knight/shared/KeCommon.h b/knight/shared/KeCommon.h new file mode 100644 index 00000000..c76b36a4 --- /dev/null +++ b/knight/shared/KeCommon.h @@ -0,0 +1,36 @@ +#ifndef _INCLUDE_KNIGHT_KE_COMMON_UTILS_H_ +#define _INCLUDE_KNIGHT_KE_COMMON_UTILS_H_ + +#include +#include + +namespace Knight +{ + /** + * @brief Formats a buffer with C platform rules. + * + * Unlink platform snprintf, this will never return nonsense values like -1. + * + * @param buffer Buffer to store to. + * @param maxlength Maximum length of buffer (including null terminator). + * @param fmt printf() format string. + * @param ... Formatting arguments. + * @return Number of characters written. + */ + extern size_t KE_PFormat(char *buffer, size_t maxlength, const char *fmt, ...); + + /** + * @brief Formats a buffer with C platform rules. + * + * Unlink platform snprintf, this will never return nonsense values like -1. + * + * @param buffer Buffer to store to. + * @param maxlength Maximum length of buffer (including null terminator). + * @param fmt printf() format string. + * @param args Formatting arguments. + * @return Number of characters written. + */ + extern size_t KE_PFormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list args); +} + +#endif //_INCLUDE_KNIGHT_KE_COMMON_UTILS_H_ diff --git a/knight/shared/KeHashTable.cpp b/knight/shared/KeHashTable.cpp new file mode 100644 index 00000000..24bc1e4b --- /dev/null +++ b/knight/shared/KeHashTable.cpp @@ -0,0 +1,409 @@ +#include + +using namespace Knight; + +struct KeHashNode +{ + KeHashNode *next; + uint32_t key_hash; + const void *key; + void *value; +}; + +namespace Knight +{ + class KeHashTable + { + public: + KeHashNode **buckets; + uint32_t num_buckets; + uint32_t shift; + uint32_t num_entries; + KeHashGenerator key_gen; + KeHashMarshal key_m; + KeHashMarshal val_m; + ke_allocator_t *node_alloc; + size_t key_offs; + size_t val_offs; + size_t node_size; + uint32_t grow_limit; + KeHashNode *free_list; + bool keep_free_list; + }; +} + +void *ke_DefHashMalloc(ke_allocator_t *alloc, size_t amt) +{ + return malloc(amt); +} + +void ke_DefHashFree(ke_allocator_t *alloc, void *addr) +{ + free(addr); +} + +ke_allocator_t s_DefHashAllocator = +{ + ke_DefHashMalloc, + ke_DefHashFree, + NULL +}; + +KeHashTable *Knight::KE_CreateHashTable( + uint32_t bits, + KeHashGenerator key_gen, + const KeHashMarshal *key_marshal, + const KeHashMarshal *val_marshal, + ke_allocator_t *nodeAlloc, + bool keep_free_list) +{ + KeHashTable *table; + + if (bits >= 27) + { + bits = 26; + } + else if (bits < 4) + { + bits = 4; + } + + /* Validate marshals. */ + if ((key_marshal->bytes != 0 + && key_marshal->ctor == NULL) + || (val_marshal->bytes != 0 + && val_marshal->ctor == NULL)) + { + return NULL; + } + + table = new KeHashTable; + table->key_gen = key_gen; + table->key_m = *key_marshal; + table->val_m = *val_marshal; + table->num_entries = 0; + table->shift = bits; + table->node_alloc = nodeAlloc == NULL ? &s_DefHashAllocator : nodeAlloc; + table->num_buckets = (1 << bits); + table->grow_limit = (uint32_t)(0.9f * table->num_buckets); + table->keep_free_list = keep_free_list; + table->free_list = NULL; + table->buckets = (KeHashNode **)malloc(sizeof(KeHashNode *) * table->num_buckets); + memset(table->buckets, 0, sizeof(KeHashNode *) * table->num_buckets); + + table->key_offs = sizeof(KeHashNode); + if (table->key_m.bytes != 0 && table->key_m.bytes % 8 != 0) + { + table->key_m.bytes += 8; + table->key_m.bytes -= (table->key_m.bytes % 8); + } + + table->val_offs = table->key_offs + table->key_m.bytes; + table->node_size = table->val_offs + table->val_m.bytes; + + return table; +} + +#define KE_GET_BUCKET(tbl, hsh) (&(tbl)->buckets[((hsh) * 0x9E3779B9) >> (tbl)->shift]) + +KeHashNode **ke_HashInternalFind(KeHashTable *table, uint32_t key_hash, const void *key) +{ + KeHashNode *node; + KeHashNode **bucket; + + bucket = KE_GET_BUCKET(table, key_hash); + + /* :TODO: move to the front once found? */ + + while ((node = *bucket) != NULL) + { + if (node->key_hash == key_hash + && ((table->key_m.cmp != NULL && table->key_m.cmp(node->key, key)) + || node->key == key)) + { + return bucket; + } + bucket = &node->next; + } + + return bucket; +} + +void ke_ResizeHashTable(KeHashTable *table, uint32_t new_shift) +{ + uint32_t entries; + KeHashNode *next; + KeHashNode *node; + KeHashNode **rbucket; + KeHashNode **old_buckets; + uint32_t old_num_buckets; + + /* Save old data */ + old_num_buckets = table->num_buckets; + old_buckets = table->buckets; + entries = table->num_entries; + + /* Save new data */ + table->num_buckets = (1 << new_shift); + table->shift = new_shift; + table->grow_limit = (uint32_t)(0.9f * table->num_buckets); + + table->buckets = (KeHashNode **)malloc(sizeof(KeHashNode *) * table->num_buckets); + memset(table->buckets, 0, sizeof(KeHashNode *) * table->num_buckets); + + /* For each old bucket... */ + for (uint32_t i = 0; + i < old_num_buckets && entries != 0; + i++) + { + node = old_buckets[i]; + /* Get each item in its list... */ + while (node != NULL) + { + next = node->next; + + /* Find the new replacement bucket it needs to go in. */ + rbucket = KE_GET_BUCKET(table, node->key_hash); + + /* Link this node to the next node in the new bucket. */ + if (*rbucket == NULL) + { + node->next = NULL; + } + else + { + node->next = *rbucket; + } + + /* Add us to the front of that bucket's list. */ + *rbucket = node; + + node = next; + } + } + + free(old_buckets); +} + +void Knight::KE_AddToHashTable(KeHashTable *table, const void *key, void *val) +{ + KeHashNode *node; + uint32_t key_hash; + KeHashNode **bucket; + + key_hash = table->key_gen(key); + bucket = ke_HashInternalFind(table, key_hash, key); + + if ((node = *bucket) != NULL) + { + /* Already in the table */ + if ((table->val_m.cmp != NULL && table->val_m.cmp(node->value, val)) + || node->value == val) + { + return; + } + + /* Destroy old value if it's set. */ + if (node->value != NULL && table->val_m.dtor != NULL) + { + table->val_m.dtor(node->value); + } + + /* Construct or set the new value. */ + if (table->val_m.bytes != 0) + { + table->val_m.ctor(node->value, val); + } + else + { + node->value = val; + } + + return; + } + + /* If we're overloaded, we may need to resize. + * Right now, we do this if we hit a .9 entry:buckets ratio. + */ + if (table->num_entries >= table->grow_limit) + { + ke_ResizeHashTable(table, table->shift << 1); + bucket = ke_HashInternalFind(table, key_hash, key); + } + + if (table->free_list != NULL) + { + node = table->free_list; + table->free_list = node->next; + } + else + { + node = (KeHashNode *)table->node_alloc->alloc(table->node_alloc, table->node_size); + } + + if (table->key_m.bytes == 0) + { + node->key = key; + } + else + { + node->key = (char *)node + table->key_offs; + table->key_m.ctor((void *)node->key, key); + } + + if (table->val_m.bytes == 0) + { + node->value = val; + } + else + { + node->value = (char *)node + table->val_offs; + table->val_m.ctor(node->value, val); + } + + node->next = *bucket; + node->key_hash = key_hash; + *bucket = node; +} + +inline void ke_CleanUpHashNode(KeHashTable *table, KeHashNode *node) +{ + /* Destroy old value if it's set. */ + if (node->value != NULL && table->val_m.dtor != NULL) + { + table->val_m.dtor(node->value); + } + + /* Destroy the key. */ + if (table->key_m.dtor != NULL) + { + table->key_m.dtor(node->key); + } + + /* Deallocate us as appropriate. */ + if (table->keep_free_list) + { + node->next = table->free_list; + table->free_list = node; + } + else + { + table->node_alloc->dealloc(table->node_alloc, node); + } +} + +void Knight::KE_RemoveFromHashTable(KeHashTable *table, const void *key) +{ + KeHashNode *node; + uint32_t key_hash; + KeHashNode **bucket; + + key_hash = table->key_gen(key); + bucket = ke_HashInternalFind(table, key_hash, key); + + if ((node = *bucket) == NULL) + { + return; + } + + /* Link the bucket to its next (removing us). */ + *bucket = node->next; + + ke_CleanUpHashNode(table, node); +} + +bool Knight::KE_FindInHashTable(KeHashTable *table, const void *key, void **value) +{ + KeHashNode *node; + uint32_t key_hash; + KeHashNode **bucket; + + key_hash = table->key_gen(key); + bucket = ke_HashInternalFind(table, key_hash, key); + + if ((node = *bucket) == NULL) + { + return false; + } + + if (value != NULL) + { + *value = node->value; + } + + return true; +} + +void Knight::KE_DestroyHashTable(KeHashTable *table) +{ + KeHashNode *node, *next; + + /* Turn off this caching! */ + table->keep_free_list = false; + + /* Find entries in buckets that need to be freed. */ + for (uint32_t i = 0; i < table->num_buckets; i++) + { + node = table->buckets[i]; + + while (node != NULL) + { + next = node->next; + ke_CleanUpHashNode(table, node); + node = next; + } + } + + /* Free the free list */ + while (table->free_list != NULL) + { + next = table->free_list->next; + ke_CleanUpHashNode(table, table->free_list); + table->free_list = next; + } + + /* Destroy everything now. */ + free(table->buckets); + delete table; +} + +void Knight::KE_ClearHashTable(KeHashTable *table) +{ + KeHashNode *node, *next; + + /* Free every entry in the table. */ + for (uint32_t i = 0; i < table->num_buckets; i++) + { + node = table->buckets[i]; + + while (node != NULL) + { + next = node->next; + ke_CleanUpHashNode(table, node); + node = next; + } + } +} + +#if defined _MSC_VER && (defined _M_IX86 || defined _M_AMD64 || defined _M_X64) +#pragma intrinsic(_rotl) +#endif + +uint32_t Knight::KE_HashString(const void *str) +{ + uint32_t h; + const unsigned char *us; + + h = 0; + + for (us = (const unsigned char *)str; *us != 0; us++) + { +#if defined _MSC_VER && (defined _M_IX86 || defined _M_AMD64 || defined _M_X64) + h = _rotl(h, 4) ^ *us; +#else + h = ((h << 4) | (h >> 28)) ^ *us; +#endif + } + + return h; +} diff --git a/knight/shared/KeHashTable.h b/knight/shared/KeHashTable.h new file mode 100644 index 00000000..79a69952 --- /dev/null +++ b/knight/shared/KeHashTable.h @@ -0,0 +1,128 @@ +#ifndef _INCLUDE_KNIGHT_KE_HASHTABLE_H_ +#define _INCLUDE_KNIGHT_KE_HASHTABLE_H_ + +#include +#include + +namespace Knight +{ + class KeHashTable; + + /** + * @brief Must generate a hash function given a key. + * + * @param key Pointer to the key. + * @return Hash value. + */ + typedef uint32_t (*KeHashGenerator)(const void *key); + + /** + * @brief Must compare two values. + * + * @param val1 First value. + * @param val2 Second value. + * @return True if equal, false if not. + */ + typedef bool (*KeHashComparator)(const void *val1, const void *val2); + + /** + * @brief Must call the destructor of the given data, and free if necessary. + * + * @param val Pointer to data. + */ + typedef void (*KeHashDestructor)(const void *val); + + /** + * @brief Must transfer the contents of an object from the source to the destination. + * + * @param dest Destination address. + * @param source Source address. + */ + typedef void (*KeHashCopyCtor)(void *dest, const void *source); + + /** + * @brief Contains information about how to process keys and values in a hash table. + */ + struct KeHashMarshal + { + size_t bytes; /**< Bytes of storage needed (0 to use pointers). */ + KeHashComparator cmp; /**< Comparator (if NULL, void * comparison used) */ + KeHashDestructor dtor; /**< Optional function for performing dtor cleanup. */ + KeHashCopyCtor ctor; /**< If bytes != 0, must be a valid function + (ignored otherwise). */ + }; + + /** + * @brief Creates a new hash table structure. + * + * @param bits Dictates starting number of buckets as a power of two. + * Pass 0 for the default (which is 4). + * @param key_gen Key generation function. + * @param key_marshal Structure detailing how to marshal keys. + * @param vak_marshal Structure detailing how to marshal values. + * @param nodeAlloc Node allocator (can be NULL for malloc/free). + * @param keep_free_list True to keep a free list of nodes, false otherwise. + * @return New hash table container. + */ + extern KeHashTable *KE_CreateHashTable( + uint32_t bits, + KeHashGenerator key_gen, + const KeHashMarshal *key_marshal, + const KeHashMarshal *val_marshal, + ke_allocator_t *nodeAlloc, + bool keep_free_list + ); + + /** + * @brief Destroys a hash table. + * + * @param table Hash table. + */ + extern void KE_DestroyHashTable(KeHashTable *table); + + /** + * @brief Adds a key/value to the hash table. If the pair already exists, the old value + * is overwritten (calling any destructors as necessary). + * + * @param table Hash table. + * @param key Key pointer. + * @param val Value pointer. + */ + extern void KE_AddToHashTable(KeHashTable *table, const void *key, void *val); + + /** + * @brief Removes a key entry from the hash table. + * + * @param table Hash table. + * @param key Key pointer. + */ + extern void KE_RemoveFromHashTable(KeHashTable *table, const void *key); + + /** + * @brief Finds an entry in the hash table. + * + * @param table Hash table. + * @param key Key pointer. + * @param value Pointer to store the value (optional). + * @return On success, true is returned and value is filled if given. + * On failure, false is failed and outputs are undefined. + */ + extern bool KE_FindInHashTable(KeHashTable *table, const void *key, void **value); + + /** + * @brief Clears all entries in the hash table (caching free entries when possible). + * + * @param table Hash table. + */ + extern void KE_ClearHashTable(KeHashTable *table); + + /** + * @brief Generic function for hasing strings. + * + * @param str Key string. + * @return Hash value. + */ + extern uint32_t KE_HashString(const void *str); +} + +#endif //_INCLUDE_KNIGHT_KE_HASHTABLE_H_ diff --git a/knight/shared/KeLinking.h b/knight/shared/KeLinking.h new file mode 100644 index 00000000..9bb7bb07 --- /dev/null +++ b/knight/shared/KeLinking.h @@ -0,0 +1,12 @@ +#ifndef _INCLUDE_KNIGHT_LINKING_H_ +#define _INCLUDE_KNIGHT_LINKING_H_ + +#if defined KE_LINK_IMPORT +#error "Not yet supported" +#elif defined KE_LINK_EXPORT +#error "Not yet supported" +#else +#define KE_LINK +#endif + +#endif //_INCLUDE_KNIGHT_LINKING_H_ diff --git a/knight/shared/KeLumpAllocator.cpp b/knight/shared/KeLumpAllocator.cpp new file mode 100644 index 00000000..022be142 --- /dev/null +++ b/knight/shared/KeLumpAllocator.cpp @@ -0,0 +1,258 @@ +#include "KePlatform.h" +#include +#include + +using namespace Knight; + +/** + * :TODO: don't make this part of the page, because touching it means + * dirtying a page. Instead, we should have a separate linked list. + * Maybe that linked list itself could be marshalled from one page. + */ +struct KeLumpRegion +{ + char *base; + char *cur; + size_t size; + size_t avail; + KeLumpRegion *next; + KeLumpRegion *prev; +}; + +class KeLumpAllocator +{ +public: + KeLumpAllocator() : m_pUsableRegions(NULL), m_pUnusableRegions(NULL) + { + m_DefLumpSize = 65536; + +#if defined KE_PLATFORM_WINDOWS + SYSTEM_INFO info; + + GetSystemInfo(&info); + if (info.dwAllocationGranularity > m_DefLumpSize) + { + m_DefLumpSize = info.dwAllocationGranularity; + } +#endif + } + + ~KeLumpAllocator() + { + FreeRegionChain(m_pUsableRegions); + FreeRegionChain(m_pUnusableRegions); + } + + void Reset() + { + KeLumpRegion *region; + + /* Find the tail of the usable regions. */ + region = m_pUsableRegions; + while (region != NULL) + { + if (region->next == NULL) + { + break; + } + } + + /* Link the unusable chain into the usable chain. */ + if (region == NULL) + { + m_pUsableRegions = m_pUnusableRegions; + } + else + { + region->next = m_pUnusableRegions; + m_pUnusableRegions->prev = region; + } + m_pUnusableRegions = NULL; + + region = m_pUsableRegions; + while (region != NULL) + { + region->avail = region->size; + region->cur = region->base; + region = region->next; + } + } + + void FreeRegionChain(KeLumpRegion *region) + { + KeLumpRegion *next; + + while (region != NULL) + { + next = region->next; + +#if defined KE_PLATFORM_WINDOWS + VirtualFree(region, 0, MEM_RELEASE); +#endif + region = next; + } + } + + void *Alloc(size_t size) + { + char *blob; + KeLumpRegion *region; + + if (size % 8 != 0) + { + size += 8; + size -= size % 8; + } + + region = FindRegionForSize(size); + + blob = region->cur; + region->avail -= size; + region->cur += size; + + /** + * Technically we could make one last small allocation, but + * this edge case is not worth the extra work. + */ + if (region->avail < 8) + { + /* Unlink us from the usable list */ + if (region == m_pUsableRegions) + { + m_pUsableRegions = m_pUsableRegions->next; + m_pUsableRegions->prev = NULL; + } + else + { + region->prev->next = region->next; + if (region->next != NULL) + { + region->next->prev = region->prev; + } + } + + /* Link us into the unusable list */ + region->prev = NULL; + region->next = m_pUnusableRegions; + + if (m_pUnusableRegions != NULL) + { + m_pUnusableRegions->prev = region; + } + + m_pUnusableRegions = region; + } + + return blob; + } + +private: + KeLumpRegion *FindRegionForSize(size_t size) + { + char *base; + KeLumpRegion *region; + size_t size_of_region; + + /** + * :TODO: replace this with a priority queue or something + * that's actually fast. Even worse is we dirty pages by + * doing this. Ouch! + */ + region = m_pUsableRegions; + while (region != NULL) + { + if (region->avail >= size) + { + return region; + } + region = region->next; + } + + /* Make sure regions end at 8-byte alignment. */ + size_of_region = sizeof(KeLumpRegion); + if (size_of_region % 8 != 0) + { + size_of_region += 8; + size_of_region -= size_of_region % 8; + } + + /* If the size is too big, fix that. */ + if (size > m_DefLumpSize - size_of_region) + { + size += m_DefLumpSize; + size -= size % m_DefLumpSize; + } + else + { + size = m_DefLumpSize; + } + +#if defined KE_PLATFORM_WINDOWS + base = (char *)VirtualAlloc( + NULL, + m_DefLumpSize, + MEM_COMMIT|MEM_RESERVE, + PAGE_READWRITE); +#endif + + /* Initialize the region */ + region = (KeLumpRegion *)base; + region->base = &base[size_of_region]; + region->size = size - size_of_region; + region->cur = region->base; + region->avail = region->size; + region->prev = NULL; + region->next = m_pUsableRegions; + + if (m_pUsableRegions != NULL) + { + m_pUsableRegions->prev = region; + } + + m_pUsableRegions = region; + + return region; + } + +private: + KeLumpRegion *m_pUsableRegions; + KeLumpRegion *m_pUnusableRegions; + size_t m_DefLumpSize; +}; + +inline KeLumpAllocator *ke_LumpFromAllocator(ke_allocator_t *arena) +{ + return (KeLumpAllocator *)arena->user; +} + +void *ke_LumpAlloc(ke_allocator_t *arena, size_t size) +{ + return ke_LumpFromAllocator(arena)->Alloc(size); +} + +void ke_LumpFree(ke_allocator_t *arena, void *ptr) +{ +} + +ke_allocator_t * KE_LINK Knight::KE_CreateLumpAllocator() +{ + ke_allocator_t *alloc; + + alloc = new ke_allocator_t; + alloc->alloc = ke_LumpAlloc; + alloc->dealloc = ke_LumpFree; + alloc->user = new KeLumpAllocator(); + + return alloc; +} + +void KE_LINK Knight::KE_DestroyLumpAllocator(ke_allocator_t *alloc) +{ + delete ke_LumpFromAllocator(alloc); + delete alloc; +} + +void KE_LINK Knight::KE_ResetLumpAllocator(ke_allocator_t *alloc) +{ + ke_LumpFromAllocator(alloc)->Reset(); +} diff --git a/knight/shared/KeLumpAllocator.h b/knight/shared/KeLumpAllocator.h new file mode 100644 index 00000000..96660dc7 --- /dev/null +++ b/knight/shared/KeLumpAllocator.h @@ -0,0 +1,36 @@ +#ifndef _INCLUDE_KNIGHT_KE_LUMP_ALLOCATOR_H_ +#define _INCLUDE_KNIGHT_KE_LUMP_ALLOCATOR_H_ + +#include +#include + +namespace Knight +{ + /** + * @brief Creates a new lump allocator. + * + * The lump allocator is intended for cases where there are many allocations + * and none need to be freed. There is memory wastage, and the lump allocator + * is typically thrown away after use. + * + * @return New lump allocator. + */ + extern ke_allocator_t * KE_CreateLumpAllocator(); + + /** + * @brief Destroys a lump allocator, freeing all of its resources. + * + * @param lump Lump allocator. + */ + extern void KE_DestroyLumpAllocator(ke_allocator_t *alloc); + + /** + * @brief Clears a lump allocator, so its memory can be re-used from + * the start. + * + * @param lump Lump allocator. + */ + extern void KE_ResetLumpAllocator(ke_allocator_t *alloc); +} + +#endif //_INCLUDE_KNIGHT_KE_LUMP_ALLOCATOR_H_ diff --git a/knight/shared/KePageAllocator.cpp b/knight/shared/KePageAllocator.cpp new file mode 100644 index 00000000..71d62a22 --- /dev/null +++ b/knight/shared/KePageAllocator.cpp @@ -0,0 +1,148 @@ +#include "KePageAllocator.h" + +using namespace Knight; + +struct PageInfo +{ + PageInfo *next; + void *base; +}; + +class Knight::KePageAllocator +{ +public: + size_t page_size; + size_t page_granularity; + PageInfo *free_pages; + PageInfo *page_blocks; +}; + +static void *ke_LumpPageAlloc(KePageAllocator *alloc) +{ + void *base; + char *page; + PageInfo *lump; + size_t pagesInBlock; + +#if defined KE_PLATFORM_WINDOWS + base = VirtualAlloc( + NULL, + alloc->page_granularity, + MEM_COMMIT|MEM_RESERVE, + PAGE_READWRITE); +#elif defined KE_PLATFORM_POSIX + base = valloc(alloc->page_granularity); +#else +#error "Unsupported platform" +#endif + + if (base == NULL) + { + return NULL; + } + + lump = new PageInfo; + lump->base = base; + lump->next = alloc->page_blocks; + alloc->page_blocks = lump->next; + + page = (char *)base + alloc->page_size; + pagesInBlock = alloc->page_granularity / alloc->page_size; + + for (size_t i = 1; i < pagesInBlock; i++) + { + lump = new PageInfo; + lump->base = page; + lump->next = alloc->free_pages; + alloc->free_pages = lump; + page += alloc->page_size; + } + + return base; +} + +KePageAllocator *Knight::KE_CreatePageAllocator() +{ + KePageAllocator *alloc; + + alloc = new KePageAllocator; + +#if defined KE_PLATFORM_WINDOWS + SYSTEM_INFO info; + + GetSystemInfo(&info); + alloc->page_size = info.dwPageSize; + alloc->page_granularity = info.dwAllocationGranularity; +#elif defined KE_PLATFORM_POSIX + alloc->page_size = sysconf(_SC_PAGESIZE); + alloc->page_granularity = alloc->page_size * 16; +#else +#error "Unsupported platform" +#endif + + alloc->free_pages = NULL; + alloc->page_blocks = NULL; + + return alloc; +} + +void Knight::KE_DestroyPageAllocator(KePageAllocator *alloc) +{ + PageInfo *info, *next; + + info = alloc->page_blocks; + while (info != NULL) + { + next = info->next; +#if defined KE_PLATFORM_WINDOWS + VirtualFree(info->base, 0, MEM_RELEASE); +#elif defined KE_PLATFORM_WINDOWS + free(info->base); +#else +#error "Unsupported platform" +#endif + delete info; + next = info; + } + + info = alloc->free_pages; + while (info != NULL) + { + next = info->next; + delete info; + info = next; + } +} + +void *Knight::KE_PageAlloc(KePageAllocator *alloc) +{ + if (alloc->free_pages != NULL) + { + void *base; + PageInfo *info; + + info = alloc->free_pages; + alloc->free_pages = info->next; + base = info->base; + delete info; + + return base; + } + + return ke_LumpPageAlloc(alloc); +} + +void Knight::KE_PageFree(KePageAllocator *alloc, void *page) +{ + PageInfo *info; + + info = new PageInfo; + info->base = page; + info->next = alloc->free_pages; + alloc->free_pages = info->next; +} + +size_t Knight::KE_PageSize(KePageAllocator *alloc) +{ + return alloc->page_size; +} diff --git a/knight/shared/KePageAllocator.h b/knight/shared/KePageAllocator.h new file mode 100644 index 00000000..4e69cdbf --- /dev/null +++ b/knight/shared/KePageAllocator.h @@ -0,0 +1,49 @@ +#ifndef _INCLUDE_KNIGHT_KE_PAGE_ALLOCATOR_H_ +#define _INCLUDE_KNIGHT_KE_PAGE_ALLOCATOR_H_ + +#include + +namespace Knight +{ + class KePageAllocator; + + /** + * @brief Creates a page allocator. + * + * @return New page allocator. + */ + extern KePageAllocator *KE_CreatePageAllocator(); + + /** + * @brief Destroys a page allocator, freeing all live pages it owns. + * + * @param Page allocator. + */ + extern void KE_DestroyPageAllocator(KePageAllocator *alloc); + + /** + * @brief Allocates a page of memory. + * + * @param alloc Page allocator. + * @return Page of memory. + */ + extern void *KE_PageAlloc(KePageAllocator *alloc); + + /** + * @brief Frees a page of memory. + * + * @param alloc Page allocator. + * @param page Page of memory. + */ + extern void KE_PageFree(KePageAllocator *alloc, void *page); + + /** + * @brief Returns the size of a page. + * + * @param alloc Page allocator. + * @return Page size. + */ + extern size_t KE_PageSize(KePageAllocator *alloc); +} + +#endif //_INCLUDE_KNIGHT_KE_PAGE_ALLOCATOR_H_ diff --git a/knight/shared/KePlatform.h b/knight/shared/KePlatform.h new file mode 100644 index 00000000..775355cb --- /dev/null +++ b/knight/shared/KePlatform.h @@ -0,0 +1,27 @@ +#ifndef _INCLUDE_KNIGHT_KE_PLATFORM_H_ +#define _INCLUDE_KNIGHT_KE_PLATFORM_H_ + +#if defined WIN32 + +#define KE_PLATFORM_WINDOWS +#define WINDOWS_LEAN_AND_MEAN +#include +#include + +#if !defined alloca +#define alloca _alloca +#endif + +#else + +#define KE_PLATFORM_POSIX + +#if defined linux +#define KE_PLATFORM_LINUX +#else +#error "TODO" +#endif + +#endif + +#endif //_INCLUDE_KNIGHT_KE_PLATFORM_H_ diff --git a/knight/shared/KeSectorStack.h b/knight/shared/KeSectorStack.h new file mode 100644 index 00000000..c18be942 --- /dev/null +++ b/knight/shared/KeSectorStack.h @@ -0,0 +1,162 @@ +#ifndef _INCLUDE_KNIGHT_KE_SECTOR_STACK_H_ +#define _INCLUDE_KNIGHT_KE_SECTOR_STACK_H_ + +#include +#include + +namespace Knight +{ + template + class KeSectorStack + { + public: + static const size_t DEFAULT_SECTOR_SIZE = 64; + + KeSectorStack() : m_SectorSize(DEFAULT_SECTOR_SIZE), m_UsedSize(0), m_MaxUsedSize(0) + { + m_pAlloc = NULL; + } + + KeSectorStack(size_t sectorSize) : m_SectorSize(sectorSize), m_UsedSize(0), m_MaxUsedSize(0) + { + m_pAlloc = NULL; + } + + KeSectorStack(size_t sectorSize, ke_allocator_t *alloc) : + m_SectorSize(sectorSize), m_UsedSize(0), m_pAlloc(alloc), m_MaxUsedSize(0) + { + } + + ~KeSectorStack() + { + clear(); + } + + void clear() + { + T *sector; + size_t last_sector; + size_t last_sector_item; + + if (m_MaxUsedSize == 0) + { + return; + } + + last_sector = (m_MaxUsedSize - 1) / m_SectorSize; + last_sector_item = (m_MaxUsedSize - 1) % m_SectorSize; + + for (size_t i = 0; i < last_sector; i++) + { + sector = m_Sectors[i]; + + for (size_t j = 0; j < m_SectorSize; j++) + { + sector[j].~T(); + } + } + + sector = m_Sectors[last_sector]; + for (size_t i = 0; i <= last_sector_item; i++) + { + sector[i].~T(); + } + + clear_no_dtors(); + } + + void clear_no_dtors() + { + for (size_t i = 0; i < m_Sectors.size(); i++) + { + free_sector(m_Sectors[i]); + } + + m_Sectors.clear(); + } + + bool empty() + { + return (m_UsedSize == 0) ? true : false; + } + + void push(const T & val) + { + if ((m_UsedSize / m_SectorSize) >= m_Sectors.size()) + { + /* Create a new sector */ + T * sector; + + if (m_pAlloc == NULL) + { + sector = (T *)malloc(sizeof(T) * m_SectorSize); + } + else + { + sector = (T *)m_pAlloc->alloc(m_pAlloc, sizeof(T) * m_SectorSize); + } + + m_Sectors.push_back(sector); + } + + at(m_UsedSize) = val; + + m_UsedSize++; + + /* Keep track of the maximum used size so we can defer the + * massive destruction job until the end. + */ + if (m_UsedSize > m_MaxUsedSize) + { + m_MaxUsedSize = m_UsedSize; + } + } + + void pop() + { + m_UsedSize--; + } + + void pop_all() + { + m_UsedSize = 0; + } + + T & front() + { + return at(m_UsedSize - 1); + } + + size_t size() + { + return m_UsedSize; + } + + private: + T & at(size_t x) + { + return m_Sectors[x / m_SectorSize][x % m_SectorSize]; + } + + void free_sector(T * sector) + { + if (m_pAlloc == NULL) + { + free(sector); + } + else if (m_pAlloc->dealloc != NULL) + { + m_pAlloc->dealloc(m_pAlloc, sector); + } + } + + private: + KeVector m_Sectors; + size_t m_SectorSize; + size_t m_UsedSize; + ke_allocator_t *m_pAlloc; + size_t m_MaxUsedSize; + }; +} + +#endif //_INCLUDE_KNIGHT_KE_SECTOR_STACK_H_ diff --git a/knight/shared/KeStdTypes.h b/knight/shared/KeStdTypes.h new file mode 100644 index 00000000..c78d9be2 --- /dev/null +++ b/knight/shared/KeStdTypes.h @@ -0,0 +1,24 @@ +#ifndef _INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_ +#define _INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_ + +#include +#include + +#if defined KE_PLATFORM_WINDOWS + +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else + +#include + +#endif + +#endif //_INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_ diff --git a/knight/shared/KeVector.h b/knight/shared/KeVector.h new file mode 100644 index 00000000..3564c156 --- /dev/null +++ b/knight/shared/KeVector.h @@ -0,0 +1,158 @@ +#ifndef _INCLUDE_KNIGHT_KE_VECTOR_H_ +#define _INCLUDE_KNIGHT_KE_VECTOR_H_ + +#include + +namespace Knight +{ + template + class KeVector + { + public: + KeVector() : m_Data(NULL), m_Size(0), m_CurrentUsedSize(0) + { + } + + KeVector(const KeVector & other) + { + m_Size = other.m_CurrentUsedSize; + m_CurrentUsedSize = other.m_CurrentUsedSize; + + if (m_Size > 0) + { + m_Data = new T[other.m_CurrentUsedSize]; + + for (size_t i = 0; i < m_Size; i++) + { + m_Data[i] = other.m_Data[i]; + } + } + else + { + m_Data = NULL; + } + } + + ~KeVector() + { + clear(); + } + + KeVector & operator =(const KeVector & other) + { + clear(); + + if (other.m_CurrentUsedSize) + { + m_Data = new T[other.m_CurrentUsedSize]; + m_Size = other.m_CurrentUsedSize; + m_CurrentUsedSize = other.m_CurrentUsedSize; + + for (size_t i = 0; i < m_Size; i++) + { + m_Data[i] = other.m_Data[i]; + } + } + } + + size_t size() const + { + return m_CurrentUsedSize; + } + + void push_back(const T & elem) + { + GrowIfNeeded(1); + + new (&m_Data[m_CurrentUsedSize]) T(elem); + + m_CurrentUsedSize++; + } + + void pop_back(const T & elem) + { + if (m_CurrentUsedSize == 0) + { + return; + } + + m_CurrentUsedSize--; + m_Data[m_CurrentUsedSize].~T(); + } + + bool is_empty() + { + return (m_CurrentUsedSize == 0); + } + + T & operator [](size_t pos) + { + return m_Data[pos]; + } + + const T & operator [](size_t pos) const + { + return m_Data[pos]; + } + + void clear() + { + for (size_t i = 0; i < m_CurrentUsedSize; i++) + { + m_Data[i].~T(); + } + + free(m_Data); + + m_Data = NULL; + m_Size = 0; + m_CurrentUsedSize = 0; + } + private: + void Grow(size_t amount) + { + T *new_data; + size_t new_size; + + if (m_Size == 0) + { + new_size = 8; + } + else + { + new_size = m_Size * 2; + } + + while (m_CurrentUsedSize + amount > new_size) + { + new_size *= 2; + } + + new_data = (T *)malloc(sizeof(T) * new_size); + + for (size_t i = 0; i < m_CurrentUsedSize; i++) + { + new (&new_data[i]) T(m_Data[i]); + m_Data[i].~T(); + } + + free(m_Data); + m_Data = new_data; + m_Size = new_size; + } + + void GrowIfNeeded(size_t amount) + { + if (m_CurrentUsedSize + amount >= m_Size) + { + Grow(amount); + } + } + private: + T *m_Data; + size_t m_Size; + size_t m_CurrentUsedSize; + }; +} + +#endif //_INCLUDE_KNIGHT_KE_VECTOR_H_ diff --git a/knight/shared/KnightAllocator.h b/knight/shared/KnightAllocator.h new file mode 100644 index 00000000..9dacf582 --- /dev/null +++ b/knight/shared/KnightAllocator.h @@ -0,0 +1,37 @@ +#ifndef _INCLUDE_KNIGHT_ALLOCATOR_H_ +#define _INCLUDE_KNIGHT_ALLOCATOR_H_ + +#include +#include + +struct ke_allocator_s; +typedef struct ke_allocator_s ke_allocator_t; + +typedef void *(*KEFN_ALLOCATOR)(ke_allocator_t *, size_t); +typedef void (*KEFN_DEALLOCATOR)(ke_allocator_t *, void *); + +struct ke_allocator_s +{ + KEFN_ALLOCATOR alloc; + KEFN_DEALLOCATOR dealloc; + void *user; +}; + +inline void *operator new(size_t size, ke_allocator_t *alloc) +{ + return alloc->alloc(alloc, size); +} + +inline void *operator new [](size_t size, ke_allocator_t *alloc) +{ + return alloc->alloc(alloc, size); +} + +template +void ke_destroy(ke_allocator_t *alloc, T * data) +{ + data->~T(); + alloc->dealloc(alloc, data); +} + +#endif //_INCLUDE_KNIGHT_ALLOCATOR_H_ diff --git a/public/IPluginSys.h b/public/IPluginSys.h index 6c0d9d46..e683dec3 100644 --- a/public/IPluginSys.h +++ b/public/IPluginSys.h @@ -296,7 +296,7 @@ namespace SourceMod * @brief Attempts to load a plugin. * * @param path Path and filename of plugin, relative to plugins folder. - * @param debug Whether or not to default the plugin into debug mode. + * @param debug Deprecated, must be false. * @param type Lifetime of the plugin. * @param error Buffer to hold any error message. * @param maxlength Maximum length of error message buffer. diff --git a/public/jit/jit_helpers.h b/public/jit/jit_helpers.h index 01d97763..406bc872 100644 --- a/public/jit/jit_helpers.h +++ b/public/jit/jit_helpers.h @@ -65,6 +65,10 @@ public: inptr++; return val; } + inline cell_t peek_cell() + { + return *inptr; + } inline cell_t *read_cellptr() { cell_t *val = *(cell_t **)(inptr); diff --git a/public/jit/x86/x86_macros.h b/public/jit/x86/x86_macros.h index 30335224..8c371cd4 100644 --- a/public/jit/x86/x86_macros.h +++ b/public/jit/x86/x86_macros.h @@ -998,6 +998,38 @@ inline void IA32_Mov_Rm_Imm32_Disp32(JitWriter *jit, jit->write_int32(val); } +inline void IA32_Mov_Rm_Imm32_SIB(JitWriter *jit, + jit_uint8_t dest, + jit_int32_t val, + jit_int32_t disp, + jit_uint8_t index, + jit_uint8_t scale) +{ + jit->write_ubyte(IA32_MOV_RM_IMM32); + + if (disp >= SCHAR_MIN && disp <= SCHAR_MAX) + { + jit->write_ubyte(ia32_modrm(MOD_DISP8, 0, REG_SIB)); + } + else + { + jit->write_ubyte(ia32_modrm(MOD_DISP32, 0, REG_SIB)); + } + + jit->write_ubyte(ia32_sib(scale, index, dest)); + + if (disp >= SCHAR_MIN && disp <= SCHAR_MAX) + { + jit->write_byte((jit_int8_t)disp); + } + else + { + jit->write_int32(disp); + } + + jit->write_int32(val); +} + inline void IA32_Mov_RmEBP_Imm32_Disp_Reg(JitWriter *jit, jit_uint8_t dest_base, jit_uint8_t dest_index, @@ -1347,24 +1379,39 @@ inline void IA32_Write_Jump32_Abs(JitWriter *jit, jitoffs_t jmp, void *target) jit->outptr = oldptr; } -/* For writing and auto-calculating an absolute target */ -inline void IA32_Jump_Imm32_Abs(JitWriter *jit, jitoffs_t target) +/* For writing and auto-calculating a relative target */ +inline void IA32_Jump_Imm32_Rel(JitWriter *jit, jitoffs_t target) { - /* :TODO: this should work, but does it? */ jit->write_ubyte(IA32_JMP_IMM32); IA32_Write_Jump32(jit, jit->get_outputpos(), target); jit->outptr += 4; } -inline void IA32_Jump_Cond_Imm32_Abs(JitWriter *jit, jit_uint8_t cond, jitoffs_t target) +/* For writing and auto-calculating an absolute target */ +inline void IA32_Jump_Imm32_Abs(JitWriter *jit, void *target) +{ + jitoffs_t jmp; + + jmp = IA32_Jump_Imm32(jit, 0); + IA32_Write_Jump32_Abs(jit, jmp, target); +} + +inline void IA32_Jump_Cond_Imm32_Rel(JitWriter *jit, jit_uint8_t cond, jitoffs_t target) { - /* :TODO: this should work, but does it? */ jit->write_ubyte(IA32_JCC_IMM32_1); jit->write_ubyte(IA32_JCC_IMM32_2+cond); IA32_Write_Jump32(jit, jit->get_outputpos(), target); jit->outptr += 4; } +inline void IA32_Jump_Cond_Imm32_Abs(JitWriter *jit, jit_uint8_t cond, void *target) +{ + jit->write_ubyte(IA32_JCC_IMM32_1); + jit->write_ubyte(IA32_JCC_IMM32_2+cond); + IA32_Write_Jump32_Abs(jit, jit->get_outputpos(), target); + jit->outptr += 4; +} + inline void IA32_Send_Jump8_Here(JitWriter *jit, jitoffs_t jmp) { jitoffs_t curptr = jit->get_outputpos(); diff --git a/public/sourcepawn/sp_file_headers.h b/public/sourcepawn/sp_file_headers.h index 0ee3572f..5cf8e05f 100644 --- a/public/sourcepawn/sp_file_headers.h +++ b/public/sourcepawn/sp_file_headers.h @@ -98,6 +98,9 @@ typedef struct sp_file_hdr_s #define SP_FLAG_DEBUG (1<<0) /**< Debug information is present in the file */ +#define SP_CODEVERS_JIT1 9 /**< Code version for JIT1 */ +#define SP_CODEVERS_JIT2 10 /**< Code version for JIT2 */ + /** * @brief File-encoded format of the ".code" section. */ diff --git a/public/sourcepawn/sp_vm_api.h b/public/sourcepawn/sp_vm_api.h index 6de6f6ee..7938c12e 100644 --- a/public/sourcepawn/sp_vm_api.h +++ b/public/sourcepawn/sp_vm_api.h @@ -42,7 +42,7 @@ /** SourcePawn Engine API Version */ #define SOURCEPAWN_ENGINE_API_VERSION 4 -#define SOURCEPAWN_ENGINE2_API_VERSION 1 +#define SOURCEPAWN_ENGINE2_API_VERSION 2 #if !defined SOURCEMOD_BUILD #define SOURCEMOD_BUILD @@ -955,7 +955,21 @@ namespace SourcePawn class IDebugListener { public: + /** + * @brief Invoked on a context execution error. + * + * @param ctx Context. + * @param error Object holding error information and a backtrace. + */ virtual void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error) =0; + + /** + * @brief Called on debug spew. + * + * @param msg Message text. + * @param fmt Message formatting arguments (printf-style). + */ + virtual void OnDebugSpew(const char *msg, ...) =0; }; /** @@ -1225,6 +1239,19 @@ namespace SourcePawn * @return Error string, or NULL if not found. */ virtual const char *GetErrorString(int err) =0; + + /** + * @brief Initializes the SourcePawn engine. + * + * @return True on success, false if failed. + */ + virtual bool Initialize() =0; + + /** + * @brief Shuts down the SourcePawn engine. Only needs to be called if + * Initialize() succeeded. + */ + virtual void Shutdown() =0; }; }; diff --git a/public/sourcepawn/sp_vm_types.h b/public/sourcepawn/sp_vm_types.h index 32fdedee..c0c38d52 100644 --- a/public/sourcepawn/sp_vm_types.h +++ b/public/sourcepawn/sp_vm_types.h @@ -83,6 +83,8 @@ typedef uint32_t funcid_t; /**< Function index code */ #define SP_ERROR_NATIVE 23 /**< Error originates from a native */ #define SP_ERROR_NOT_RUNNABLE 24 /**< Function or plugin is not runnable */ #define SP_ERROR_ABORTED 25 /**< Function call was aborted */ +#define SP_ERROR_CODE_TOO_OLD 26 /**< Code is too old for this VM */ +#define SP_ERROR_CODE_TOO_NEW 27 /**< Code is too new for this VM */ //Hey you! Update the string table if you add to the end of me! */ /********************************************** @@ -201,7 +203,4 @@ typedef struct sp_debug_symbol_s sp_fdbg_symbol_t *sym; /**< Pointer to original symbol */ } sp_debug_symbol_t; -//#define SPFLAG_PLUGIN_DEBUG (1<<0) /**< plugin is in debug mode */ -//#define SPFLAG_PLUGIN_PAUSED (1<<1) /**< plugin is "paused" (blocked from executing) */ - #endif //_INCLUDE_SOURCEPAWN_VM_TYPES_H diff --git a/sourcepawn/jit/BaseRuntime.cpp b/sourcepawn/jit/BaseRuntime.cpp index 76aa9c5b..53d37f1e 100644 --- a/sourcepawn/jit/BaseRuntime.cpp +++ b/sourcepawn/jit/BaseRuntime.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "BaseRuntime.h" #include "sp_vm_engine.h" #include "x86/jit_x86.h" @@ -9,61 +10,225 @@ using namespace SourcePawn; -int GlobalDebugBreak(BaseContext *ctx, uint32_t frm, uint32_t cip) +BaseRuntime::BaseRuntime() : m_Debug(&m_plugin), m_pPlugin(&m_plugin), m_pCtx(NULL), +m_PubFuncs(NULL), m_PubJitFuncs(NULL), m_pCo(NULL), m_CompSerial(0) { - g_engine1.RunTracer(ctx, frm, cip); + memset(&m_plugin, 0, sizeof(m_plugin)); - return SP_ERROR_NONE; -} - -BaseRuntime::BaseRuntime(sp_plugin_t *pl) : m_Debug(pl), m_pPlugin(pl) -{ - m_pCtx = new BaseContext(this); - - if (m_pPlugin->info.publics_num > 0) - { - m_PubFuncs = new CFunction *[m_pPlugin->info.publics_num]; - memset(m_PubFuncs, 0, sizeof(CFunction *) * m_pPlugin->info.publics_num); - } - else - { - m_PubFuncs = NULL; - } - - m_pPlugin->dbreak = GlobalDebugBreak; - m_pPlugin->profiler = g_engine2.GetProfiler(); + m_FuncCache = NULL; + m_MaxFuncs = 0; + m_NumFuncs = 0; } BaseRuntime::~BaseRuntime() { - for (uint32_t i = 0; i < m_pPlugin->info.publics_num; i++) + for (uint32_t i = 0; i < m_pPlugin->num_publics; i++) { delete m_PubFuncs[i]; m_PubFuncs[i] = NULL; } delete [] m_PubFuncs; + delete [] m_PubJitFuncs; - ClearCompile(); + for (unsigned int i = 0; i < m_NumFuncs; i++) + { + delete m_FuncCache[i]; + } + free(m_FuncCache); + + delete m_pCtx; + delete m_pCo; free(m_pPlugin->base); delete [] m_pPlugin->memory; + delete [] m_pPlugin->publics; + delete [] m_pPlugin->pubvars; delete [] m_pPlugin->natives; - delete m_pPlugin; + free(m_pPlugin->name); } -void BaseRuntime::ClearCompile() +static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params) { - g_Jit1.FreeContextVars(m_pCtx->GetCtx()); - g_Jit1.FreePluginVars(m_pPlugin); + return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native"); +} + +int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base) +{ + int set_err; + char *nameptr; + uint8_t sectnum = 0; + sp_plugin_t *plugin = m_pPlugin; + sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t)); + + memset(plugin, 0, sizeof(sp_plugin_t)); + + plugin->base = base; + plugin->base_size = hdr->imagesize; + set_err = SP_ERROR_NONE; + + /* We have to read the name section first */ + for (sectnum = 0; sectnum < hdr->sections; sectnum++) + { + nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs); + if (strcmp(nameptr, ".names") == 0) + { + plugin->stringbase = (const char *)(base + secptr[sectnum].dataoffs); + break; + } + } + + sectnum = 0; + + /* Now read the rest of the sections */ + while (sectnum < hdr->sections) + { + nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs); + + if (!(plugin->pcode) && !strcmp(nameptr, ".code")) + { + sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs); + + if (cod->codeversion < SP_CODEVERS_JIT1) + { + return SP_ERROR_CODE_TOO_OLD; + } + else if (cod->codeversion > SP_CODEVERS_JIT2) + { + return SP_ERROR_CODE_TOO_NEW; + } + + plugin->pcode = base + secptr->dataoffs + cod->code; + plugin->pcode_size = cod->codesize; + plugin->flags = cod->flags; + plugin->pcode_version = cod->codeversion; + } + else if (!(plugin->data) && !strcmp(nameptr, ".data")) + { + sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs); + plugin->data = base + secptr->dataoffs + dat->data; + plugin->data_size = dat->datasize; + plugin->mem_size = dat->memsize; + plugin->memory = new uint8_t[plugin->mem_size]; + memcpy(plugin->memory, plugin->data, plugin->data_size); + } + else if ((plugin->publics == NULL) && !strcmp(nameptr, ".publics")) + { + sp_file_publics_t *publics; + + publics = (sp_file_publics_t *)(base + secptr->dataoffs); + plugin->num_publics = secptr->size / sizeof(sp_file_publics_t); + + if (plugin->num_publics > 0) + { + plugin->publics = new sp_public_t[plugin->num_publics]; + + for (uint32_t i = 0; i < plugin->num_publics; i++) + { + plugin->publics[i].code_offs = publics[i].address; + plugin->publics[i].funcid = (i << 1) | 1; + plugin->publics[i].name = plugin->stringbase + publics[i].name; + } + } + } + else if ((plugin->pubvars == NULL) && !strcmp(nameptr, ".pubvars")) + { + sp_file_pubvars_t *pubvars; + + pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs); + plugin->num_pubvars = secptr->size / sizeof(sp_file_pubvars_t); + + if (plugin->num_pubvars > 0) + { + plugin->pubvars = new sp_pubvar_t[plugin->num_pubvars]; + + for (uint32_t i = 0; i < plugin->num_pubvars; i++) + { + plugin->pubvars[i].name = plugin->stringbase + pubvars[i].name; + plugin->pubvars[i].offs = (cell_t *)(plugin->memory + pubvars[i].address); + } + } + } + else if ((plugin->natives == NULL) && !strcmp(nameptr, ".natives")) + { + sp_file_natives_t *natives; + + natives = (sp_file_natives_t *)(base + secptr->dataoffs); + plugin->num_natives = secptr->size / sizeof(sp_file_natives_t); + + if (plugin->num_natives > 0) + { + plugin->natives = new sp_native_t[plugin->num_natives]; + + for (uint32_t i = 0; i < plugin->num_natives; i++) + { + plugin->natives[i].flags = 0; + plugin->natives[i].pfn = InvalidNative; + plugin->natives[i].status = SP_NATIVE_UNBOUND; + plugin->natives[i].user = NULL; + plugin->natives[i].name = plugin->stringbase + natives[i].name; + } + } + } + else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files")) + { + plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs); + } + else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines")) + { + plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs); + } + else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols")) + { + plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs); + } + else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info")) + { + sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs); + plugin->debug.files_num = inf->num_files; + plugin->debug.lines_num = inf->num_lines; + plugin->debug.syms_num = inf->num_syms; + } + else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) + { + plugin->debug.stringbase = (const char *)(base + secptr->dataoffs); + } + + secptr++; + sectnum++; + } + + if (plugin->pcode == NULL || plugin->data == NULL) + { + return SP_ERROR_FILE_FORMAT; + } + + if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols))) + { + return SP_ERROR_FILE_FORMAT; + } + + if (m_pPlugin->num_publics > 0) + { + m_PubFuncs = new CFunction *[m_pPlugin->num_publics]; + memset(m_PubFuncs, 0, sizeof(CFunction *) * m_pPlugin->num_publics); + m_PubJitFuncs = new JitFunction *[m_pPlugin->num_publics]; + memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_pPlugin->num_publics); + } + + m_pPlugin->profiler = g_engine2.GetProfiler(); + m_pCtx = new BaseContext(this); + m_pCo = g_Jit.StartCompilation(this); + + return SP_ERROR_NONE; } int BaseRuntime::FindNativeByName(const char *name, uint32_t *index) { int high; - high = m_pPlugin->info.natives_num - 1; + high = m_pPlugin->num_natives - 1; - for (uint32_t i=0; iinfo.natives_num; i++) + for (uint32_t i=0; inum_natives; i++) { if (strcmp(m_pPlugin->natives[i].name, name) == 0) { @@ -80,7 +245,7 @@ int BaseRuntime::FindNativeByName(const char *name, uint32_t *index) int BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native) { - if (index >= m_pPlugin->info.natives_num) + if (index >= m_pPlugin->num_natives) { return SP_ERROR_INDEX; } @@ -96,7 +261,7 @@ int BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native) uint32_t BaseRuntime::GetNativesNum() { - return m_pPlugin->info.natives_num; + return m_pPlugin->num_natives; } int BaseRuntime::FindPublicByName(const char *name, uint32_t *index) @@ -104,7 +269,7 @@ int BaseRuntime::FindPublicByName(const char *name, uint32_t *index) int diff, high, low; uint32_t mid; - high = m_pPlugin->info.publics_num - 1; + high = m_pPlugin->num_publics - 1; low = 0; while (low <= high) @@ -130,7 +295,7 @@ int BaseRuntime::FindPublicByName(const char *name, uint32_t *index) int BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic) { - if (index >= m_pPlugin->info.publics_num) + if (index >= m_pPlugin->num_publics) { return SP_ERROR_INDEX; } @@ -145,12 +310,12 @@ int BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic) uint32_t BaseRuntime::GetPublicsNum() { - return m_pPlugin->info.publics_num; + return m_pPlugin->num_publics; } int BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar) { - if (index >= m_pPlugin->info.pubvars_num) + if (index >= m_pPlugin->num_pubvars) { return SP_ERROR_INDEX; } @@ -168,7 +333,7 @@ int BaseRuntime::FindPubvarByName(const char *name, uint32_t *index) int diff, high, low; uint32_t mid; - high = m_pPlugin->info.pubvars_num - 1; + high = m_pPlugin->num_pubvars - 1; low = 0; while (low <= high) @@ -182,9 +347,13 @@ int BaseRuntime::FindPubvarByName(const char *name, uint32_t *index) *index = mid; } return SP_ERROR_NONE; - } else if (diff < 0) { + } + else if (diff < 0) + { low = mid + 1; - } else { + } + else + { high = mid - 1; } } @@ -194,12 +363,12 @@ int BaseRuntime::FindPubvarByName(const char *name, uint32_t *index) int BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr) { - if (index >= m_pPlugin->info.pubvars_num) + if (index >= m_pPlugin->num_pubvars) { return SP_ERROR_INDEX; } - *local_addr = m_pPlugin->info.pubvars[index].address; + *local_addr = (uint8_t *)m_pPlugin->pubvars[index].offs - m_pPlugin->memory; *phys_addr = m_pPlugin->pubvars[index].offs; return SP_ERROR_NONE; @@ -207,7 +376,7 @@ int BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phy uint32_t BaseRuntime::GetPubVarsNum() { - return m_pPlugin->info.pubvars_num; + return m_pPlugin->num_pubvars; } IPluginContext *BaseRuntime::GetDefaultContext() @@ -217,35 +386,9 @@ IPluginContext *BaseRuntime::GetDefaultContext() IPluginDebugInfo *BaseRuntime::GetDebugInfo() { - if (!IsDebugging()) - { - return NULL; - } - return &m_Debug; } -void BaseRuntime::RefreshFunctionCache() -{ - if (m_PubFuncs != NULL) - { - sp_public_t *pub; - for (uint32_t i = 0; i < m_pPlugin->info.publics_num; i++) - { - if (m_PubFuncs[i] == NULL) - { - continue; - } - if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE) - { - continue; - } - m_PubFuncs[i]->Set(pub->code_offs, this, (i << 1) | 1, i); - } - } - -} - IPluginFunction *BaseRuntime::GetFunctionById(funcid_t func_id) { CFunction *pFunc = NULL; @@ -253,26 +396,18 @@ IPluginFunction *BaseRuntime::GetFunctionById(funcid_t func_id) if (func_id & 1) { func_id >>= 1; - if (func_id >= m_pPlugin->info.publics_num) + if (func_id >= m_pPlugin->num_publics) { return NULL; } pFunc = m_PubFuncs[func_id]; if (!pFunc) { - m_PubFuncs[func_id] = new CFunction(m_pPlugin->publics[func_id].code_offs, - this, + m_PubFuncs[func_id] = new CFunction(this, (func_id << 1) | 1, func_id); pFunc = m_PubFuncs[func_id]; } - else if (pFunc->IsInvalidated()) - { - pFunc->Set(m_pPlugin->publics[func_id].code_offs, - this, - (func_id << 1) | 1, - func_id); - } } return pFunc; @@ -294,30 +429,17 @@ IPluginFunction *BaseRuntime::GetFunctionByName(const char *public_name) GetPublicByIndex(index, &pub); if (pub) { - m_PubFuncs[index] = new CFunction(pub->code_offs, this, (index << 1) | 1, index); + m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index); } pFunc = m_PubFuncs[index]; } - else if (pFunc->IsInvalidated()) - { - sp_public_t *pub = NULL; - GetPublicByIndex(index, &pub); - if (pub) - { - pFunc->Set(pub->code_offs, this, (index << 1) | 1, index); - } - else - { - pFunc = NULL; - } - } return pFunc; } bool BaseRuntime::IsDebugging() { - return ((m_pPlugin->run_flags & SPFLAG_PLUGIN_DEBUG) == SPFLAG_PLUGIN_DEBUG); + return true; } void BaseRuntime::SetPauseState(bool paused) @@ -345,8 +467,6 @@ size_t BaseRuntime::GetMemUsage() mem += sizeof(sp_plugin_t); mem += sizeof(BaseContext); mem += m_pPlugin->base_size; - mem += m_pPlugin->jit_codesize; - mem += m_pPlugin->jit_memsize; return mem; } @@ -356,3 +476,49 @@ BaseContext *BaseRuntime::GetBaseContext() return m_pCtx; } +int BaseRuntime::ApplyCompilationOptions(ICompilation *co) +{ + if (co == NULL) + { + return SP_ERROR_NONE; + } + + m_pCo = g_Jit.ApplyOptions(m_pCo, co); + + return SP_ERROR_NONE; +} + +JitFunction *BaseRuntime::GetJittedFunction(uint32_t idx) +{ + assert(idx <= m_NumFuncs); + + if (idx == 0 || idx > m_NumFuncs) + { + return NULL; + } + + return m_FuncCache[idx - 1]; +} + +uint32_t BaseRuntime::AddJittedFunction(JitFunction *fn) +{ + if (m_NumFuncs + 1 > m_MaxFuncs) + { + if (m_MaxFuncs == 0) + { + m_MaxFuncs = 8; + } + else + { + m_MaxFuncs *= 2; + } + + m_FuncCache = (JitFunction **)realloc( + m_FuncCache, + sizeof(JitFunction *) * m_MaxFuncs); + } + + m_FuncCache[m_NumFuncs++] = fn; + + return m_NumFuncs; +} diff --git a/sourcepawn/jit/BaseRuntime.h b/sourcepawn/jit/BaseRuntime.h index 5ff75601..bcd797ae 100644 --- a/sourcepawn/jit/BaseRuntime.h +++ b/sourcepawn/jit/BaseRuntime.h @@ -6,6 +6,7 @@ #include "sp_vm_function.h" class BaseContext; +class JitFunction; class DebugInfo : public IPluginDebugInfo { @@ -23,9 +24,10 @@ private: class BaseRuntime : public SourcePawn::IPluginRuntime { public: - BaseRuntime(sp_plugin_t *pl); + BaseRuntime(); ~BaseRuntime(); public: + virtual int CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base); virtual bool IsDebugging(); virtual IPluginDebugInfo *GetDebugInfo(); virtual int FindNativeByName(const char *name, uint32_t *index); @@ -45,16 +47,23 @@ public: virtual void SetPauseState(bool paused); virtual bool IsPaused(); virtual size_t GetMemUsage(); + JitFunction *GetJittedFunction(uint32_t idx); + uint32_t AddJittedFunction(JitFunction *fn); public: BaseContext *GetBaseContext(); private: - void ClearCompile(); - void RefreshFunctionCache(); + sp_plugin_t m_plugin; + unsigned int m_NumFuncs; + unsigned int m_MaxFuncs; + JitFunction **m_FuncCache; public: DebugInfo m_Debug; sp_plugin_t *m_pPlugin; BaseContext *m_pCtx; CFunction **m_PubFuncs; + JitFunction **m_PubJitFuncs; + ICompilation *m_pCo; + unsigned int m_CompSerial; }; #endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_ diff --git a/sourcepawn/jit/Makefile b/sourcepawn/jit/Makefile index fdc1358b..51df620e 100644 --- a/sourcepawn/jit/Makefile +++ b/sourcepawn/jit/Makefile @@ -1,98 +1,110 @@ -# (C)2004-2008 SourceMod Development Team -# Makefile written by David "BAILOPAN" Anderson - -SMSDK = ../.. -SOURCEHOOK = ../../../sourcemm-1.6/sourcehook - -##################################### -### EDIT BELOW FOR OTHER PROJECTS ### -##################################### - -PROJECT = sourcepawn.jit.x86 - -OBJECTS = dll_exports.cpp \ - x86/jit_x86.cpp \ - x86/opcode_helpers.cpp \ - sp_vm_basecontext.cpp \ - sp_vm_engine.cpp \ - sp_vm_function.cpp \ - engine2.cpp \ - BaseRuntime.cpp \ - zlib/adler32.c \ - zlib/compress.c \ - zlib/crc32.c \ - zlib/deflate.c \ - zlib/gzio.c \ - zlib/infback.c \ - zlib/inffast.c \ - zlib/inflate.c \ - zlib/inftrees.c \ - zlib/trees.c \ - zlib/uncompr.c \ - zlib/zutil.c - -############################################## -### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### -############################################## - -C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing -C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3 -C_GCC4_FLAGS = -fvisibility=hidden -CPP_GCC4_FLAGS = -fvisibility-inlines-hidden -CPP = gcc-4.1 - -LINK = -static-libgcc - -INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \ - -I$(SMSDK)/public/sourcepawn -I$(SOURCEHOOK) - -CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ - -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \ - -m32 -CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti - -################################################ -### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### -################################################ - -ifeq "$(DEBUG)" "true" - BIN_DIR = Debug - CFLAGS += $(C_DEBUG_FLAGS) -else - BIN_DIR = Release - CFLAGS += $(C_OPT_FLAGS) -endif - -GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) -ifeq "$(GCC_VERSION)" "4" - CFLAGS += $(C_GCC4_FLAGS) - CPPFLAGS += $(CPP_GCC4_FLAGS) -endif - -BINARY = $(PROJECT).so - -OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) -OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o) - -$(BIN_DIR)/%.o: %.c - $(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< - -$(BIN_DIR)/%.o: %.cpp - $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< - -all: - mkdir -p $(BIN_DIR)/x86 - mkdir -p $(BIN_DIR)/zlib - $(MAKE) -f Makefile jit - -jit: $(OBJ_LINUX) - $(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) - -debug: - $(MAKE) -f Makefile all DEBUG=true - -default: all - -clean: - rm -rf $(BIN_DIR)/*.o - rm -rf $(BIN_DIR)/$(BINARY) +# (C)2004-2008 SourceMod Development Team +# Makefile written by David "BAILOPAN" Anderson + +SMSDK = ../.. +SOURCEHOOK = ../../../sourcemm-1.6/sourcehook + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = sourcepawn.jit.x86 + +OBJECTS = dll_exports.cpp \ + x86/jit_x86.cpp \ + x86/opcode_helpers.cpp \ + sp_vm_basecontext.cpp \ + sp_vm_engine.cpp \ + sp_vm_function.cpp \ + engine2.cpp \ + BaseRuntime.cpp \ + jit_function.cpp \ + zlib/adler32.c \ + zlib/compress.c \ + zlib/crc32.c \ + zlib/deflate.c \ + zlib/gzio.c \ + zlib/infback.c \ + zlib/inffast.c \ + zlib/inflate.c \ + zlib/inftrees.c \ + zlib/trees.c \ + zlib/uncompr.c \ + zlib/zutil.c \ + +OBJECTS += ../../knight/shared/KeCodeAllocator.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3 +C_GCC4_FLAGS = -fvisibility=hidden +CPP_GCC4_FLAGS = -fvisibility-inlines-hidden +CPP = gcc-4.1 + +LINK = -static-libgcc + +INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \ + -I$(SMSDK)/public/sourcepawn -I$(SOURCEHOOK) -I$(SMSDK)/knight/shared -Ix86 + +CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \ + -m32 -Wno-uninitialized -Werror +CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CFLAGS += $(C_GCC4_FLAGS) + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).so + +OBJ_LINUX := $(OBJECTS:../../knight/shared/%.cpp=$(BIN_DIR)/knight/%.o) +OBJ_LINUX := $(OBJ_LINUX:%.cpp=$(BIN_DIR)/%.o) +OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o) + +default: all + +$(BIN_DIR)/%.o: %.c + $(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +$(BIN_DIR)/knight/%.o: ../../knight/shared/%.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/x86 + mkdir -p $(BIN_DIR)/zlib + mkdir -p $(BIN_DIR)/knight + $(MAKE) -f Makefile jit + +jit: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile all DEBUG=true + +clean: + rm -rf $(BIN_DIR)/x86/*.o + rm -rf $(BIN_DIR)/zlib/*.o + rm -rf $(BIN_DIR)/knight/*.o + rm -rf $(BIN_DIR)/*.o + rm -rf $(BIN_DIR)/$(BINARY) + diff --git a/sourcepawn/jit/engine2.cpp b/sourcepawn/jit/engine2.cpp index 6684af81..eeae092e 100644 --- a/sourcepawn/jit/engine2.cpp +++ b/sourcepawn/jit/engine2.cpp @@ -15,113 +15,9 @@ SourcePawnEngine2::SourcePawnEngine2() m_Profiler = NULL; } -sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err) -{ - char *nameptr; - uint8_t sectnum = 0; - sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t)); - - memset(plugin, 0, sizeof(sp_plugin_t)); - - plugin->base = base; - - while (sectnum < hdr->sections) - { - nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs); - - if (!(plugin->pcode) && !strcmp(nameptr, ".code")) - { - sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs); - plugin->pcode = base + secptr->dataoffs + cod->code; - plugin->pcode_size = cod->codesize; - plugin->flags = cod->flags; - } - else if (!(plugin->data) && !strcmp(nameptr, ".data")) - { - sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs); - plugin->data = base + secptr->dataoffs + dat->data; - plugin->data_size = dat->datasize; - plugin->mem_size = dat->memsize; - plugin->memory = new uint8_t[plugin->mem_size]; - memcpy(plugin->memory, plugin->data, plugin->data_size); - } - else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics")) - { - plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t); - plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs); - } - else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars")) - { - plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t); - plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs); - } - else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives")) - { - plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t); - plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs); - } - else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names")) - { - plugin->info.stringbase = (const char *)(base + secptr->dataoffs); - } - else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files")) - { - plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs); - } - else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines")) - { - plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs); - } - else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols")) - { - plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs); - } - else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info")) - { - sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs); - plugin->debug.files_num = inf->num_files; - plugin->debug.lines_num = inf->num_lines; - plugin->debug.syms_num = inf->num_syms; - } - else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) - { - plugin->debug.stringbase = (const char *)(base + secptr->dataoffs); - } - - secptr++; - sectnum++; - } - - if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase)) - { - goto return_error; - } - - if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols))) - { - goto return_error; - } - - if (err) - { - *err = SP_ERROR_NONE; - } - - return plugin; - -return_error: - if (err) - { - *err = SP_ERROR_FILE_FORMAT; - } - - return NULL; -} - IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err) { sp_file_hdr_t hdr; - sp_plugin_t *plugin; uint8_t *base; int z_result; int error; @@ -141,7 +37,6 @@ IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file if (hdr.magic != SPFILE_MAGIC) { error = SP_ERROR_FILE_FORMAT; - fclose(fp); goto return_error; } @@ -168,7 +63,6 @@ IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file free(sectheader); free(uncompdata); error = SP_ERROR_DECOMPRESSOR; - fclose(fp); goto return_error; } @@ -190,40 +84,41 @@ IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file default: { error = SP_ERROR_DECOMPRESSOR; - fclose(fp); goto return_error; } } - fclose(fp); - plugin = new sp_plugin_t; - - memset(plugin, 0, sizeof(sp_plugin_t)); - - plugin->base_size = hdr.imagesize; - if (!_ReadPlugin(&hdr, base, plugin, err)) - { - delete plugin; - free(base); - return NULL; - } - - pRuntime = new BaseRuntime(plugin); - - if (co == NULL) - { - co = g_Jit1.StartCompilation(pRuntime); - } - - - *err = pRuntime->ApplyCompilationOptions(co); - if (*err != SP_ERROR_NONE) + pRuntime = new BaseRuntime(); + if ((error = pRuntime->CreateFromMemory(&hdr, base)) != SP_ERROR_NONE) { delete pRuntime; - return NULL; + goto return_error; } + size_t len; + + len = strlen(file); + for (size_t i = len - 1; i >= 0 && i < len; i--) + { + if (file[i] == '/' + #if defined WIN32 + || file[i] == '\\' + #endif + ) + { + pRuntime->m_pPlugin->name = strdup(&file[i+1]); + break; + } + } + + if (pRuntime->m_pPlugin->name == NULL) + { + pRuntime->m_pPlugin->name = strdup(file); + } + + pRuntime->ApplyCompilationOptions(co); + return pRuntime; return_error: @@ -234,12 +129,12 @@ return_error: SPVM_NATIVE_FUNC SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) { - return g_Jit1.CreateFakeNative(callback, pData); + return g_Jit.CreateFakeNative(callback, pData); } void SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func) { - g_Jit1.DestroyFakeNative(func); + g_Jit.DestroyFakeNative(func); } const char *SourcePawnEngine2::GetEngineName() @@ -274,10 +169,20 @@ unsigned int SourcePawnEngine2::GetAPIVersion() ICompilation *SourcePawnEngine2::StartCompilation() { - return g_Jit1.StartCompilation(); + return g_Jit.StartCompilation(); } const char *SourcePawnEngine2::GetErrorString(int err) { return g_engine1.GetErrorString(err); } + +bool SourcePawnEngine2::Initialize() +{ + return g_Jit.InitializeJIT(); +} + +void SourcePawnEngine2::Shutdown() +{ + g_Jit.ShutdownJIT(); +} diff --git a/sourcepawn/jit/engine2.h b/sourcepawn/jit/engine2.h index 09de29d5..6198df7a 100644 --- a/sourcepawn/jit/engine2.h +++ b/sourcepawn/jit/engine2.h @@ -23,6 +23,8 @@ namespace SourcePawn void SetProfiler(IProfiler *profiler); ICompilation *StartCompilation(); const char *GetErrorString(int err); + bool Initialize(); + void Shutdown(); public: IProfiler *GetProfiler(); private: diff --git a/sourcepawn/jit/jit_function.cpp b/sourcepawn/jit/jit_function.cpp new file mode 100644 index 00000000..d40b629d --- /dev/null +++ b/sourcepawn/jit/jit_function.cpp @@ -0,0 +1,23 @@ +#include "jit_function.h" +#include "sp_vm_engine.h" +#include "jit_x86.h" + +JitFunction::JitFunction(void *entry_addr, cell_t pcode_offs) +: m_pEntryAddr(entry_addr), m_PcodeOffs(pcode_offs) +{ +} + +JitFunction::~JitFunction() +{ + g_Jit.FreeCode(m_pEntryAddr); +} + +void *JitFunction::GetEntryAddress() +{ + return m_pEntryAddr; +} + +cell_t JitFunction::GetPCodeAddress() +{ + return m_PcodeOffs; +} diff --git a/sourcepawn/jit/jit_function.h b/sourcepawn/jit/jit_function.h new file mode 100644 index 00000000..d2630d38 --- /dev/null +++ b/sourcepawn/jit/jit_function.h @@ -0,0 +1,19 @@ +#ifndef _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_ +#define _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_ + +#include + +class JitFunction +{ +public: + JitFunction(void *entry_addr, cell_t pcode_offs); + ~JitFunction(); +public: + void *GetEntryAddress(); + cell_t GetPCodeAddress(); +private: + void *m_pEntryAddr; + cell_t m_PcodeOffs; +}; + +#endif //_INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_ diff --git a/sourcepawn/jit/jit_shared.h b/sourcepawn/jit/jit_shared.h index 11f4f573..14b32940 100644 --- a/sourcepawn/jit/jit_shared.h +++ b/sourcepawn/jit/jit_shared.h @@ -5,23 +5,11 @@ using namespace SourcePawn; -/** -* @brief Information about the core plugin tables. These may or may not be present! -*/ -typedef struct sp_plugin_infotab_s -{ - const char *stringbase; /**< base of string table */ - uint32_t publics_num; /**< number of publics */ - sp_file_publics_t *publics; /**< public table */ - uint32_t natives_num; /**< number of natives */ - sp_file_natives_t *natives; /**< native table */ - uint32_t pubvars_num; /**< number of pubvars */ - sp_file_pubvars_t *pubvars; /**< pubvars table */ -} sp_plugin_infotab_t; +#define SP_MAX_RETURN_STACK 1024 /** -* @brief Information about the plugin's debug tables. These are all present if one is present. -*/ + * @brief Information about the plugin's debug tables. These are all present if one is present. + */ typedef struct sp_plugin_debug_s { const char *stringbase; /**< base of string table */ @@ -36,12 +24,12 @@ typedef struct sp_plugin_debug_s class BaseContext; /** -* Breaks into a debugger -* Params: -* [0] - plugin context -* [1] - frm -* [2] - cip -*/ + * Breaks into a debugger + * Params: + * [0] - plugin context + * [1] - frm + * [2] - cip + */ typedef int (*SPVM_DEBUGBREAK)(BaseContext *, uint32_t, uint32_t); /** @@ -59,37 +47,40 @@ namespace SourcePawn uint32_t data_size; /**< Size of data */ uint32_t mem_size; /**< Required memory space */ uint16_t flags; /**< Code flags */ - sp_plugin_infotab_t info; /**< Base info table */ sp_plugin_debug_t debug; /**< Debug info table */ size_t base_size; /**< Size of the entire plugin base */ - void *codebase; /**< Base of generated code and memory */ - SPVM_DEBUGBREAK dbreak; /**< Debug break function */ uint8_t *memory; /**< Data chunk */ + const char *stringbase; /**< base of string table */ sp_public_t *publics; /**< Public functions table */ + uint32_t num_publics; /**< Number of publics. */ sp_pubvar_t *pubvars; /**< Public variables table */ + uint32_t num_pubvars; /**< Number of public variables */ sp_native_t *natives; /**< Natives table */ - sp_debug_file_t *files; /**< Files */ - sp_debug_line_t *lines; /**< Lines */ - sp_debug_symbol_t *symbols; /**< Symbols */ + uint32_t num_natives; /**< Number of natives */ IProfiler *profiler; /**< Pointer to IProfiler */ uint32_t prof_flags; /**< Profiling flags */ uint32_t run_flags; /**< Runtime flags */ - size_t jit_codesize; /**< JIT compiled codesize */ - size_t jit_memsize; /**< JIT additional memory */ + uint32_t pcode_version; /**< P-Code version number */ + char *name; /**< Plugin/script name */ } sp_plugin_t; } typedef struct sp_context_s { - cell_t hp; /**< Heap pointer */ - cell_t sp; /**< Stack pointer */ - cell_t frm; /**< Frame pointer */ - int32_t n_err; /**< Error code set by a native */ - uint32_t n_idx; /**< Current native index being executed */ - void * vm[8]; /**< VM-specific pointers */ + cell_t hp; /**< Heap pointer */ + cell_t sp; /**< Stack pointer */ + cell_t frm; /**< Frame pointer */ + int32_t err_cip; /**< Code pointer last error occurred in */ + int32_t n_err; /**< Error code set by a native */ + uint32_t n_idx; /**< Current native index being executed */ + void * vm[8]; /**< VM-specific pointers */ + cell_t rp; /**< Return stack pointer */ + cell_t rstk_cips[SP_MAX_RETURN_STACK]; } sp_context_t; -#define SPFLAG_PLUGIN_DEBUG (1<<0) +//#define SPFLAG_PLUGIN_DEBUG (1<<0) #define SPFLAG_PLUGIN_PAUSED (1<<1) +#define INVALID_CIP 0xFFFFFFFF + #endif //_INCLUDE_SOURCEPAWN_JIT_SHARED_H_ diff --git a/sourcepawn/jit/jit_version.h b/sourcepawn/jit/jit_version.h index 54e7f13a..ae87784f 100644 --- a/sourcepawn/jit/jit_version.h +++ b/sourcepawn/jit/jit_version.h @@ -32,7 +32,7 @@ #ifndef _INCLUDE_JIT_VERSION_H_ #define _INCLUDE_JIT_VERSION_H_ -#define SVN_FULL_VERSION "1.1.0-svn" -#define SVN_FILE_VERSION 1,1,0,2218 +#define SVN_FULL_VERSION "1.2.0-svn" +#define SVN_FILE_VERSION 1,2,0,2218 #endif //_INCLUDE_JIT_VERSION_H_ diff --git a/sourcepawn/jit/sp_vm_basecontext.cpp b/sourcepawn/jit/sp_vm_basecontext.cpp index 2b282488..929c08cc 100644 --- a/sourcepawn/jit/sp_vm_basecontext.cpp +++ b/sourcepawn/jit/sp_vm_basecontext.cpp @@ -51,12 +51,43 @@ BaseContext::BaseContext(BaseRuntime *pRuntime) m_InExec = false; m_CustomMsg = false; - m_pNullVec = NULL; - m_pNullString = NULL; + /* Initialize the null references */ + uint32_t index; + if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE) + { + sp_pubvar_t *pubvar; + GetPubvarByIndex(index, &pubvar); + m_pNullVec = pubvar->offs; + } + else + { + m_pNullVec = NULL; + } + + if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE) + { + sp_pubvar_t *pubvar; + GetPubvarByIndex(index, &pubvar); + m_pNullString = pubvar->offs; + } + else + { + m_pNullString = NULL; + } + + m_ctx.hp = m_pPlugin->data_size; + m_ctx.sp = m_pPlugin->mem_size - sizeof(cell_t); + m_ctx.frm = m_ctx.sp; + m_ctx.n_err = SP_ERROR_NONE; + m_ctx.n_idx = SP_ERROR_NONE; + m_ctx.rp = 0; + + g_Jit.SetupContextVars(m_pRuntime, this, &m_ctx); } BaseContext::~BaseContext() { + g_Jit.FreeContextVars(&m_ctx); } IVirtualMachine *BaseContext::GetVirtualMachine() @@ -76,7 +107,7 @@ sp_context_t *BaseContext::GetCtx() bool BaseContext::IsDebugging() { - return m_pRuntime->IsDebugging(); + return true; } int BaseContext::SetDebugBreak(void *newpfn, void *oldpfn) @@ -517,15 +548,15 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig int serial; cell_t *sp; funcid_t fnid; + JitFunction *fn; sp_public_t *pubfunc; cell_t _ignore_result; + unsigned int public_id; fnid = function->GetFunctionID(); if (fnid & 1) - { - unsigned int public_id; - + { public_id = fnid >> 1; if (m_pRuntime->GetPublicByIndex(public_id, &pubfunc) != SP_ERROR_NONE) @@ -560,16 +591,40 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig serial = m_pPlugin->profiler->OnCallbackBegin(this, pubfunc); } + /* See if we have to compile the callee. */ + if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL) + { + uint32_t func_idx; + + /* We might not have to - check pcode offset. */ + if ((func_idx = FuncLookup((CompData *)m_pRuntime->m_pCo, pubfunc->code_offs)) != 0) + { + fn = m_pRuntime->GetJittedFunction(func_idx); + assert(fn != NULL); + m_pRuntime->m_PubJitFuncs[public_id] = fn; + } + else + { + if ((fn = g_Jit.CompileFunction(m_pRuntime, pubfunc->code_offs, &ir)) == NULL) + { + return ir; + } + m_pRuntime->m_PubJitFuncs[public_id] = fn; + } + } + /* Save our previous state. */ bool save_exec; uint32_t save_n_idx; - cell_t save_sp, save_hp; + cell_t save_sp, save_hp, save_rp, save_cip; save_sp = m_ctx.sp; save_hp = m_ctx.hp; save_exec = m_InExec; save_n_idx = m_ctx.n_idx; + save_rp = m_ctx.rp; + save_cip = m_ctx.err_cip; /* Push parameters */ @@ -589,15 +644,11 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig m_CustomMsg = false; m_InExec = true; - /* Start the tracer */ + /* Start the frame tracer */ - g_engine1.PushTracer(this); + ir = g_Jit.InvokeFunction(m_pRuntime, fn, result); - /* Execute the function */ - - ir = g_Jit1.ContextExecute(m_pPlugin, &m_ctx, pubfunc->code_offs, result); - - /* Restore some states, stop tracing */ + /* Restore some states, stop the frame tracer */ m_InExec = save_exec; @@ -618,18 +669,30 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig m_ctx.hp, save_hp); } + if (m_ctx.rp != save_rp) + { + ir = SP_ERROR_STACKLEAK; + _SetErrorMessage("Return stack leak detected: rp:%d should be %d!", + m_ctx.rp, + save_rp); + } + } + + if (ir != SP_ERROR_NONE) + { + g_engine1.ReportError(m_pRuntime, ir, m_MsgCache, save_rp); } m_ctx.sp = save_sp; m_ctx.hp = save_hp; - - g_engine1.PopTracer(ir, m_CustomMsg ? m_MsgCache : NULL); - + m_ctx.rp = save_rp; + if ((m_pPlugin->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS) { m_pPlugin->profiler->OnCallbackEnd(serial); } + m_ctx.err_cip = save_cip; m_ctx.n_idx = save_n_idx; m_ctx.n_err = SP_ERROR_NONE; m_MsgCache[0] = '\0'; @@ -643,23 +706,6 @@ IPluginRuntime *BaseContext::GetRuntime() return m_pRuntime; } -int BaseRuntime::ApplyCompilationOptions(ICompilation *co) -{ - int err; - - /* The JIT does not destroy anything until it is guaranteed to succeed. */ - if (!g_Jit1.Compile(co, this, &err)) - { - return err; - } - - RefreshFunctionCache(); - - m_pCtx->Refresh(); - - return SP_ERROR_NONE; -} - DebugInfo::DebugInfo(sp_plugin_t *plugin) : m_pPlugin(plugin) { } @@ -676,7 +722,7 @@ int DebugInfo::LookupFile(ucell_t addr, const char **filename) while (high - low > 1) { mid = USHR(low + high); - if (m_pPlugin->files[mid].addr <= addr) + if (m_pPlugin->debug.files[mid].addr <= addr) { low = mid; } else { @@ -689,33 +735,44 @@ int DebugInfo::LookupFile(ucell_t addr, const char **filename) return SP_ERROR_NOT_FOUND; } - *filename = m_pPlugin->files[low].name; + *filename = m_pPlugin->debug.stringbase + m_pPlugin->debug.files[low].name; return SP_ERROR_NONE; } int DebugInfo::LookupFunction(ucell_t addr, const char **name) { - uint32_t iter, max = m_pPlugin->debug.syms_num; + uint32_t max, iter; + sp_fdbg_symbol_t *sym; + sp_fdbg_arraydim_t *arr; + uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols); - for (iter=0; iterdebug.syms_num; + for (iter = 0; iter < max; iter++) { - if ((m_pPlugin->symbols[iter].sym->ident == SP_SYM_FUNCTION) - && (m_pPlugin->symbols[iter].codestart <= addr) - && (m_pPlugin->symbols[iter].codeend > addr)) + sym = (sp_fdbg_symbol_t *)cursor; + + if (sym->ident == SP_SYM_FUNCTION + && sym->codestart <= addr + && sym->codeend > addr) { - break; + *name = m_pPlugin->debug.stringbase + sym->name; + return SP_ERROR_NONE; } + + if (sym->dimcount > 0) + { + cursor += sizeof(sp_fdbg_symbol_t); + arr = (sp_fdbg_arraydim_t *)cursor; + cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount; + continue; + } + + cursor += sizeof(sp_fdbg_symbol_t); } - if (iter >= max) - { - return SP_ERROR_NOT_FOUND; - } + return SP_ERROR_NOT_FOUND; - *name = m_pPlugin->symbols[iter].name; - - return SP_ERROR_NONE; } int DebugInfo::LookupLine(ucell_t addr, uint32_t *line) @@ -728,7 +785,7 @@ int DebugInfo::LookupLine(ucell_t addr, uint32_t *line) while (high - low > 1) { mid = USHR(low + high); - if (m_pPlugin->lines[mid].addr <= addr) + if (m_pPlugin->debug.lines[mid].addr <= addr) { low = mid; } else { @@ -742,7 +799,7 @@ int DebugInfo::LookupLine(ucell_t addr, uint32_t *line) } /* Since the CIP occurs BEFORE the line, we have to add one */ - *line = m_pPlugin->lines[low].line + 1; + *line = m_pPlugin->debug.lines[low].line + 1; return SP_ERROR_NONE; } @@ -781,30 +838,3 @@ bool BaseContext::GetKey(int k, void **value) return true; } - -void BaseContext::Refresh() -{ - /* Initialize the null references */ - uint32_t index; - if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE) - { - sp_pubvar_t *pubvar; - GetPubvarByIndex(index, &pubvar); - m_pNullVec = pubvar->offs; - } - else - { - m_pNullVec = NULL; - } - - if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE) - { - sp_pubvar_t *pubvar; - GetPubvarByIndex(index, &pubvar); - m_pNullString = pubvar->offs; - } - else - { - m_pNullString = NULL; - } -} diff --git a/sourcepawn/jit/sp_vm_engine.cpp b/sourcepawn/jit/sp_vm_engine.cpp index 158da04b..28235a94 100644 --- a/sourcepawn/jit/sp_vm_engine.cpp +++ b/sourcepawn/jit/sp_vm_engine.cpp @@ -30,23 +30,18 @@ */ #include "sp_vm_types.h" -#include -/* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */ -#if !defined SH_ASSERT - #define SH_ASSERT(x, info) - #include - #undef SH_ASSERT -#else - #include -#endif - #include #include #include +#include #include "sp_file_headers.h" #include "sp_vm_engine.h" #include "zlib/zlib.h" #include "sp_vm_basecontext.h" +#include "jit_x86.h" +#if defined __GNUC__ +#include +#endif SourcePawnEngine g_engine1; @@ -57,12 +52,8 @@ SourcePawnEngine g_engine1; #include #endif -#define INVALID_CIP 0xFFFFFFFF - using namespace SourcePawn; -SourceHook::CPageAlloc g_ExeMemory(16); - #define ERROR_MESSAGE_MAX 25 static const char *g_ErrorMsgTable[] = { @@ -92,6 +83,8 @@ static const char *g_ErrorMsgTable[] = "Native detected error", "Plugin not runnable", "Call was aborted", + "Plugin format is too old", + "Plugin format is too new", }; const char *SourcePawnEngine::GetErrorString(int error) @@ -107,33 +100,10 @@ const char *SourcePawnEngine::GetErrorString(int error) SourcePawnEngine::SourcePawnEngine() { m_pDebugHook = NULL; - m_CallStack = NULL; - m_FreedCalls = NULL; - m_CurChain = 0; -#if 0 - m_pFreeFuncs = NULL; -#endif } SourcePawnEngine::~SourcePawnEngine() { - TracedCall *pTemp; - while (m_FreedCalls) - { - pTemp = m_FreedCalls->next; - delete m_FreedCalls; - m_FreedCalls = pTemp; - } - -#if 0 - CFunction *pNext; - while (m_pFreeFuncs) - { - pNext = m_pFreeFuncs->m_pNext; - delete m_pFreeFuncs; - m_pFreeFuncs = pNext; - } -#endif } void *SourcePawnEngine::ExecAlloc(size_t size) @@ -153,22 +123,22 @@ void *SourcePawnEngine::ExecAlloc(size_t size) void *SourcePawnEngine::AllocatePageMemory(size_t size) { - return g_ExeMemory.Alloc(size); + return g_Jit.AllocCode(size); } void SourcePawnEngine::SetReadExecute(void *ptr) { - g_ExeMemory.SetRE(ptr); + /* already re */ } void SourcePawnEngine::SetReadWrite(void *ptr) { - g_ExeMemory.SetRW(ptr); + /* already rw */ } void SourcePawnEngine::FreePageMemory(void *ptr) { - g_ExeMemory.Free(ptr); + g_Jit.FreeCode(ptr); } void SourcePawnEngine::ExecFree(void *address) @@ -180,6 +150,12 @@ void SourcePawnEngine::ExecFree(void *address) #endif } +void SourcePawnEngine::SetReadWriteExecute(void *ptr) +{ +//:TODO: g_ExeMemory.SetRWE(ptr); + SetReadExecute(ptr); +} + void *SourcePawnEngine::BaseAlloc(size_t size) { return malloc(size); @@ -224,142 +200,38 @@ IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener) return old; } -unsigned int SourcePawnEngine::GetContextCallCount() -{ - if (!m_CallStack) - { - return 0; - } - - return m_CallStack->chain; -} - -TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain) -{ - TracedCall *pCall; - - if (!m_FreedCalls) - { - pCall = new TracedCall; - } else { - /* Unlink the head node from the free list */ - pCall = m_FreedCalls; - m_FreedCalls = m_FreedCalls->next; - } - - /* Link as the head node into the call stack */ - pCall->next = m_CallStack; - - if (new_chain) - { - pCall->chain = ++m_CurChain; - } else { - pCall->chain = m_CurChain; - } - - m_CallStack = pCall; - - return pCall; -} - -void SourcePawnEngine::FreeTracedCall(TracedCall *pCall) -{ - /* Check if this is the top of the call stack */ - if (pCall == m_CallStack) - { - m_CallStack = m_CallStack->next; - } - - /* Add this to our linked list of freed calls */ - if (!m_FreedCalls) - { - m_FreedCalls = pCall; - m_FreedCalls->next = NULL; - } else { - pCall->next = m_FreedCalls; - m_FreedCalls = pCall; - } -} - -void SourcePawnEngine::PushTracer(BaseContext *ctx) -{ - TracedCall *pCall = MakeTracedCall(true); - - pCall->cip = INVALID_CIP; - pCall->ctx = ctx; - pCall->frm = INVALID_CIP; -} - -void SourcePawnEngine::RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip) -{ - assert(m_CallStack != NULL); - assert(m_CallStack->ctx == ctx); - assert(m_CallStack->chain == m_CurChain); - - if (m_CallStack->cip == INVALID_CIP) - { - /* We aren't logging anything yet, so begin the trace */ - m_CallStack->cip = codeip; - m_CallStack->frm = frame; - } else { - if (m_CallStack->frm > frame) - { - /* The last frame has moved down the stack, - * so we have to push a new call onto our list. - */ - TracedCall *pCall = MakeTracedCall(false); - pCall->ctx = ctx; - pCall->frm = frame; - } else if (m_CallStack->frm < frame) { - /* The last frame has moved up the stack, - * so we have to pop the call from our list. - */ - FreeTracedCall(m_CallStack); - } - /* no matter where we are, update the cip */ - m_CallStack->cip = codeip; - } -} - -void SourcePawnEngine::PopTracer(int error, const char *msg) -{ - assert(m_CallStack != NULL); - - if (error != SP_ERROR_NONE && m_pDebugHook) - { - uint32_t native = INVALID_CIP; - - if (m_CallStack->ctx->GetCtx()->n_err) - { - native = m_CallStack->ctx->GetCtx()->n_idx; - } - - CContextTrace trace(m_CallStack, error, msg, native); - m_pDebugHook->OnContextExecuteError(m_CallStack->ctx, &trace); - } - - /* Now pop the error chain */ - while (m_CallStack && m_CallStack->chain == m_CurChain) - { - FreeTracedCall(m_CallStack); - } - - m_CurChain--; -} - unsigned int SourcePawnEngine::GetEngineAPIVersion() { return SOURCEPAWN_ENGINE_API_VERSION; } -CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) : - m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native) +unsigned int SourcePawnEngine::GetContextCallCount() { + return 0; +} + +void SourcePawnEngine::ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start) +{ + if (m_pDebugHook == NULL) + { + return; + } + + CContextTrace trace(runtime, err, errstr, rp_start); + + m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace); +} + +CContextTrace::CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) +: m_pRuntime(pRuntime), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0) +{ + m_ctx = pRuntime->m_pCtx->GetCtx(); + m_pDebug = m_pRuntime->GetDebugInfo(); } bool CContextTrace::DebugInfoAvailable() { - return m_pStart->ctx->IsDebugging(); + return (m_pDebug != NULL); } const char *CContextTrace::GetCustomErrorString() @@ -374,83 +246,99 @@ int CContextTrace::GetErrorCode() const char *CContextTrace::GetErrorString() { - if (m_Error > ERROR_MESSAGE_MAX || - m_Error < 1) + if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1) { return "Invalid error code"; - } else { + } + else + { return g_ErrorMsgTable[m_Error]; } } void CContextTrace::ResetTrace() { - m_pIterator = m_pStart; + m_Level = 0; } bool CContextTrace::GetTraceInfo(CallStackInfo *trace) { - if (!m_pIterator || (m_pIterator->chain != m_pStart->chain)) + cell_t cip; + + if (m_Level == 0) + { + cip = m_ctx->err_cip; + } + else if (m_ctx->rp > 0) + { + /* Entries go from ctx.rp - 1 to m_StartRp */ + cell_t offs, start, end; + + offs = m_Level - 1; + start = m_ctx->rp - 1; + end = m_StartRp; + + if (start - offs < end) + { + return false; + } + + cip = m_ctx->rstk_cips[start - offs]; + } + else { return false; } - if (m_pIterator->cip == INVALID_CIP) + if (trace == NULL) { - return false; - } - - IPluginContext *pContext = m_pIterator->ctx; - IPluginDebugInfo *pInfo = pContext->GetRuntime()->GetDebugInfo(); - - if (!pInfo) - { - return false; - } - - if (!trace) - { - m_pIterator = m_pIterator->next; + m_Level++; return true; } - if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE) + if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE) { trace->filename = NULL; } - if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE) + if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE) { trace->function = NULL; } - if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE) + if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE) { trace->line = 0; } - m_pIterator = m_pIterator->next; + m_Level++; return true; } const char *CContextTrace::GetLastNative(uint32_t *index) { - if (m_Native == INVALID_CIP) + if (m_ctx->n_err == SP_ERROR_NONE) { return NULL; } sp_native_t *native; - if (m_pIterator->ctx->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE) + if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE) { return NULL; } if (index) { - *index = m_Native; + *index = m_ctx->n_idx; } return native->name; } + +IDebugListener *SourcePawnEngine::GetDebugHook() +{ + return m_pDebugHook; +} + diff --git a/sourcepawn/jit/sp_vm_engine.h b/sourcepawn/jit/sp_vm_engine.h index 08e5c79a..486e83a6 100644 --- a/sourcepawn/jit/sp_vm_engine.h +++ b/sourcepawn/jit/sp_vm_engine.h @@ -37,19 +37,10 @@ class BaseContext; -struct TracedCall -{ - uint32_t cip; - uint32_t frm; - BaseContext *ctx; - TracedCall *next; - unsigned int chain; -}; - class CContextTrace : public IContextTrace { public: - CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native); + CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp); public: int GetErrorCode(); const char *GetErrorString(); @@ -59,11 +50,13 @@ public: void ResetTrace(); const char *GetLastNative(uint32_t *index); private: + BaseRuntime *m_pRuntime; + sp_context_t *m_ctx; int m_Error; const char *m_pMsg; - TracedCall *m_pStart; - TracedCall *m_pIterator; - uint32_t m_Native; + cell_t m_StartRp; + cell_t m_Level; + IPluginDebugInfo *m_pDebug; }; class SourcePawnEngine : public ISourcePawnEngine @@ -85,37 +78,16 @@ public: //ISourcePawnEngine void *AllocatePageMemory(size_t size); void SetReadWrite(void *ptr); void SetReadExecute(void *ptr); + void SetReadWriteExecute(void *ptr); void FreePageMemory(void *ptr); const char *GetErrorString(int err); -public: //Debugger Stuff - /** - * @brief Pushes a context onto the top of the call tracer. - * - * @param ctx Plugin context. - */ - void PushTracer(BaseContext *ctx); - - /** - * @brief Pops a plugin off the call tracer. - */ - void PopTracer(int error, const char *msg); - - /** - * @brief Runs tracer from a debug break. - */ - void RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip); + void ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start); public: //Plugin function stuff CFunction *GetFunctionFromPool(funcid_t f, IPluginContext *plugin); void ReleaseFunctionToPool(CFunction *func); -private: - TracedCall *MakeTracedCall(bool new_chain); - void FreeTracedCall(TracedCall *pCall); + IDebugListener *GetDebugHook(); private: IDebugListener *m_pDebugHook; - TracedCall *m_FreedCalls; - TracedCall *m_CallStack; - unsigned int m_CurChain; - //CFunction *m_pFreeFuncs; }; extern SourcePawnEngine g_engine1; diff --git a/sourcepawn/jit/sp_vm_function.cpp b/sourcepawn/jit/sp_vm_function.cpp index 6a52ea02..07dfab91 100644 --- a/sourcepawn/jit/sp_vm_function.cpp +++ b/sourcepawn/jit/sp_vm_function.cpp @@ -38,13 +38,11 @@ * FUNCTION CALLING * ********************/ -void CFunction::Set(uint32_t code_addr, BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id) +void CFunction::Set(BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id) { - m_codeaddr = code_addr; m_pRuntime = runtime; m_curparam = 0; m_errorstate = SP_ERROR_NONE; - m_Invalid = false; m_FnId = fnid; } @@ -68,10 +66,9 @@ IPluginContext *CFunction::GetParentContext() return m_pRuntime->GetDefaultContext(); } -CFunction::CFunction(uint32_t code_addr, BaseRuntime *runtime, funcid_t id, uint32_t pub_id) : - m_codeaddr(code_addr), m_curparam(0), m_errorstate(SP_ERROR_NONE), m_FnId(id) +CFunction::CFunction(BaseRuntime *runtime, funcid_t id, uint32_t pub_id) : + m_curparam(0), m_errorstate(SP_ERROR_NONE), m_FnId(id) { - m_Invalid = false; m_pRuntime = runtime; } @@ -347,9 +344,3 @@ int CFunction::SetError(int err) return err; } -bool CFunction::IsInvalidated() -{ - return m_Invalid; -} - - diff --git a/sourcepawn/jit/sp_vm_function.h b/sourcepawn/jit/sp_vm_function.h index c9f9c610..0969aa22 100644 --- a/sourcepawn/jit/sp_vm_function.h +++ b/sourcepawn/jit/sp_vm_function.h @@ -54,13 +54,13 @@ struct ParamInfo }; class CPlugin; +class JitFunction; class CFunction : public IPluginFunction { friend class SourcePawnEngine; public: - CFunction(uint32_t code_addr, - BaseRuntime *pRuntime, + CFunction(BaseRuntime *pRuntime, funcid_t fnid, uint32_t pub_id); public: @@ -75,8 +75,6 @@ public: virtual void Cancel(); virtual int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result); virtual IPluginContext *GetParentContext(); - bool IsInvalidated(); - void Invalidate(); bool IsRunnable(); funcid_t GetFunctionID(); int Execute2(IPluginContext *ctx, cell_t *result); @@ -85,19 +83,16 @@ public: unsigned int num_params, cell_t *result); public: - void Set(uint32_t code_addr, BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id); + void Set(BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id); private: int _PushString(const char *string, int sz_flags, int cp_flags, size_t len); int SetError(int err); private: - uint32_t m_codeaddr; BaseRuntime *m_pRuntime; cell_t m_params[SP_MAX_EXEC_PARAMS]; ParamInfo m_info[SP_MAX_EXEC_PARAMS]; unsigned int m_curparam; int m_errorstate; - CFunction *m_pNext; - bool m_Invalid; funcid_t m_FnId; }; diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index db108b48..70d7ff9b 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -41,11 +41,14 @@ #include "../BaseRuntime.h" #include "../sp_vm_basecontext.h" +using namespace Knight; + #if defined USE_UNGEN_OPCODES #include "ungen_opcodes.h" #endif -JITX86 g_Jit1; +JITX86 g_Jit; +KeCodeCache *g_pCodeCache = NULL; ISourcePawnEngine *engine = &g_engine1; inline sp_plugin_t *GETPLUGIN(sp_context_t *ctx) @@ -271,8 +274,7 @@ inline void WriteOp_Sub_Alt(JitWriter *jit) inline void WriteOp_Proc(JitWriter *jit) { - CompData *co = (CompData *)jit->data; - +#if 0 /* :TODO: We no longer use this */ /* Specialized code to align this taking in account the function magic number */ jitoffs_t cur_offs = jit->get_outputpos(); jitoffs_t offset = ((cur_offs & 0xFFFFFFF8) + 8) - cur_offs; @@ -295,8 +297,8 @@ inline void WriteOp_Proc(JitWriter *jit) /* Now we have to backpatch our reloction offset! */ { jitoffs_t offs = jit->get_inputpos() - sizeof(cell_t); - jitcode_t rebase = ((CompData *)jit->data)->rebase; - *(jitoffs_t *)((unsigned char *)rebase + offs) = jit->get_outputpos(); + uint8_t *rebase = ((CompData *)jit->data)->rebase; + *(jitoffs_t *)(rebase + offs) = jit->get_outputpos(); } /* Lastly, if we're writing, keep track of the function count */ @@ -304,6 +306,7 @@ inline void WriteOp_Proc(JitWriter *jit) { co->func_idx++; } +#endif //push old frame on stack: //mov ecx, [esi+frm] @@ -312,6 +315,7 @@ inline void WriteOp_Proc(JitWriter *jit) IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_INFO, MOD_MEM_REG); IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4); IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 8, MOD_REG); + //save frame: //mov ecx, edi - get new frame //mov ebx, edi - store frame back @@ -1065,7 +1069,7 @@ inline void WriteOp_GenArray(JitWriter *jit, bool autozero) IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, AMX_REG_ALT, AMX_INFO_HEAP); IA32_Add_Reg_Rm(jit, AMX_REG_ALT, AMX_REG_DAT, MOD_REG); IA32_Cmp_Reg_Rm(jit, AMX_REG_ALT, AMX_REG_STK, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_heaplow); + IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_heaplow); WriteOp_Tracker_Push_Reg(jit, REG_ECX); @@ -1104,7 +1108,7 @@ inline void WriteOp_GenArray(JitWriter *jit, bool autozero) IA32_Mov_Reg_Imm32(jit, REG_EDX, 0); } jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, call, ((CompData *)jit->data)->jit_genarray); + IA32_Write_Jump32_Abs(jit, call, g_Jit.GetGenArrayIntrinsic()); } } @@ -1376,51 +1380,97 @@ inline void WriteOp_Call(JitWriter *jit) cell_t offs; jitoffs_t jmp; CompData *data; - + uint32_t func_idx; + data = (CompData *)jit->data; offs = jit->read_cell(); - if ((data->profile & SP_PROF_FUNCTIONS) == SP_PROF_FUNCTIONS) + /* Get the context + rp */ + //mov eax, [esi+ctx] + //mov ecx, [eax+rp] + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); + IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, rp)); + + /* Check if the return stack is used up. */ + //cmp ecx, + //jae :stacklow + IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_ECX, SP_MAX_RETURN_STACK); + IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, data->jit_error_stacklow); + + /* Add us to the return stack */ + //mov [eax+ecx*4+cips], cip + IA32_Mov_Rm_Imm32_SIB(jit, + REG_EAX, + (uint8_t *)(jit->inptr - 2) - data->plugin->pcode, + offsetof(sp_context_t, rstk_cips), + REG_ECX, + SCALE4); + + /* Increment the return stack pointer */ + //inc [eax+rp] + if ((int)offsetof(sp_context_t, rp) >= SCHAR_MIN && (int)offsetof(sp_context_t, rp) <= SCHAR_MAX) { - const char *name; - - /* Find the function name */ - if ((name = find_func_name(data->plugin, offs)) == NULL) - { - name = "unknown"; - } - - //push name - //push [esi+context] - //call ProfCallGate_Begin - //add esp, 8 - IA32_Push_Imm32(jit, (jit_int32_t)(intptr_t)name); - IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_CONTEXT); - jmp = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, jmp, (void *)ProfCallGate_Begin); - IA32_Add_Rm_Imm8(jit, REG_ESP, 8, MOD_REG); - - //call - //push eax - jmp = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, jmp, RelocLookup(jit, offs, false)); - IA32_Push_Reg(jit, REG_EAX); - - //push [esi+context] - //call ProfCallGate_End - //add esp, 4 - //pop eax - IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_CONTEXT); - jmp = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32_Abs(jit, jmp, (void *)ProfCallGate_End); - IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); - IA32_Pop_Reg(jit, REG_EAX); + IA32_Inc_Rm_Disp8(jit, REG_EAX, offsetof(sp_context_t, rp)); } else { - jmp = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, jmp, RelocLookup(jit, offs, false)); + IA32_Inc_Rm_Disp32(jit, REG_EAX, offsetof(sp_context_t, rp)); } + + /* Store the CIP of the function we're about to call. */ + IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_INFO, offs, AMX_INFO_CIP); + + /* Call the function */ + func_idx = FuncLookup(data, offs); + + /* We need to emit a delayed thunk instead. */ + if (func_idx == 0) + { + if (jit->outbase == NULL) + { + data->num_thunks++; + + /* We still emit the call because we need consistent size counting */ + IA32_Call_Imm32(jit, 0); + } + else + { + call_thunk_t *thunk; + + /* Find the thunk we're associated with */ + thunk = &data->thunks[data->num_thunks]; + data->num_thunks++; + + /* Emit the call, save its target position. + * Save thunk info, then patch the target to the thunk. + */ + thunk->patch_addr = IA32_Call_Imm32(jit, 0); + thunk->pcode_offs = offs; + IA32_Write_Jump32(jit, thunk->patch_addr, thunk->thunk_addr); + } + } + /* The function is already jitted. We can emit a direct call. */ + else + { + JitFunction *fn; + + fn = data->runtime->GetJittedFunction(func_idx); + jmp = IA32_Call_Imm32(jit, 0); + IA32_Write_Jump32_Abs(jit, jmp, fn->GetEntryAddress()); + } + + /* Restore the last cip */ + //mov [esi+cip], + IA32_Mov_Rm_Imm32_Disp8(jit, + AMX_REG_INFO, + (uint8_t *)(jit->inptr - 2) - data->plugin->pcode, + AMX_INFO_CIP); + + /* Mark us as leaving the last frame. */ + //mov ecx, [esi+ctx] + //dec [ecx+rp] + IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); + IA32_Dec_Rm_Disp8(jit, REG_ECX, offsetof(sp_context_t, rp)); } inline void WriteOp_Bounds(JitWriter *jit) @@ -1435,7 +1485,7 @@ inline void WriteOp_Bounds(JitWriter *jit) } else { IA32_Cmp_Eax_Imm32(jit, val); } - IA32_Jump_Cond_Imm32_Abs(jit, CC_A, ((CompData *)jit->data)->jit_error_bounds); + IA32_Jump_Cond_Imm32_Rel(jit, CC_A, ((CompData *)jit->data)->jit_error_bounds); } inline void WriteOp_Halt(JitWriter *jit) @@ -1453,32 +1503,27 @@ inline void WriteOp_Halt(JitWriter *jit) */ jit->read_cell(); - CompData *data = (CompData *)jit->data; - IA32_Jump_Imm32_Abs(jit, data->jit_return); + IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint()); } inline void WriteOp_Break(JitWriter *jit) { - CompData *data = (CompData *)jit->data; - if (data->debug) - { - //jit->write_ubyte(IA32_INT3); - //mov ecx, - jitoffs_t wr = IA32_Mov_Reg_Imm32(jit, AMX_REG_TMP, 0); - jitoffs_t save = jit->get_outputpos(); - jit->set_outputpos(wr); - jit->write_uint32((uint32_t)(wr)); - jit->set_outputpos(save); - wr = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, wr, data->jit_break); - } + CompData *data; + + data = (CompData *)jit->data; + + //mov [esi+cip], + IA32_Mov_Rm_Imm32_Disp8(jit, + AMX_REG_INFO, + (uint8_t *)(jit->inptr - 1) - data->plugin->pcode, + AMX_INFO_CIP); } inline void WriteOp_Jump(JitWriter *jit) { //jmp cell_t amx_offs = jit->read_cell(); - IA32_Jump_Imm32_Abs(jit, RelocLookup(jit, amx_offs, false)); + IA32_Jump_Imm32_Rel(jit, RelocLookup(jit, amx_offs, false)); } inline void WriteOp_Jzer(JitWriter *jit) @@ -1487,7 +1532,7 @@ inline void WriteOp_Jzer(JitWriter *jit) //jz cell_t target = jit->read_cell(); IA32_Test_Rm_Reg(jit, AMX_REG_PRI, AMX_REG_PRI, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_Z, RelocLookup(jit, target, false)); } inline void WriteOp_Jnz(JitWriter *jit) @@ -1496,7 +1541,7 @@ inline void WriteOp_Jnz(JitWriter *jit) //jnz cell_t target = jit->read_cell(); IA32_Test_Rm_Reg(jit, AMX_REG_PRI, AMX_REG_PRI, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_NZ, RelocLookup(jit, target, false)); } inline void WriteOp_Jeq(JitWriter *jit) @@ -1505,7 +1550,7 @@ inline void WriteOp_Jeq(JitWriter *jit) //je cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_E, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_E, RelocLookup(jit, target, false)); } inline void WriteOp_Jneq(JitWriter *jit) @@ -1514,7 +1559,7 @@ inline void WriteOp_Jneq(JitWriter *jit) //jne cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NE, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_NE, RelocLookup(jit, target, false)); } inline void WriteOp_Jsless(JitWriter *jit) @@ -1523,7 +1568,7 @@ inline void WriteOp_Jsless(JitWriter *jit) //jl cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_L, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_L, RelocLookup(jit, target, false)); } inline void WriteOp_Jsleq(JitWriter *jit) @@ -1532,7 +1577,7 @@ inline void WriteOp_Jsleq(JitWriter *jit) //jle cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_LE, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_LE, RelocLookup(jit, target, false)); } inline void WriteOp_JsGrtr(JitWriter *jit) @@ -1541,7 +1586,7 @@ inline void WriteOp_JsGrtr(JitWriter *jit) //jg cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_G, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_G, RelocLookup(jit, target, false)); } inline void WriteOp_JsGeq(JitWriter *jit) @@ -1550,7 +1595,7 @@ inline void WriteOp_JsGeq(JitWriter *jit) //jge cell_t target = jit->read_cell(); IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_GE, RelocLookup(jit, target, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_GE, RelocLookup(jit, target, false)); } inline void WriteOp_Switch(JitWriter *jit) @@ -1570,7 +1615,7 @@ inline void WriteOp_Switch(JitWriter *jit) { /* Special treatment for 0 cases */ //jmp - IA32_Jump_Imm32_Abs(jit, RelocLookup(jit, *tbl, false)); + IA32_Jump_Imm32_Rel(jit, RelocLookup(jit, *tbl, false)); } else { /* Check if the case layout is fully sequential */ casetbl *iter = (casetbl *)(tbl + 1); @@ -1615,7 +1660,7 @@ inline void WriteOp_Switch(JitWriter *jit) IA32_Cmp_Rm_Imm32(jit, MOD_REG, AMX_REG_TMP, high_bound); } //ja - IA32_Jump_Cond_Imm32_Abs(jit, CC_A, RelocLookup(jit, *tbl, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_A, RelocLookup(jit, *tbl, false)); /** * Now we've taken the default case out of the way, it's time to do the @@ -1662,10 +1707,10 @@ inline void WriteOp_Switch(JitWriter *jit) } else { IA32_Cmp_Eax_Imm32(jit, cases[i].val); } - IA32_Jump_Cond_Imm32_Abs(jit, CC_E, RelocLookup(jit, cases[i].offs, false)); + IA32_Jump_Cond_Imm32_Rel(jit, CC_E, RelocLookup(jit, cases[i].offs, false)); } /* After all this, jump to the default case! */ - IA32_Jump_Imm32_Abs(jit, RelocLookup(jit, *tbl, false)); + IA32_Jump_Imm32_Rel(jit, RelocLookup(jit, *tbl, false)); } } } @@ -1687,7 +1732,7 @@ inline void WriteOp_Sysreq_C(JitWriter *jit) */ cell_t native_index = jit->read_cell(); - if ((uint32_t)native_index >= ((CompData*)jit->data)->plugin->info.natives_num) + if ((uint32_t)native_index >= ((CompData*)jit->data)->plugin->num_natives) { ((CompData *)jit->data)->error_set = SP_ERROR_INSTRUCTION_PARAM; return; @@ -1700,29 +1745,6 @@ inline void WriteOp_Sysreq_C(JitWriter *jit) IA32_Write_Jump32(jit, call, ((CompData *)jit->data)->jit_sysreq_c); } -inline void WriteOp_Sysreq_N_NoInline(JitWriter *jit) -{ - /* store the number of parameters on the stack, - * and store the native index as well. - */ - cell_t native_index = jit->read_cell(); - cell_t num_params = jit->read_cell(); - - if ((uint32_t)native_index >= ((CompData*)jit->data)->plugin->info.natives_num) - { - ((CompData *)jit->data)->error_set = SP_ERROR_INSTRUCTION_PARAM; - return; - } - - //mov eax, - //mov ecx, - IA32_Mov_Reg_Imm32(jit, REG_EAX, num_params); - IA32_Mov_Reg_Imm32(jit, REG_ECX, native_index); - - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, call, ((CompData *)jit->data)->jit_sysreq_n); -} - inline void WriteOp_Sysreq_N(JitWriter *jit) { /* The big daddy of opcodes. */ @@ -1730,7 +1752,7 @@ inline void WriteOp_Sysreq_N(JitWriter *jit) cell_t num_params = jit->read_cell(); CompData *data = (CompData *)jit->data; - if ((uint32_t)native_index >= data->plugin->info.natives_num) + if ((uint32_t)native_index >= data->plugin->num_natives) { data->error_set = SP_ERROR_INSTRUCTION_PARAM; return; @@ -1788,27 +1810,13 @@ inline void WriteOp_Sysreq_N(JitWriter *jit) //call NativeCallback IA32_Push_Reg(jit, REG_EAX); jitoffs_t call = IA32_Call_Imm32(jit, 0); - if (!data->debug) + if ((data->profile & SP_PROF_NATIVES) == SP_PROF_NATIVES) { - if ((data->profile & SP_PROF_NATIVES) == SP_PROF_NATIVES) - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Profile); - } - else - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback); - } + IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Profile); } else { - if ((data->profile & SP_PROF_NATIVES) == SP_PROF_NATIVES) - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug_Profile); - } - else - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug); - } + IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback); } /* check for errors */ @@ -1817,7 +1825,7 @@ inline void WriteOp_Sysreq_N(JitWriter *jit) //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); + IA32_Jump_Cond_Imm32_Rel(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ //mov esp, ebx @@ -1846,7 +1854,6 @@ inline void WriteOp_Sysreq_N(JitWriter *jit) inline void WriteOp_Tracker_Push_C(JitWriter *jit) { - CompData *data = (CompData *)jit->data; cell_t val = jit->read_cell(); /* Save registers that may be damaged by the call */ @@ -1868,7 +1875,7 @@ inline void WriteOp_Tracker_Push_C(JitWriter *jit) //cmp eax, 0 //jnz :error IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return); + IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, g_Jit.GetReturnPoint()); /* Restore */ //pop eax @@ -1893,8 +1900,6 @@ inline void WriteOp_Tracker_Push_C(JitWriter *jit) inline void WriteOp_Tracker_Pop_SetHeap(JitWriter *jit) { - CompData *data = (CompData *)jit->data; - /* Save registers that may be damaged by the call */ //push eax //push edx @@ -1914,7 +1919,7 @@ inline void WriteOp_Tracker_Pop_SetHeap(JitWriter *jit) //cmp eax, 0 //jnz :error IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return); + IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, g_Jit.GetReturnPoint()); /* Restore */ //pop eax @@ -2241,8 +2246,6 @@ inline void WriteOp_RoundToZero(JitWriter *jit) inline void WriteOp_FloatCompare(JitWriter *jit) { - CompData *data = (CompData *)jit->data; - //fld [edi] //fld [edi+4] //fucomip st(0), st(1) @@ -2255,7 +2258,7 @@ inline void WriteOp_FloatCompare(JitWriter *jit) IA32_Fld_Mem32(jit, AMX_REG_STK); IA32_Fld_Mem32_Disp8(jit, AMX_REG_STK, 4); IA32_Fucomip_ST0_FPUreg(jit, 1); - IA32_Mov_Reg_Imm32(jit, AMX_REG_TMP, (jit_int32_t)jit->outbase + data->jit_rounding_table); + IA32_Mov_Reg_Imm32(jit, AMX_REG_TMP, (jit_int32_t)g_Jit.GetRoundingTable()); IA32_CmovCC_Rm_Disp8(jit, AMX_REG_TMP, CC_Z, 4); IA32_CmovCC_Rm_Disp8(jit, AMX_REG_TMP, CC_B, 8); IA32_CmovCC_Rm(jit, AMX_REG_TMP, CC_A); @@ -2266,6 +2269,45 @@ inline void WriteOp_FloatCompare(JitWriter *jit) IA32_Add_Rm_Imm8(jit, AMX_REG_STK, 8, MOD_REG); } +inline void WriteOp_EndProc(JitWriter *jit) +{ +} + +void Write_CallThunk(JitWriter *jit, jitoffs_t jmploc, cell_t pcode_offs) +{ + CompData *data; + jitoffs_t call; + + data = (CompData *)jit->data; + + //push + //push + //push + //call CompileFromThunk + //add esp, 4*3 + //test eax, eax + //jz :error + //call eax + //ret + IA32_Push_Imm32(jit, (jit_int32_t)(jit->outbase + jmploc)); + IA32_Push_Imm32(jit, pcode_offs); + IA32_Push_Imm32(jit, (jit_int32_t)(data->runtime)); + call = IA32_Call_Imm32(jit, 0); + IA32_Write_Jump32_Abs(jit, call, (void *)CompileThunk); + IA32_Add_Rm_Imm32(jit, REG_ESP, 4*3, MOD_REG); + IA32_Test_Rm_Reg(jit, REG_EAX, REG_EAX, MOD_REG); + call = IA32_Jump_Cond_Imm8(jit, CC_Z, 0); + IA32_Call_Reg(jit, REG_EAX); + IA32_Return(jit); + + /* We decrement the frame and store the target cip. */ + //:error + //mov [esi+cip], pcode_offs + //goto error + IA32_Send_Jump8_Here(jit, call); + Write_SetError(jit, SP_ERROR_INVALID_INSTRUCTION); +} + /************************************************* ************************************************* * JIT PROPER ************************************ @@ -2273,152 +2315,110 @@ inline void WriteOp_FloatCompare(JitWriter *jit) ************************************************* *************************************************/ +void *CompileThunk(BaseRuntime *runtime, cell_t pcode_offs, void *jmploc_addr) +{ + int err; + JitFunction *fn; + uint32_t func_idx; + void *target_addr; + + if ((func_idx = FuncLookup((CompData *)runtime->m_pCo, pcode_offs)) == 0) + { + fn = g_Jit.CompileFunction(runtime, pcode_offs, &err); + } + else + { + fn = runtime->GetJittedFunction(func_idx); + +#if defined _DEBUG + g_engine1.GetDebugHook()->OnDebugSpew("Patching thunk to %s::%s", runtime->m_pPlugin->name, find_func_name(runtime->m_pPlugin, pcode_offs)); +#endif + + } + + if (fn == NULL) + { + return NULL; + } + + target_addr = fn->GetEntryAddress(); + + /* Right now, we always keep the code RWE */ + *(intptr_t *)((char *)jmploc_addr) = + intptr_t(target_addr) - (intptr_t(jmploc_addr) + 4); + + return target_addr; +} + cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params) { sp_native_t *native; - sp_plugin_t *plugin; - - plugin = (sp_plugin_t *)ctx->vm[JITVARS_PLUGIN]; - - native = &plugin->natives[native_idx]; + cell_t save_sp = ctx->sp; + cell_t save_hp = ctx->hp; + sp_plugin_t *pl = GETPLUGIN(ctx); ctx->n_idx = native_idx; - - /* Technically both aren't needed, I guess */ + + if (ctx->hp < (cell_t)pl->data_size) + { + ctx->n_err = SP_ERROR_HEAPMIN; + return 0; + } + + if (ctx->hp + STACK_MARGIN > ctx->sp) + { + ctx->n_err = SP_ERROR_STACKLOW; + return 0; + } + + if ((uint32_t)ctx->sp >= pl->mem_size) + { + ctx->n_err = SP_ERROR_STACKMIN; + return 0; + } + + native = &pl->natives[native_idx]; + if (native->status == SP_NATIVE_UNBOUND) { ctx->n_err = SP_ERROR_INVALID_NATIVE; return 0; } - return native->pfn(GET_CONTEXT(ctx), params); + cell_t result = native->pfn(GET_CONTEXT(ctx), params); + + if (ctx->n_err != SP_ERROR_NONE) + { + return result; + } + + if (save_sp != ctx->sp) + { + ctx->n_err = SP_ERROR_STACKLEAK; + return result; + } + else if (save_hp != ctx->hp) + { + ctx->n_err = SP_ERROR_HEAPLEAK; + return result; + } + + return result; } cell_t NativeCallback_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params) { - cell_t val; - sp_native_t *native; - sp_plugin_t *plugin; - - plugin = (sp_plugin_t *)ctx->vm[JITVARS_PLUGIN]; - - native = &plugin->natives[native_idx]; - - ctx->n_idx = native_idx; - - /* Technically both aren't needed, I guess */ - if (native->status == SP_NATIVE_UNBOUND) - { - ctx->n_err = SP_ERROR_INVALID_NATIVE; - return 0; - } - - plugin->profiler->OnNativeBegin(GET_CONTEXT(ctx), native); - val = native->pfn(GET_CONTEXT(ctx), params); - plugin->profiler->OnNativeEnd(); - - return val; + /* :TODO: */ + return NativeCallback(ctx, native_idx, params); } -cell_t NativeCallback_Debug(sp_context_t *ctx, ucell_t native_idx, cell_t *params) +uint32_t FuncLookup(CompData *data, cell_t pcode_offs) { - cell_t save_sp = ctx->sp; - cell_t save_hp = ctx->hp; + /* Offset must always be 1)positive and 2)less than or equal to the codesize */ + assert(pcode_offs >= 0 && (uint32_t)pcode_offs <= data->plugin->pcode_size); - sp_plugin_t *pl = GETPLUGIN(ctx); - - ctx->n_idx = native_idx; - - if (ctx->hp < (cell_t)pl->data_size) - { - ctx->n_err = SP_ERROR_HEAPMIN; - return 0; - } - - if (ctx->hp + STACK_MARGIN > ctx->sp) - { - ctx->n_err = SP_ERROR_STACKLOW; - return 0; - } - - if ((uint32_t)ctx->sp >= pl->mem_size) - { - ctx->n_err = SP_ERROR_STACKMIN; - return 0; - } - - cell_t result = NativeCallback(ctx, native_idx, params); - - if (ctx->n_err != SP_ERROR_NONE) - { - return result; - } - - if (save_sp != ctx->sp) - { - ctx->n_err = SP_ERROR_STACKLEAK; - return result; - } - else if (save_hp != ctx->hp) - { - ctx->n_err = SP_ERROR_HEAPLEAK; - return result; - } - - return result; -} - -cell_t NativeCallback_Debug_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params) -{ - cell_t save_sp = ctx->sp; - cell_t save_hp = ctx->hp; - - sp_plugin_t *pl = GETPLUGIN(ctx); - - ctx->n_idx = native_idx; - - if (ctx->hp < (cell_t)pl->data_size) - { - ctx->n_err = SP_ERROR_HEAPMIN; - return 0; - } - - if (ctx->hp + STACK_MARGIN > ctx->sp) - { - ctx->n_err = SP_ERROR_STACKLOW; - return 0; - } - - if ((uint32_t)ctx->sp >= pl->mem_size) - { - ctx->n_err = SP_ERROR_STACKMIN; - return 0; - } - - cell_t result = NativeCallback_Profile(ctx, native_idx, params); - - if (ctx->n_err != SP_ERROR_NONE) - { - return result; - } - - if (save_sp != ctx->sp) - { - ctx->n_err = SP_ERROR_STACKLEAK; - return result; - } - else if (save_hp != ctx->hp) - { - ctx->n_err = SP_ERROR_HEAPLEAK; - return result; - } - - return result; -} - -static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params) -{ - return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native"); + /* Do the lookup in the native dictionary. */ + return *(jitoffs_t *)(data->rebase + pcode_offs); } jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative) @@ -2434,7 +2434,7 @@ jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative) pcode_offs += jit->get_inputpos(); } /* Offset must always be 1)positive and 2)less than or equal to the codesize */ - assert(pcode_offs >= 0 && (uint32_t)pcode_offs <= data->codesize); + assert(pcode_offs >= 0 && (uint32_t)pcode_offs <= data->plugin->pcode_size); /* Do the lookup in the native dictionary. */ return *(jitoffs_t *)(data->rebase + pcode_offs); } @@ -2469,130 +2469,144 @@ void WriteErrorRoutines(CompData *data, JitWriter *jit) data->jit_error_heapmin = jit->get_outputpos(); Write_SetError(jit, SP_ERROR_HEAPMIN); - data->jit_error_array_too_big = jit->get_outputpos(); - Write_SetError(jit, SP_ERROR_ARRAY_TOO_BIG); - data->jit_extern_error = jit->get_outputpos(); Write_GetError(jit); } -bool JITX86::Compile(ICompilation *co, BaseRuntime *prt, int *err) +ICompilation *JITX86::ApplyOptions(ICompilation *_IN, ICompilation *_OUT) { - CompData *data = (CompData *)co; - sp_plugin_t *plugin = data->plugin; - - if (data->plugin == NULL) + if (_IN == NULL) { - if (data->debug && !(prt->m_pPlugin->flags & SP_FLAG_DEBUG)) - { - if (err != NULL) - { - *err = SP_ERROR_NOTDEBUGGING; - } - co->Abort(); - return false; - } - - data->SetRuntime(prt); - plugin = data->plugin; + return _OUT; } - /* The first phase is to browse */ - uint8_t *code = plugin->pcode; - uint8_t *end_cip = plugin->pcode + plugin->pcode_size; - OPCODE op; + CompData *_in = (CompData * )_IN; + CompData *_out = (CompData * )_OUT; - /********************************************* - * FIRST PASS (medium load): writer.outbase is NULL, getting size only - * SECOND PASS (heavy load!!): writer.outbase is valid and output is written - *********************************************/ + _in->inline_level = _out->inline_level; + _in->profile = _out->profile; - JitWriter writer; - JitWriter *jit = &writer; - cell_t *endptr = (cell_t *)(end_cip); - uint32_t codemem = 0; + _out->Abort(); - /* Initial code is written "blank," - * so we can check the exact memory usage. - */ - data->codesize = plugin->pcode_size; - writer.data = data; - writer.inbase = (cell_t *)code; - writer.outptr = NULL; - writer.outbase = NULL; - /* Allocate relocation. One extra cell for final CIP. */ - data->rebase = (jitcode_t)engine->BaseAlloc(plugin->pcode_size + sizeof(cell_t)); + return _in; +} - /* We will jump back here for second pass */ -jit_rewind: - /* Initialize pass vars */ - writer.inptr = writer.inbase; - data->jit_verify_addr_eax = 0; - data->jit_verify_addr_edx = 0; +JITX86::JITX86() +{ + m_pJitEntry = NULL; + m_pJitReturn = NULL; + m_RoundTable[0] = -1; + m_RoundTable[1] = 0; + m_RoundTable[2] = 1; + m_pJitGenArray = NULL; +} - /* Write the prologue of the JIT */ - data->jit_return = Write_Execute_Function(jit); +bool JITX86::InitializeJIT() +{ + jitoffs_t offs; + JitWriter writer, *jit; - /* Write the SYSREQ.N opcode if we need to */ - if (!(data->inline_level & JIT_INLINE_NATIVES)) - { - AlignMe(jit); - data->jit_sysreq_n = jit->get_outputpos(); - WriteOp_Sysreq_N_Function(jit); - } + g_pCodeCache = KE_CreateCodeCache(); - /* Write the debug section if we need it */ - if (data->debug == true) - { - AlignMe(jit); - data->jit_break = jit->get_outputpos(); - Write_BreakDebug(jit); - } - - /* Plugins compiled with -O0 will need this! */ - AlignMe(jit); - data->jit_sysreq_c = jit->get_outputpos(); - WriteOp_Sysreq_C_Function(jit); - - AlignMe(jit); - data->jit_genarray = jit->get_outputpos(); + jit = &writer; + + /* Build the genarray intrinsic */ + jit->outbase = NULL; + jit->outptr = NULL; + WriteIntrinsic_GenArray(jit); + m_pJitGenArray = Knight::KE_AllocCode(g_pCodeCache, jit->get_outputpos()); + jit->outbase = (jitcode_t)m_pJitGenArray; + jit->outptr = jit->outbase; WriteIntrinsic_GenArray(jit); - /* Write error checking routines that are called to */ - if (!(data->inline_level & JIT_INLINE_ERRORCHECKS)) + /* Build the entry point */ + writer = JitWriter(); + jit->outbase = NULL; + jit->outptr = NULL; + Write_Execute_Function(jit); + m_pJitEntry = Knight::KE_AllocCode(g_pCodeCache, jit->get_outputpos()); + jit->outbase = (jitcode_t)m_pJitEntry; + jit->outptr = jit->outbase; + offs = Write_Execute_Function(jit); + m_pJitReturn = (uint8_t *)m_pJitEntry + offs; + + return true; +} + +void JITX86::ShutdownJIT() +{ + KE_DestroyCodeCache(g_pCodeCache); +} + +JitFunction *JITX86::CompileFunction(BaseRuntime *prt, cell_t pcode_offs, int *err) +{ + CompData *data = (CompData *)prt->m_pCo; + sp_plugin_t *plugin = data->plugin; + + uint8_t *code = plugin->pcode + pcode_offs; + uint8_t *end_code = plugin->pcode + plugin->pcode_size; + + assert(FuncLookup(data, pcode_offs) == 0); + + if (code >= end_code || *(cell_t *)code != OP_PROC) { - AlignMe(jit); - data->jit_verify_addr_eax = jit->get_outputpos(); - Write_Check_VerifyAddr(jit, REG_EAX); - - AlignMe(jit); - data->jit_verify_addr_edx = jit->get_outputpos(); - Write_Check_VerifyAddr(jit, REG_EDX); + *err = SP_ERROR_INVALID_INSTRUCTION; + return NULL; } - /* Write the rounding table for the float compare opcode */ - data->jit_rounding_table = jit->get_outputpos(); - Write_RoundingTable(jit); +#if defined _DEBUG + g_engine1.GetDebugHook()->OnDebugSpew("Compiling function %s::%s", prt->m_pPlugin->name, find_func_name(prt->m_pPlugin, pcode_offs)); +#endif + + code += sizeof(cell_t); + + OPCODE op; + uint32_t code_size; + cell_t *cip, *end_cip; + JitWriter writer, *jit; + + jit = &writer; + cip = (cell_t *)code; + end_cip = (cell_t *)end_code; + writer.data = data; + writer.inbase = cip; + writer.outptr = NULL; + writer.outbase = NULL; + data->cur_func = pcode_offs; + +jit_rewind: + data->num_thunks = 0; + writer.inptr = writer.inbase; + + WriteOp_Proc(jit); /* Actual code generation! */ if (writer.outbase == NULL) { /* First Pass - find codesize and resolve relocation */ - jitoffs_t pcode_offs; + jitoffs_t jpcode_offs; jitoffs_t native_offs; - for (; writer.inptr < endptr;) + for (; writer.inptr < end_cip;) { + op = (OPCODE)writer.peek_cell(); + + /* If we hit another function, we must stop. */ + if (op == OP_PROC || op == OP_ENDPROC) + { + break; + } + /* Store the native offset into the rebase memory. * This large chunk of memory lets us do an instant lookup * based on an original pcode offset. */ - pcode_offs = (jitoffs_t)((uint8_t *)writer.inptr - code); + jpcode_offs = (jitoffs_t)((uint8_t *)writer.inptr - plugin->pcode); native_offs = jit->get_outputpos(); - *((jitoffs_t *)(data->rebase + pcode_offs)) = native_offs; + *((jitoffs_t *)(data->rebase + jpcode_offs)) = native_offs; - /* Now read the opcode and continue. */ - op = (OPCODE)writer.read_cell(); + /* Read past the opcode. */ + writer.read_cell(); /* Patch the floating point natives with our opcodes */ if (op == OP_SYSREQ_N) @@ -2619,209 +2633,108 @@ jit_rewind: if (data->error_set != SP_ERROR_NONE) { *err = data->error_set; - co->Abort(); - return false; + return NULL; } } - /* Write these last because error jumps should be unpredicted, and thus forward */ + + /* Write these last because error jumps should be predicted forwardly (not taken) */ WriteErrorRoutines(data, jit); + /* Build thunk tables */ + if (data->num_thunks > data->max_thunks) + { + data->max_thunks = data->num_thunks; + data->thunks = (call_thunk_t *)realloc( + data->thunks, + data->max_thunks * sizeof(call_thunk_t)); + } + + /* Write the thunk offsets. + * :TODO: we can emit all but one call to Write_CallThunk(). + */ + for (unsigned int i = 0; i < data->num_thunks; i++) + { + data->thunks[i].thunk_addr = jit->get_outputpos(); + Write_CallThunk(jit, 0, 0); + } + + /** + * I don't understand the purpose of this code. + * Why do we want to know about the last opcode? + * It should already have gotten written. + */ + #if 0 /* Write the final CIP to the last position in the reloc array */ pcode_offs = (jitoffs_t)((uint8_t *)writer.inptr - code); native_offs = jit->get_outputpos(); *((jitoffs_t *)(data->rebase + pcode_offs)) = native_offs; + #endif /* the total codesize is now known! */ - codemem = writer.get_outputpos(); - writer.outbase = (jitcode_t)engine->AllocatePageMemory(codemem); - engine->SetReadWrite(writer.outbase); + code_size = writer.get_outputpos(); + writer.outbase = (jitcode_t)Knight::KE_AllocCode(g_pCodeCache, code_size); writer.outptr = writer.outbase; - /* go back for third pass */ + + /* go back for second pass */ goto jit_rewind; } else { /******* - * THIRD PASS - write opcode info + * SECOND PASS - write opcode info *******/ - for (; writer.inptr < endptr;) + for (; writer.inptr < end_cip;) { op = (OPCODE)writer.read_cell(); + + /* If we hit another function, we must stop. */ + if (op == OP_PROC || op == OP_ENDPROC) + { + break; + } + switch (op) { #include "opcode_switch.inc" } } - /* Write these last because error jumps should be unpredicted, and thus forward */ + + /* Write these last because error jumps should be predicted as not taken (forward) */ WriteErrorRoutines(data, jit); - engine->SetReadExecute(writer.outbase); + /* Write the thunk offsets. */ + for (unsigned int i = 0; i < data->num_thunks; i++) + { + Write_CallThunk(jit, data->thunks[i].patch_addr, data->thunks[i].pcode_offs); + } } - /************* - * FOURTH PASS - Context Setup - *************/ - - /* setup basics */ - sp_context_t *ctx = data->runtime->GetBaseContext()->GetCtx(); - - /* Clear out any old cruft */ - if (plugin->codebase != NULL) - { - FreePluginVars(data->runtime->m_pPlugin); - FreeContextVars(ctx); - } - - plugin->codebase = writer.outbase; - plugin->jit_codesize = codemem; - plugin->jit_memsize = 0; - - /* setup memory */ - - ctx->hp = plugin->data_size; - ctx->sp = plugin->mem_size - sizeof(cell_t); - ctx->frm = ctx->sp; - ctx->n_err = SP_ERROR_NONE; - ctx->n_idx = SP_ERROR_NONE; plugin->prof_flags = data->profile; - plugin->run_flags = data->debug ? SPFLAG_PLUGIN_DEBUG : 0; - - const char *strbase = plugin->info.stringbase; - uint32_t max, iter; - - /* relocate public info */ - if ((max = plugin->info.publics_num)) - { - plugin->publics = new sp_public_t[max]; - plugin->jit_memsize += sizeof(sp_public_t) * max; - for (iter=0; iterpublics[iter].name = strbase + plugin->info.publics[iter].name; - plugin->publics[iter].code_offs = RelocLookup(jit, plugin->info.publics[iter].address, false); - /* Encode the ID as a straight code offset */ - plugin->publics[iter].funcid = (plugin->publics[iter].code_offs << 1); - } - } - - /* relocate pubvar info */ - if ((max = plugin->info.pubvars_num)) - { - uint8_t *dat = plugin->memory; - plugin->pubvars = new sp_pubvar_t[max]; - plugin->jit_memsize += sizeof(sp_pubvar_t) * max; - for (iter=0; iterpubvars[iter].name = strbase + plugin->info.pubvars[iter].name; - plugin->pubvars[iter].offs = (cell_t *)(dat + plugin->info.pubvars[iter].address); - } - } - - /* relocate native info */ - if ((max = plugin->info.natives_num) - && plugin->natives == NULL) - { - plugin->natives = new sp_native_t[max]; - plugin->jit_memsize += sizeof(sp_native_t) * max; - for (iter=0; iternatives[iter].name = strbase + plugin->info.natives[iter].name; - plugin->natives[iter].pfn = &InvalidNative; - plugin->natives[iter].status = SP_NATIVE_UNBOUND; - plugin->natives[iter].flags = 0; - plugin->natives[iter].user = NULL; - } - } - - /** - * If we're debugging, make sure we copy the necessary info. - */ - if (data->debug) - { - strbase = plugin->debug.stringbase; - - /* relocate files */ - max = plugin->debug.files_num; - plugin->files = new sp_debug_file_t[max]; - plugin->jit_memsize += sizeof(sp_debug_file_t) * max; - for (iter=0; iterfiles[iter].addr = RelocLookup(jit, plugin->debug.files[iter].addr, false); - plugin->files[iter].name = strbase + plugin->debug.files[iter].name; - } - - /* relocate lines */ - max = plugin->debug.lines_num; - plugin->lines = new sp_debug_line_t[max]; - plugin->jit_memsize += sizeof(sp_debug_line_t) * max; - for (iter=0; iterlines[iter].addr = RelocLookup(jit, plugin->debug.lines[iter].addr, false); - plugin->lines[iter].line = plugin->debug.lines[iter].line; - } - - /* relocate arrays */ - sp_fdbg_symbol_t *sym; - sp_fdbg_arraydim_t *arr; - uint8_t *cursor = (uint8_t *)(plugin->debug.symbols); - - max = plugin->debug.syms_num; - plugin->symbols = new sp_debug_symbol_t[max]; - plugin->jit_memsize += sizeof(sp_debug_symbol_t) * max; - for (iter=0; itercodestart > data->codesize) - { - plugin->symbols[iter].codestart = 0; - } else { - plugin->symbols[iter].codestart = RelocLookup(jit, sym->codestart, false); - } - if (sym->codeend > data->codesize) - { - plugin->symbols[iter].codeend = data->codesize; - } else { - plugin->symbols[iter].codeend = RelocLookup(jit, sym->codeend, false); - } - plugin->symbols[iter].name = strbase + sym->name; - plugin->symbols[iter].sym = sym; - - if (sym->dimcount > 0) - { - cursor += sizeof(sp_fdbg_symbol_t); - arr = (sp_fdbg_arraydim_t *)cursor; - plugin->symbols[iter].dims = arr; - cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount; - continue; - } - - plugin->symbols[iter].dims = NULL; - cursor += sizeof(sp_fdbg_symbol_t); - } - } - - tracker_t *trk = new tracker_t; - ctx->vm[JITVARS_TRACKER] = trk; - ctx->vm[JITVARS_BASECTX] = data->runtime->GetDefaultContext(); - ctx->vm[JITVARS_PROFILER] = g_engine2.GetProfiler(); - ctx->vm[JITVARS_PLUGIN] = data->runtime->m_pPlugin; - trk->pBase = (ucell_t *)malloc(1024); - trk->pCur = trk->pBase; - trk->size = 1024 / sizeof(cell_t); - - plugin->jit_memsize += trk->size; - - /* clean up relocation+compilation memory */ - co->Abort(); *err = SP_ERROR_NONE; - return true; + JitFunction *fn; + uint32_t func_idx; + + fn = new JitFunction(writer.outbase, pcode_offs); + func_idx = prt->AddJittedFunction(fn); + *(cell_t *)(data->rebase + pcode_offs) = func_idx; + + return fn; +} + +void JITX86::SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx) +{ + tracker_t *trk = new tracker_t; + + ctx->vm[JITVARS_TRACKER] = trk; + ctx->vm[JITVARS_BASECTX] = pCtx; /* GetDefaultContext() is not constructed yet */ + ctx->vm[JITVARS_PROFILER] = g_engine2.GetProfiler(); + ctx->vm[JITVARS_PLUGIN] = runtime->m_pPlugin; + + trk->pBase = (ucell_t *)malloc(1024); + trk->pCur = trk->pBase; + trk->size = 1024 / sizeof(cell_t); } SPVM_NATIVE_FUNC JITX86::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) @@ -2876,32 +2789,22 @@ rewind: if (jw.outbase == NULL) { /* Second pass: Actually write */ - jw.outbase = (jitcode_t)engine->AllocatePageMemory(jw.get_outputpos()); + jw.outbase = (jitcode_t)KE_AllocCode(g_pCodeCache, jw.get_outputpos()); if (!jw.outbase) { return NULL; } - engine->SetReadWrite(jw.outbase); jw.outptr = jw.outbase; goto rewind; } - engine->SetReadExecute(jw.outbase); - return (SPVM_NATIVE_FUNC)jw.outbase; } void JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func) { - engine->FreePageMemory((void *)func); -} - -int JITX86::ContextExecute(sp_plugin_t *pl, sp_context_t *ctx, uint32_t code_idx, cell_t *result) -{ - typedef int (*CONTEXT_EXECUTE)(sp_context_t *, uint32_t, cell_t *); - CONTEXT_EXECUTE fn = (CONTEXT_EXECUTE)pl->codebase; - return fn(ctx, code_idx, result); + KE_FreeCode(g_pCodeCache, (void *)func); } ICompilation *JITX86::StartCompilation() @@ -2917,10 +2820,10 @@ void CompData::SetRuntime(BaseRuntime *runtime) { plugin = runtime->m_pPlugin; - uint32_t max_natives = plugin->info.natives_num; - const char *strbase = plugin->info.stringbase; + uint32_t max_natives = plugin->num_natives; this->runtime = runtime; + this->plugin = runtime->m_pPlugin; inline_level = JIT_INLINE_ERRORCHECKS|JIT_INLINE_NATIVES; error_set = SP_ERROR_NONE; @@ -2928,43 +2831,69 @@ void CompData::SetRuntime(BaseRuntime *runtime) jit_float_table = new floattbl_t[max_natives]; for (uint32_t i=0; iinfo.natives[i].name; + const char *name = plugin->natives[i].name; if (!strcmp(name, "FloatAbs")) { jit_float_table[i].found = true; jit_float_table[i].index = OP_FABS; - } else if (!strcmp(name, "FloatAdd")) { + } + else if (!strcmp(name, "FloatAdd")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOATADD; - } else if (!strcmp(name, "FloatSub")) { + } + else if (!strcmp(name, "FloatSub")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOATSUB; - } else if (!strcmp(name, "FloatMul")) { + } + else if (!strcmp(name, "FloatMul")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOATMUL; - } else if (!strcmp(name, "FloatDiv")) { + } + else if (!strcmp(name, "FloatDiv")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOATDIV; - } else if (!strcmp(name, "float")) { + } + else if (!strcmp(name, "float")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOAT; - } else if (!strcmp(name, "FloatCompare")) { + } + else if (!strcmp(name, "FloatCompare")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_FLOATCMP; - } else if (!strcmp(name, "RoundToZero")) { + } + else if (!strcmp(name, "RoundToZero")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_RND_TO_ZERO; - } else if (!strcmp(name, "RoundToCeil")) { + } + else if (!strcmp(name, "RoundToCeil")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_RND_TO_CEIL; - } else if (!strcmp(name, "RoundToFloor")) { + } + else if (!strcmp(name, "RoundToFloor")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_RND_TO_FLOOR; - } else if (!strcmp(name, "RoundToNearest")) { + } + else if (!strcmp(name, "RoundToNearest")) + { jit_float_table[i].found = true; jit_float_table[i].index = OP_RND_TO_NEAREST; } } + + /* We need a relocation table. This is the relocation table we'll use for jumps + * and calls alike. One extra cell for final CIP. + */ + this->rebase = (uint8_t *)engine->BaseAlloc(plugin->pcode_size + sizeof(cell_t)); + memset(this->rebase, 0, plugin->pcode_size + sizeof(cell_t)); } ICompilation *JITX86::StartCompilation(BaseRuntime *runtime) @@ -2982,6 +2911,7 @@ void CompData::Abort() { engine->BaseFree(rebase); } + delete [] thunks; delete [] jit_float_table; delete this; } @@ -2992,42 +2922,10 @@ void JITX86::FreeContextVars(sp_context_t *ctx) delete (tracker_t *)ctx->vm[JITVARS_TRACKER]; } -void JITX86::FreePluginVars(sp_plugin_t *pl) -{ - delete [] pl->files; - delete [] pl->lines; - delete [] pl->publics; - delete [] pl->pubvars; - delete [] pl->symbols; - - if (pl->codebase != NULL) - { - g_engine1.FreePageMemory(pl->codebase); - pl->codebase = NULL; - } - - pl->files = NULL; - pl->lines = NULL; - pl->publics = NULL; - pl->pubvars = NULL; - pl->symbols = NULL; -} - bool CompData::SetOption(const char *key, const char *val) { if (strcmp(key, SP_JITCONF_DEBUG) == 0) { - if ((atoi(val) == 1) || !strcmp(val, "yes")) - { - debug = true; - } else { - debug = false; - } - if (debug && plugin && !(plugin->flags & SP_FLAG_DEBUG)) - { - debug = false; - return false; - } return true; } else if (strcmp(key, SP_JITCONF_PROFILE) == 0) @@ -3046,3 +2944,56 @@ bool CompData::SetOption(const char *key, const char *val) return false; } +void *JITX86::GetGenArrayIntrinsic() +{ + return m_pJitGenArray; +} + +void *JITX86::GetReturnPoint() +{ + return m_pJitReturn; +} + +void *JITX86::GetRoundingTable() +{ + return m_RoundTable; +} + +typedef int (*JIT_EXECUTE)(cell_t *vars, void *addr); +int JITX86::InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result) +{ + int err; + JIT_EXECUTE pfn; + sp_context_t *ctx; + cell_t vars[AMX_NUM_INFO_VARS]; + + ctx = runtime->GetBaseContext()->GetCtx(); + + vars[0] = ctx->sp; + vars[1] = ctx->hp; + vars[2] = (cell_t)result; + vars[3] = (cell_t)ctx; + vars[4] = (cell_t)(runtime->m_pPlugin->memory + runtime->m_pPlugin->mem_size); + vars[5] = fn->GetPCodeAddress(); + vars[6] = runtime->m_pPlugin->data_size; + vars[7] = (cell_t)(runtime->m_pPlugin->memory); + /* vars[8] will be set to ESP */ + + pfn = (JIT_EXECUTE)m_pJitEntry; + err = pfn(vars, fn->GetEntryAddress()); + + ctx->hp = vars[1]; + ctx->err_cip = vars[5]; + + return err; +} + +void *JITX86::AllocCode(size_t size) +{ + return Knight::KE_AllocCode(g_pCodeCache, size); +} + +void JITX86::FreeCode(void *code) +{ + KE_FreeCode(g_pCodeCache, code); +} diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index 7b8bdf31..b30e4cc2 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -34,9 +34,11 @@ #include #include -#include -#include "../jit_shared.h" -#include "../BaseRuntime.h" +#include +#include "jit_helpers.h" +#include "jit_shared.h" +#include "BaseRuntime.h" +#include "jit_function.h" using namespace SourcePawn; @@ -84,12 +86,27 @@ struct floattbl_t unsigned int index; }; +struct call_thunk_t +{ + jitoffs_t patch_addr; + cell_t pcode_offs; + jitoffs_t thunk_addr; +}; + class CompData : public ICompilation { public: - CompData() : plugin(NULL), - debug(false), profile(0), inline_level(0), rebase(NULL), - error_set(SP_ERROR_NONE), func_idx(0) + CompData() + : runtime(NULL), + plugin(NULL), + rebase(NULL), + jit_float_table(NULL), + profile(0), + inline_level(0), + error_set(SP_ERROR_NONE), + num_thunks(0), + max_thunks(0), + thunks(NULL) { }; bool SetOption(const char *key, const char *val); @@ -98,18 +115,15 @@ public: public: BaseRuntime *runtime; /* runtime handle */ sp_plugin_t *plugin; /* plugin handle */ - bool debug; /* whether to compile debug mode */ + uint8_t *rebase; /* relocation map */ + floattbl_t *jit_float_table; + cell_t cur_func; /* current func pcode offset */ + /* Options */ int profile; /* profiling flags */ int inline_level; /* inline optimization level */ - jitcode_t rebase; /* relocation map */ + /* Per-compilation properties */ int error_set; /* error code to halt process */ unsigned int func_idx; /* current function index */ - jitoffs_t jit_return; /* point in main call to return to */ - jitoffs_t jit_verify_addr_eax; - jitoffs_t jit_verify_addr_edx; - jitoffs_t jit_break; /* call to op.break */ - jitoffs_t jit_sysreq_n; /* call version of op.sysreq.n */ - jitoffs_t jit_genarray; /* call to genarray intrinsic */ jitoffs_t jit_error_bounds; jitoffs_t jit_error_divzero; jitoffs_t jit_error_stacklow; @@ -117,32 +131,47 @@ public: jitoffs_t jit_error_memaccess; jitoffs_t jit_error_heaplow; jitoffs_t jit_error_heapmin; - jitoffs_t jit_error_array_too_big; jitoffs_t jit_extern_error; /* returning generic error */ jitoffs_t jit_sysreq_c; /* old version! */ - jitoffs_t jit_rounding_table; - floattbl_t *jit_float_table; - uint32_t codesize; /* total codesize */ + uint32_t num_thunks; /* number of thunks needed */ + uint32_t max_thunks; /* maximum number of thunks */ + call_thunk_t *thunks; /* thunk array */ }; class JITX86 { public: + JITX86(); +public: + bool InitializeJIT(); + void ShutdownJIT(); ICompilation *StartCompilation(BaseRuntime *runtime); ICompilation *StartCompilation(); - bool Compile(ICompilation *co, BaseRuntime *runtime, int *err); - void FreePluginVars(sp_plugin_t *pl); + void SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx); void FreeContextVars(sp_context_t *ctx); - int ContextExecute(sp_plugin_t *pl, sp_context_t *ctx, uint32_t code_idx, cell_t *result); SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData); void DestroyFakeNative(SPVM_NATIVE_FUNC func); + JitFunction *CompileFunction(BaseRuntime *runtime, cell_t pcode_offs, int *err); + ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT); + int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result); +public: + void *GetGenArrayIntrinsic(); + void *GetReturnPoint(); + void *GetRoundingTable(); + void *AllocCode(size_t size); + void FreeCode(void *code); +private: + void *m_pJitEntry; /* Entry function */ + void *m_pJitReturn; /* Return point for errors */ + int m_RoundTable[3]; /* [-1, 0, 1] rounding table */ + void *m_pJitGenArray; /* Generates an array */ }; cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params); -cell_t NativeCallback_Debug(sp_context_t *ctx, ucell_t native_idx, cell_t *params); -cell_t NativeCallback_Debug_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params); cell_t NativeCallback_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params); +uint32_t FuncLookup(CompData *data, cell_t pcode_offs); jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative=false); +void *CompileThunk(BaseRuntime *runtime, cell_t pcode_ffs, void *jmploc_addr); #define AMX_REG_PRI REG_EAX #define AMX_REG_ALT REG_EDX @@ -152,13 +181,20 @@ jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative=false); #define AMX_REG_INFO REG_ESI #define AMX_REG_FRM REG_EBX +#define AMX_NUM_INFO_VARS 9 + #define AMX_INFO_FRM AMX_REG_INFO //not relocated #define AMX_INFO_FRAME 0 //(same thing as above) #define AMX_INFO_HEAP 4 //not relocated #define AMX_INFO_RETVAL 8 //physical #define AMX_INFO_CONTEXT 12 //physical #define AMX_INFO_STACKTOP 16 //relocated +#define AMX_INFO_CIP 20 //pcode CIP +#define AMX_INFO_DATASIZE 24 //plugin->data_size +#define AMX_INFO_MEMORY 28 //plugin->memory +#define AMX_INFO_NSTACK 32 //native stack -extern JITX86 g_Jit1; +extern Knight::KeCodeCache *g_pCodeCache; +extern JITX86 g_Jit; #endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_ diff --git a/sourcepawn/jit/x86/opcode_helpers.cpp b/sourcepawn/jit/x86/opcode_helpers.cpp index 2b12036c..30311202 100644 --- a/sourcepawn/jit/x86/opcode_helpers.cpp +++ b/sourcepawn/jit/x86/opcode_helpers.cpp @@ -36,14 +36,11 @@ #include "opcode_helpers.h" #include "x86_macros.h" -#define NUM_INFO_PARAMS 5 - jitoffs_t Write_Execute_Function(JitWriter *jit) { - CompData *co = (CompData *)jit->data; /** * The variables we're passed in: - * sp_context_t *ctx, uint32_t code_idx, cell_t *result + * void *vars[], void *entry_func */ /** @@ -65,97 +62,58 @@ jitoffs_t Write_Execute_Function(JitWriter *jit) IA32_Push_Reg(jit, REG_EDI); IA32_Push_Reg(jit, REG_EBX); - //sub esp, 4*n - reserve info array - //mov esi, esp - save info pointer - IA32_Sub_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG); - IA32_Mov_Reg_Rm(jit, AMX_REG_INFO, REG_ESP, MOD_REG); + /* Prep us for doing the real work */ + //mov esi, [ebp+param0] ;get vars + //mov ecx, [ebp+param1] ;get entry addr + //mov eax, [esi+MEMORY] ;get memory base + //mov edx, [esi+CONTEXT] ;get context + IA32_Mov_Reg_Rm_Disp8(jit, REG_ESI, REG_EBP, 8 + 4*0); + IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EBP, 8 + 4*1); + IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_ESI, AMX_INFO_MEMORY); + IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_CONTEXT); - /* Initial memory setup */ - //mov eax, [ebp+16] - get result pointer - //mov [esi+8], eax - store into info pointer - //mov eax, [ebp+8] - get context - //mov [esi+12], eax - store context into info pointer - //mov ecx, [eax+] - get heap pointer - //mov [esi+4], ecx - store heap into info pointer - //mov ebp, - get data pointer - IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 16); - IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_RETVAL); - IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EBP, 8); - IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EAX, AMX_INFO_CONTEXT); - IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EAX, offsetof(sp_context_t, hp)); - IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_HEAP); - IA32_Mov_Reg_Imm32(jit, AMX_REG_DAT, jit_int32_t(co->plugin->memory)); - - /* Frame setup */ - //mov edi, [eax+] - get stack pointer - //add edi, ebp - relocate to data section - //mov ebx, edi - copy sp to frm - IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_STK, REG_EAX, offsetof(sp_context_t, sp)); - IA32_Add_Rm_Reg(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); - IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_REG); - - /* Info memory setup */ - //mov ecx, [eax+] - copy memsize to temp var - //add ecx, ebp - relocate - //mov [esi+x], ecx - store relocated - IA32_Mov_Reg_Imm32(jit, REG_ECX, co->plugin->mem_size); - IA32_Add_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_DAT, MOD_REG); - IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_ECX, AMX_INFO_STACKTOP); - - /* Remaining needed vars */ - //mov ecx, [esp+(4*(NUM_INFO_PARAMS+3))+12] - get code index (normally esp+12, but we have another array on the stack) - //push eax - //mov eax, - //mov eax, [eax] - //add ecx, eax - //pop eax - IA32_Mov_Reg_Esp_Disp8(jit, REG_ECX, 12+(4*(NUM_INFO_PARAMS+3))); - IA32_Push_Reg(jit, REG_EAX); - IA32_Mov_Reg_Imm32(jit, REG_EAX, jit_int32_t(&co->plugin->codebase)); - IA32_Mov_Reg_Rm(jit, REG_EAX, REG_EAX, MOD_MEM_REG); - IA32_Add_Reg_Rm(jit, REG_ECX, REG_EAX, MOD_REG); - IA32_Pop_Reg(jit, REG_EAX); + /* Set up run-time registers */ + //mov edi, [edx+SP] ;non-reloc SP + //add edi, eax ;reloc SP + //mov ebp, eax ;DAT + //mov ebx, edi ;reloc FRM + //mov [esi+NSTACK], esp ;save ESP + IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EDX, offsetof(sp_context_t, sp)); + IA32_Add_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_REG); + IA32_Mov_Reg_Rm(jit, REG_EBP, REG_EAX, MOD_REG); + IA32_Mov_Reg_Rm(jit, REG_EBX, REG_EDI, MOD_REG); + IA32_Mov_Rm_Reg_Disp8(jit, REG_ESI, REG_ESP, AMX_INFO_NSTACK); /* by now, everything is set up, so we can call into the plugin */ //call ecx IA32_Call_Reg(jit, REG_ECX); /* if the code flow gets to here, there was a normal return */ - //mov ecx, [esi+8] - get retval pointer - //mov [ecx], eax - store retval from PRI - //mov eax, SP_ERROR_NONE - set no error + //mov ecx, [esi+RETVAL] ;get retval pointer + //mov [ecx], eax ;store retval from PRI + //mov eax, SP_ERROR_NONE ;set no error IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, AMX_REG_INFO, AMX_INFO_RETVAL); IA32_Mov_Rm_Reg(jit, REG_ECX, AMX_REG_PRI, MOD_MEM_REG); IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERROR_NONE); /* save where error checking/halting functions should go to */ jitoffs_t offs_return = jit->get_outputpos(); - //mov esp, esi - restore stack pointer - IA32_Mov_Reg_Rm(jit, REG_ESP, REG_ESI, MOD_REG); + //mov esp, [esi+NSTACK] ;restore stack pointer + IA32_Mov_Reg_Rm_Disp8(jit, REG_ESP, REG_ESI, AMX_INFO_NSTACK); - /* _FOR NOW_ ... - * We are going to restore SP, HP, and FRM for now. This is for - * debugging only, to check for alignment errors. As such: - * :TODO: probably remove this. - */ - //mov ecx, [esi+context] + /* Restore SP */ + //mov ecx, [esi+CONTEXT] //sub edi, ebp - //mov edx, [esi+heap] - //mov [ecx+sp], edi - //mov [ecx+hp], edx + //mov [ecx+SP], edi IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_ESI, AMX_INFO_CONTEXT); IA32_Sub_Reg_Rm(jit, REG_EDI, REG_EBP, MOD_REG); - IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_HEAP); IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDI, offsetof(sp_context_t, sp)); - IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDX, offsetof(sp_context_t, hp)); - //add esp, 4*NUM_INFO_PARAMS //pop ebx //pop edi //pop esi //pop ebp //ret - IA32_Add_Rm_Imm8(jit, REG_ESP, 4*NUM_INFO_PARAMS, MOD_REG); IA32_Pop_Reg(jit, REG_EBX); IA32_Pop_Reg(jit, REG_EDI); IA32_Pop_Reg(jit, REG_ESI); @@ -165,58 +123,22 @@ jitoffs_t Write_Execute_Function(JitWriter *jit) return offs_return; } -void Write_BreakDebug(JitWriter *jit) -{ - //push edi - //mov edi, ecx - //mov ecx, [esi+ctx] - IA32_Push_Reg(jit, REG_EDI); - IA32_Mov_Reg_Rm(jit, REG_EDI, REG_ECX, MOD_REG); - IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); - - //:TODO: align the stack to 16bytes like in sysreq.x - /* NOTE, Hack! PUSHAD pushes EDI last which still has the CIP */ - //pushad - //push [esi+frm] - //push [ctx+basectx] - //mov ecx, - //call ecx - //add esp, 8 - //popad - IA32_Pushad(jit); - IA32_Push_Rm_Disp8(jit, AMX_REG_INFO, AMX_INFO_FRAME); //:TODO: move to regs and push? and dont disp for 0 - IA32_Push_Rm_Disp8(jit, AMX_REG_TMP, offsetof(sp_context_t, vm[JITVARS_BASECTX])); - IA32_Mov_Reg_Imm32(jit, AMX_REG_TMP, jit_int32_t(((CompData *)jit->data)->plugin->dbreak)); - IA32_Call_Reg(jit, AMX_REG_TMP); - IA32_Add_Rm_Imm8(jit, REG_ESP, 4*2, MOD_REG); - IA32_Popad(jit); - - //pop edi - //ret - IA32_Pop_Reg(jit, REG_EDI); - IA32_Return(jit); -} - void Write_GetError(JitWriter *jit) { - CompData *data = (CompData *)jit->data; - //mov eax, [esi+info.context] //mov eax, [eax+ctx.error] //jmp [jit_return] IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err)); - IA32_Jump_Imm32_Abs(jit, data->jit_return); + IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint()); } void Write_SetError(JitWriter *jit, int error) { - CompData *data = (CompData *)jit->data; - //mov eax, //jmp [jit_return] IA32_Mov_Reg_Imm32(jit, REG_EAX, error); - IA32_Jump_Imm32_Abs(jit, data->jit_return); + IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint()); } void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg) @@ -224,7 +146,7 @@ void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg) //test reg, reg //jz :error IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero); + IA32_Jump_Cond_Imm32_Rel(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero); } void Write_CheckHeap_Min(JitWriter *jit) @@ -238,7 +160,7 @@ void Write_CheckHeap_Min(JitWriter *jit) //cmp [esi+info.heap], //jb :error IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size); - IA32_Jump_Cond_Imm32_Abs(jit, CC_B, data->jit_error_heapmin); + IA32_Jump_Cond_Imm32_Rel(jit, CC_B, data->jit_error_heapmin); } void Write_CheckHeap_Low(JitWriter *jit) @@ -252,7 +174,7 @@ void Write_CheckHeap_Low(JitWriter *jit) IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN); IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow); + IA32_Jump_Cond_Imm32_Rel(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow); } void Write_CheckStack_Min(JitWriter *jit) @@ -263,7 +185,7 @@ void Write_CheckStack_Min(JitWriter *jit) //cmp edi, [esi+info.stacktop] //jae :error IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP); - IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin); + IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin); } void Write_CheckStack_Low(JitWriter *jit) @@ -279,36 +201,15 @@ void Write_CheckStack_Low(JitWriter *jit) IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN); IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow); + IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow); } void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg) { - CompData *data = (CompData *)jit->data; - /* :TODO: Should this be checking for below heaplow? * The old JIT did not. */ - bool call = false; - if (!(data->inline_level & JIT_INLINE_ERRORCHECKS)) - { - /* If we're not in the initial generation phase, - * Write a call to the actual routine instead. - */ - if ((reg == REG_EAX) && data->jit_verify_addr_eax) - { - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, call, data->jit_verify_addr_eax); - return; - } else if ((reg == REG_EDX) && data->jit_verify_addr_edx) { - jitoffs_t call = IA32_Call_Imm32(jit, 0); - IA32_Write_Jump32(jit, call, data->jit_verify_addr_edx); - return; - } - call = true; - } - /** * :TODO: If we can't find a nicer way of doing this, * then scrap it on high optimizations. The second portion is not needed at all! @@ -318,7 +219,7 @@ void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg) //cmp , //jae :error IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->mem_size); - IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess); + IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess); /* Part 2: Check if we're in the invalid region between HP and SP */ jitoffs_t jmp; @@ -332,13 +233,8 @@ void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg) jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0); IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0); IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess); + IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess); IA32_Send_Jump8_Here(jit, jmp); - - if (call) - { - IA32_Return(jit); - } } void Macro_PushN_Addr(JitWriter *jit, int i) @@ -472,12 +368,7 @@ void WriteOp_Sysreq_C_Function(JitWriter *jit) //call NativeCallback IA32_Push_Reg(jit, REG_EAX); jitoffs_t call = IA32_Call_Imm32(jit, 0); - if (!data->debug) - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback); - } else { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug); - } + IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback); /* Test for error */ //mov ecx, [esi+context] @@ -485,7 +376,7 @@ void WriteOp_Sysreq_C_Function(JitWriter *jit) //jnz :error IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); + IA32_Jump_Cond_Imm32_Rel(jit, CC_NZ, data->jit_extern_error); /* restore what we damaged */ //mov esp, ebx @@ -594,6 +485,8 @@ void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _d */ void WriteIntrinsic_GenArray(JitWriter *jit) { + jitoffs_t err1, err2; + /** * save important values */ @@ -635,18 +528,18 @@ void WriteIntrinsic_GenArray(JitWriter *jit) /* Test if we have heap space for this */ //mov eax, [esi+info.heap] ;get heap pointer //lea eax, [eax+edx*4] ;new heap pointer - //cmp eax, ;compare to heap low + //cmp eax, [esi+info.datasz] ;compare to heap low //jbe :error ;die if we hit this (it should always be >) //add eax, ebp ;relocate to stack //cmp eax, edi ;die if above the stack pointer //jae :error IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP); IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4); - IA32_Cmp_Rm_Imm32(jit, MOD_REG, REG_EAX, ((CompData *)jit->data)->plugin->data_size); - IA32_Jump_Cond_Imm32_Abs(jit, CC_BE, ((CompData *)jit->data)->jit_error_array_too_big); + IA32_Cmp_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_DATASIZE); + err1 = IA32_Jump_Cond_Imm32(jit, CC_BE, 0); IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG); IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG); - IA32_Jump_Cond_Imm32_Abs(jit, CC_AE, ((CompData *)jit->data)->jit_error_array_too_big); + err2 = IA32_Jump_Cond_Imm32(jit, CC_AE, 0); /* Prepare for indirection iteration */ //mov eax, [esi+info.heap] ;get heap pointer @@ -697,107 +590,15 @@ void WriteIntrinsic_GenArray(JitWriter *jit) IA32_Pop_Reg(jit, REG_EAX); IA32_Pop_Reg(jit, REG_EBX); IA32_Return(jit); -} -void WriteOp_Sysreq_N_Function(JitWriter *jit) -{ - /* The big daddy of opcodes. - * eax - num_params - * ecx - native index - */ - CompData *data = (CompData *)jit->data; - - /* store the number of parameters on the stack */ - //mov [edi-4], eax - //sub edi, 4 - IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, REG_EAX, -4); - IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG); - - /* save registers we will need */ - //push eax ; num_params for stack popping - //push edx - IA32_Push_Reg(jit, REG_EAX); - IA32_Push_Reg(jit, AMX_REG_ALT); - - /* Align the stack to 16 bytes */ - //push ebx - //mov ebx, esp - //and esp, 0xFFFFFF0 - //sub esp, 4 - IA32_Push_Reg(jit, REG_EBX); - IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ESP, MOD_REG); - IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16); - IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG); - - /* push some callback stuff */ - //push edi ; stack - //push ecx ; native index - IA32_Push_Reg(jit, AMX_REG_STK); - IA32_Push_Reg(jit, REG_ECX); - - /* Relocate stack, heap, frm information, then store back */ - //mov eax, [esi+context] - //mov ecx, [esi+hea] - //sub edi, ebp - //mov [eax+hp], ecx - //mov ecx, [esi] - //mov [eax+sp], edi - //mov [eax+frm], ecx - IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP); - IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); - IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp)); - IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG); - IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp)); - IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm)); - - /* finally, push the last parameter and make the call */ - //push eax ; context - //call NativeCallback - IA32_Push_Reg(jit, REG_EAX); - jitoffs_t call = IA32_Call_Imm32(jit, 0); - if (!data->debug) - { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback); - } else { - IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback_Debug); - } - - /* Test for error */ - //mov ecx, [esi+context] - //cmp [ecx+err], 0 - //jnz :error - IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT); - IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_extern_error); - - /* restore what we damaged */ - //mov esp, ebx - //pop ebx - //add edi, ebp - //pop edx - //pop ecx ; num_params - IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBX, MOD_REG); - IA32_Pop_Reg(jit, REG_EBX); - IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG); - IA32_Pop_Reg(jit, AMX_REG_ALT); - IA32_Pop_Reg(jit, REG_ECX); - - /* pop the AMX stack. do not check the margins. - * Note that this is not a true macro - we don't bother to - * set ALT here because nothing will be using it. - */ - //lea edi, [edi+ecx*4+4] - IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, 4); - - //ret - IA32_Return(jit); + //:error + IA32_Send_Jump32_Here(jit, err1); + IA32_Send_Jump32_Here(jit, err2); + Write_SetError(jit, SP_ERROR_ARRAY_TOO_BIG); } void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg) { - CompData *data = (CompData *)jit->data; - /* Save registers that may be damaged by the call */ //push eax //push ecx @@ -824,7 +625,7 @@ void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg) //cmp eax, 0 //jnz :error IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0); - IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, data->jit_return); + IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, g_Jit.GetReturnPoint()); /* Restore */ //pop eax @@ -890,13 +691,6 @@ int JIT_VerifyLowBoundTracker(sp_context_t *ctx) return SP_ERROR_NONE; } -void Write_RoundingTable(JitWriter *jit) -{ - jit->write_int32(-1); - jit->write_int32(0); - jit->write_int32(1); -} - void AlignMe(JitWriter *jit) { jitoffs_t cur_offs = jit->get_outputpos(); diff --git a/sourcepawn/jit/x86/opcode_helpers.h b/sourcepawn/jit/x86/opcode_helpers.h index fbdcdbab..37d91955 100644 --- a/sourcepawn/jit/x86/opcode_helpers.h +++ b/sourcepawn/jit/x86/opcode_helpers.h @@ -44,7 +44,6 @@ jitoffs_t Write_Execute_Function(JitWriter *jit); /** * Writes the Sysreq.* opcodes as a function call. */ -void WriteOp_Sysreq_N_Function(JitWriter *jit); void WriteOp_Sysreq_C_Function(JitWriter *jit); /** @@ -52,6 +51,8 @@ void WriteOp_Sysreq_C_Function(JitWriter *jit); */ void WriteIntrinsic_GenArray(JitWriter *jit); +void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg); + /** * Generates code to set an error state in the VM and return. * This is used for generating the error set points in the VM. @@ -73,12 +74,6 @@ void Write_CheckStack_Low(JitWriter *jit); void Write_CheckHeap_Min(JitWriter *jit); void Write_CheckHeap_Low(JitWriter *jit); -/** - * Verifies an address by register. The address must reside - * between DAT and HP and SP and STP. - */ -void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg); - /** * Checks for division by zero. */ @@ -303,6 +298,7 @@ typedef enum OP_GENARRAY_Z, //-VERIFIED (not tested for 1D arrays) OP_STRADJUST_PRI, //VERIFIED OP_STACKADJUST, //:TODO: VERIFY + OP_ENDPROC, //VERIFIED OP_FABS, //VERIFIED OP_FLOAT, //VERIFIED OP_FLOATADD, //VERIFIED diff --git a/sourcepawn/jit/x86/opcode_switch.inc b/sourcepawn/jit/x86/opcode_switch.inc index afb16ef8..9eb759fc 100644 --- a/sourcepawn/jit/x86/opcode_switch.inc +++ b/sourcepawn/jit/x86/opcode_switch.inc @@ -676,12 +676,7 @@ } case OP_SYSREQ_N: { - if (data->inline_level & JIT_INLINE_NATIVES) - { - WriteOp_Sysreq_N(jit); - } else { - WriteOp_Sysreq_N_NoInline(jit); - } + WriteOp_Sysreq_N(jit); break; } case OP_TRACKER_PUSH_C: @@ -769,6 +764,11 @@ WriteOp_StackAdjust(jit); break; } + case OP_ENDPROC: + { + WriteOp_EndProc(jit); + break; + } #if defined USE_UNGEN_OPCODES #include "ungen_opcode_switch.inc" #endif