sourcemod/sourcepawn/jit/BaseRuntime.cpp
David Anderson 7875fe1acd 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
2008-08-15 05:22:26 +00:00

525 lines
11 KiB
C++

#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"
#include "sp_vm_basecontext.h"
#include "engine2.h"
using namespace SourcePawn;
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)
{
memset(&m_plugin, 0, sizeof(m_plugin));
m_FuncCache = NULL;
m_MaxFuncs = 0;
m_NumFuncs = 0;
}
BaseRuntime::~BaseRuntime()
{
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;
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;
free(m_pPlugin->name);
}
static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params)
{
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->num_natives - 1;
for (uint32_t i=0; i<m_pPlugin->num_natives; i++)
{
if (strcmp(m_pPlugin->natives[i].name, name) == 0)
{
if (index)
{
*index = i;
}
return SP_ERROR_NONE;
}
}
return SP_ERROR_NOT_FOUND;
}
int BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
{
if (index >= m_pPlugin->num_natives)
{
return SP_ERROR_INDEX;
}
if (native)
{
*native = &(m_pPlugin->natives[index]);
}
return SP_ERROR_NONE;
}
uint32_t BaseRuntime::GetNativesNum()
{
return m_pPlugin->num_natives;
}
int BaseRuntime::FindPublicByName(const char *name, uint32_t *index)
{
int diff, high, low;
uint32_t mid;
high = m_pPlugin->num_publics - 1;
low = 0;
while (low <= high)
{
mid = (low + high) / 2;
diff = strcmp(m_pPlugin->publics[mid].name, name);
if (diff == 0)
{
if (index)
{
*index = mid;
}
return SP_ERROR_NONE;
} else if (diff < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return SP_ERROR_NOT_FOUND;
}
int BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
{
if (index >= m_pPlugin->num_publics)
{
return SP_ERROR_INDEX;
}
if (pblic)
{
*pblic = &(m_pPlugin->publics[index]);
}
return SP_ERROR_NONE;
}
uint32_t BaseRuntime::GetPublicsNum()
{
return m_pPlugin->num_publics;
}
int BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
{
if (index >= m_pPlugin->num_pubvars)
{
return SP_ERROR_INDEX;
}
if (pubvar)
{
*pubvar = &(m_pPlugin->pubvars[index]);
}
return SP_ERROR_NONE;
}
int BaseRuntime::FindPubvarByName(const char *name, uint32_t *index)
{
int diff, high, low;
uint32_t mid;
high = m_pPlugin->num_pubvars - 1;
low = 0;
while (low <= high)
{
mid = (low + high) / 2;
diff = strcmp(m_pPlugin->pubvars[mid].name, name);
if (diff == 0)
{
if (index)
{
*index = mid;
}
return SP_ERROR_NONE;
}
else if (diff < 0)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return SP_ERROR_NOT_FOUND;
}
int BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
{
if (index >= m_pPlugin->num_pubvars)
{
return SP_ERROR_INDEX;
}
*local_addr = (uint8_t *)m_pPlugin->pubvars[index].offs - m_pPlugin->memory;
*phys_addr = m_pPlugin->pubvars[index].offs;
return SP_ERROR_NONE;
}
uint32_t BaseRuntime::GetPubVarsNum()
{
return m_pPlugin->num_pubvars;
}
IPluginContext *BaseRuntime::GetDefaultContext()
{
return m_pCtx;
}
IPluginDebugInfo *BaseRuntime::GetDebugInfo()
{
return &m_Debug;
}
IPluginFunction *BaseRuntime::GetFunctionById(funcid_t func_id)
{
CFunction *pFunc = NULL;
if (func_id & 1)
{
func_id >>= 1;
if (func_id >= m_pPlugin->num_publics)
{
return NULL;
}
pFunc = m_PubFuncs[func_id];
if (!pFunc)
{
m_PubFuncs[func_id] = new CFunction(this,
(func_id << 1) | 1,
func_id);
pFunc = m_PubFuncs[func_id];
}
}
return pFunc;
}
IPluginFunction *BaseRuntime::GetFunctionByName(const char *public_name)
{
uint32_t index;
if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
{
return NULL;
}
CFunction *pFunc = m_PubFuncs[index];
if (!pFunc)
{
sp_public_t *pub = NULL;
GetPublicByIndex(index, &pub);
if (pub)
{
m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
}
pFunc = m_PubFuncs[index];
}
return pFunc;
}
bool BaseRuntime::IsDebugging()
{
return true;
}
void BaseRuntime::SetPauseState(bool paused)
{
if (paused)
{
m_pPlugin->run_flags |= SPFLAG_PLUGIN_PAUSED;
}
else
{
m_pPlugin->run_flags &= ~SPFLAG_PLUGIN_PAUSED;
}
}
bool BaseRuntime::IsPaused()
{
return ((m_pPlugin->run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED);
}
size_t BaseRuntime::GetMemUsage()
{
size_t mem = 0;
mem += sizeof(this);
mem += sizeof(sp_plugin_t);
mem += sizeof(BaseContext);
mem += m_pPlugin->base_size;
return mem;
}
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;
}