Landed sourcepawn-1.2. The big changes:

1) JIT compilation/optimization now occurs per-function, and only when functions are first used.  We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT).  This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive.  If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.

2) I've removed debug mode.  We always show full backtraces now, as there was a very cheap way to implement this which really cleaned up everything.  This is great for a number of reasons -- there's less code, the JIT is better designed, we don't need to relocate debug tables, and best of all we no longer have to tell users to enable debug mode at their own expense.

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402459
This commit is contained in:
David Anderson 2008-08-15 05:22:26 +00:00
parent 95aca7b61b
commit 7875fe1acd
49 changed files with 3475 additions and 1724 deletions

View File

@ -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;

View File

@ -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:

View File

@ -2,6 +2,10 @@
#include "ShareSys.h"
#include "PluginSys.h"
CNativeOwner::CNativeOwner() : m_nMarkSerial(0)
{
}
void CNativeOwner::SetMarkSerial(unsigned int serial)
{
m_nMarkSerial = serial;

View File

@ -30,6 +30,8 @@ using namespace SourceHook;
class CNativeOwner
{
public:
CNativeOwner();
public:
virtual void DropEverything();
public:

View File

@ -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);

View File

@ -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<CPlugin *>::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;
}

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -0,0 +1,428 @@
#include <KePlatform.h>
#include <assert.h>
#include <string.h>
#if defined KE_PLATFORM_WINDOWS
#include <windows.h>
#elif defined KE_PLATFORM_POSIX
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#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 = &region->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 = &region->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 = &region->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;
}

View File

@ -0,0 +1,47 @@
#ifndef _INCLUDE_KNIGHT_KE_CODE_ALLOCATOR_H_
#define _INCLUDE_KNIGHT_KE_CODE_ALLOCATOR_H_
#include <KeLinking.h>
#include <KeStdTypes.h>
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_

View File

@ -0,0 +1,33 @@
#include <stdio.h>
#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;
}
}

36
knight/shared/KeCommon.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef _INCLUDE_KNIGHT_KE_COMMON_UTILS_H_
#define _INCLUDE_KNIGHT_KE_COMMON_UTILS_H_
#include <stddef.h>
#include <stdarg.h>
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_

View File

@ -0,0 +1,409 @@
#include <KeHashTable.h>
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;
}

128
knight/shared/KeHashTable.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef _INCLUDE_KNIGHT_KE_HASHTABLE_H_
#define _INCLUDE_KNIGHT_KE_HASHTABLE_H_
#include <KeStdTypes.h>
#include <KnightAllocator.h>
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_

12
knight/shared/KeLinking.h Normal file
View File

@ -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_

View File

@ -0,0 +1,258 @@
#include "KePlatform.h"
#include <KeLumpAllocator.h>
#include <KeVector.h>
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();
}

View File

@ -0,0 +1,36 @@
#ifndef _INCLUDE_KNIGHT_KE_LUMP_ALLOCATOR_H_
#define _INCLUDE_KNIGHT_KE_LUMP_ALLOCATOR_H_
#include <KeLinking.h>
#include <KnightAllocator.h>
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_

View File

@ -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;
}

View File

@ -0,0 +1,49 @@
#ifndef _INCLUDE_KNIGHT_KE_PAGE_ALLOCATOR_H_
#define _INCLUDE_KNIGHT_KE_PAGE_ALLOCATOR_H_
#include <KeStdTypes.h>
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_

View File

@ -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 <windows.h>
#include <malloc.h>
#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_

View File

@ -0,0 +1,162 @@
#ifndef _INCLUDE_KNIGHT_KE_SECTOR_STACK_H_
#define _INCLUDE_KNIGHT_KE_SECTOR_STACK_H_
#include <KnightAllocator.h>
#include <KeVector.h>
namespace Knight
{
template <class T>
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<T *> 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_

View File

@ -0,0 +1,24 @@
#ifndef _INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_
#define _INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_
#include <KePlatform.h>
#include <stddef.h>
#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 <stdint.h>
#endif
#endif //_INCLUDE_KNIGHT_KE_STANDARD_TYPES_H_

158
knight/shared/KeVector.h Normal file
View File

@ -0,0 +1,158 @@
#ifndef _INCLUDE_KNIGHT_KE_VECTOR_H_
#define _INCLUDE_KNIGHT_KE_VECTOR_H_
#include <new>
namespace Knight
{
template <class T>
class KeVector
{
public:
KeVector<T>() : m_Data(NULL), m_Size(0), m_CurrentUsedSize(0)
{
}
KeVector<T>(const KeVector<T> & 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<T>()
{
clear();
}
KeVector & operator =(const KeVector<T> & 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_

View File

@ -0,0 +1,37 @@
#ifndef _INCLUDE_KNIGHT_ALLOCATOR_H_
#define _INCLUDE_KNIGHT_ALLOCATOR_H_
#include <stddef.h>
#include <stdlib.h>
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 <typename T>
void ke_destroy(ke_allocator_t *alloc, T * data)
{
data->~T();
alloc->dealloc(alloc, data);
}
#endif //_INCLUDE_KNIGHT_ALLOCATOR_H_

View File

@ -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.

View File

@ -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);

View File

@ -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();

View File

@ -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.
*/

View File

@ -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;
};
};

View File

@ -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

View File

@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#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; i<m_pPlugin->info.natives_num; i++)
for (uint32_t i=0; i<m_pPlugin->num_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;
}

View File

@ -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_

View File

@ -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)

View File

@ -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();
}

View File

@ -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:

View File

@ -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;
}

View File

@ -0,0 +1,19 @@
#ifndef _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_
#define _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_
#include <sp_vm_types.h>
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_

View File

@ -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_

View File

@ -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_

View File

@ -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; iter<max; iter++)
max = m_pPlugin->debug.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;
}
}

View File

@ -30,23 +30,18 @@
*/
#include "sp_vm_types.h"
#include <sh_memory.h>
/* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
#if !defined SH_ASSERT
#define SH_ASSERT(x, info)
#include <sh_pagealloc.h>
#undef SH_ASSERT
#else
#include <sh_pagealloc.h>
#endif
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include <KeCodeAllocator.h>
#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 <unistd.h>
#endif
SourcePawnEngine g_engine1;
@ -57,12 +52,8 @@ SourcePawnEngine g_engine1;
#include <sys/mman.h>
#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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
};

File diff suppressed because it is too large Load Diff

View File

@ -34,9 +34,11 @@
#include <sp_vm_types.h>
#include <sp_vm_api.h>
#include <jit_helpers.h>
#include "../jit_shared.h"
#include "../BaseRuntime.h"
#include <KeCodeAllocator.h>
#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_

View File

@ -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+<offs>] - get heap pointer
//mov [esi+4], ecx - store heap into info pointer
//mov ebp, <addr> - 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+<offs>] - 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+<offs>] - 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, <addr of addr of code>
//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, <dbreak>
//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, <error>
//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], <heaplow>
//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 <reg>, <stpu>
//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, <hlw> ;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();

View File

@ -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

View File

@ -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