sourcepawn/jit/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
parent 2195 5b70179559f9
child 2228 83a7d6b44e3c
permissions -rw-r--r--
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.
dvander@2193
     1
/**
dvander@2193
     2
 * vim: set ts=4 :
dvander@2193
     3
 * =============================================================================
dvander@2193
     4
 * SourcePawn
dvander@2193
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2193
     6
 * =============================================================================
dvander@2193
     7
 *
dvander@2193
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2193
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2193
    10
 * Free Software Foundation.
dvander@2193
    11
 * 
dvander@2193
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2193
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2193
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2193
    15
 * details.
dvander@2193
    16
 *
dvander@2193
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2193
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2193
    19
 *
dvander@2193
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2193
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2193
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2193
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2193
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2193
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2193
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2193
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2193
    28
 *
dvander@2193
    29
 * Version: $Id$
dvander@2193
    30
 */
dvander@2193
    31
dvander@2193
    32
#include "sp_vm_types.h"
dvander@2193
    33
#include <malloc.h>
dvander@2193
    34
#include <string.h>
dvander@2193
    35
#include <assert.h>
dvander@2226
    36
#include <KeCodeAllocator.h>
dvander@2193
    37
#include "sp_file_headers.h"
dvander@2193
    38
#include "sp_vm_engine.h"
dvander@2193
    39
#include "zlib/zlib.h"
dvander@2193
    40
#include "sp_vm_basecontext.h"
dvander@2226
    41
#include "jit_x86.h"
dvander@2226
    42
#if defined __GNUC__
dvander@2226
    43
#include <unistd.h>
dvander@2226
    44
#endif
dvander@2193
    45
dvander@2193
    46
SourcePawnEngine g_engine1;
dvander@2193
    47
dvander@2193
    48
#if defined WIN32
dvander@2193
    49
 #define WIN32_LEAN_AND_MEAN
dvander@2193
    50
 #include <windows.h>
dvander@2193
    51
#elif defined __GNUC__
dvander@2193
    52
 #include <sys/mman.h>
dvander@2193
    53
#endif
dvander@2193
    54
dvander@2193
    55
using namespace SourcePawn;
dvander@2193
    56
dvander@2193
    57
#define ERROR_MESSAGE_MAX		25
dvander@2193
    58
static const char *g_ErrorMsgTable[] = 
dvander@2193
    59
{
dvander@2193
    60
	NULL,
dvander@2193
    61
	"Unrecognizable file format",
dvander@2193
    62
	"Decompressor was not found",
dvander@2193
    63
	"Not enough space on the heap",
dvander@2193
    64
	"Invalid parameter or parameter type",
dvander@2193
    65
	"Invalid plugin address",
dvander@2193
    66
	"Object or index not found",
dvander@2193
    67
	"Invalid index or index not found",
dvander@2193
    68
	"Not enough space on the stack",
dvander@2193
    69
	"Debug section not found or debug not enabled",
dvander@2193
    70
	"Invalid instruction",
dvander@2193
    71
	"Invalid memory access",
dvander@2193
    72
	"Stack went below stack boundary",
dvander@2193
    73
	"Heap went below heap boundary",
dvander@2193
    74
	"Divide by zero",
dvander@2193
    75
	"Array index is out of bounds",
dvander@2193
    76
	"Instruction contained invalid parameter",
dvander@2193
    77
	"Stack memory leaked by native",
dvander@2193
    78
	"Heap memory leaked by native",
dvander@2193
    79
	"Dynamic array is too big",
dvander@2193
    80
	"Tracker stack is out of bounds",
dvander@2193
    81
	"Native is not bound",
dvander@2193
    82
	"Maximum number of parameters reached",
dvander@2193
    83
	"Native detected error",
dvander@2193
    84
	"Plugin not runnable",
dvander@2193
    85
	"Call was aborted",
dvander@2226
    86
	"Plugin format is too old",
dvander@2226
    87
	"Plugin format is too new",
dvander@2193
    88
};
dvander@2193
    89
dvander@2193
    90
const char *SourcePawnEngine::GetErrorString(int error)
dvander@2193
    91
{
dvander@2193
    92
	if (error < 1 || error > ERROR_MESSAGE_MAX)
dvander@2193
    93
	{
dvander@2193
    94
		return NULL;
dvander@2193
    95
	}
dvander@2193
    96
dvander@2193
    97
	return g_ErrorMsgTable[error];
dvander@2193
    98
}
dvander@2193
    99
dvander@2193
   100
SourcePawnEngine::SourcePawnEngine()
dvander@2193
   101
{
dvander@2193
   102
	m_pDebugHook = NULL;
dvander@2193
   103
}
dvander@2193
   104
dvander@2193
   105
SourcePawnEngine::~SourcePawnEngine()
dvander@2193
   106
{
dvander@2193
   107
}
dvander@2193
   108
dvander@2193
   109
void *SourcePawnEngine::ExecAlloc(size_t size)
dvander@2193
   110
{
dvander@2193
   111
#if defined WIN32
dvander@2193
   112
	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dvander@2193
   113
#elif defined __GNUC__
dvander@2193
   114
	void *base = memalign(sysconf(_SC_PAGESIZE), size);
dvander@2193
   115
	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
dvander@2193
   116
	{
dvander@2193
   117
		free(base);
dvander@2193
   118
		return NULL;
dvander@2193
   119
	}
dvander@2193
   120
	return base;
dvander@2193
   121
#endif
dvander@2193
   122
}
dvander@2193
   123
dvander@2193
   124
void *SourcePawnEngine::AllocatePageMemory(size_t size)
dvander@2193
   125
{
dvander@2226
   126
	return g_Jit.AllocCode(size);
dvander@2193
   127
}
dvander@2193
   128
dvander@2193
   129
void SourcePawnEngine::SetReadExecute(void *ptr)
dvander@2193
   130
{
dvander@2226
   131
	/* already re */
dvander@2193
   132
}
dvander@2193
   133
dvander@2193
   134
void SourcePawnEngine::SetReadWrite(void *ptr)
dvander@2193
   135
{
dvander@2226
   136
	/* already rw */
dvander@2193
   137
}
dvander@2193
   138
dvander@2193
   139
void SourcePawnEngine::FreePageMemory(void *ptr)
dvander@2193
   140
{
dvander@2226
   141
	g_Jit.FreeCode(ptr);
dvander@2193
   142
}
dvander@2193
   143
dvander@2193
   144
void SourcePawnEngine::ExecFree(void *address)
dvander@2193
   145
{
dvander@2193
   146
#if defined WIN32
dvander@2193
   147
	VirtualFree(address, 0, MEM_RELEASE);
dvander@2193
   148
#elif defined __GNUC__
dvander@2193
   149
	free(address);
dvander@2193
   150
#endif
dvander@2193
   151
}
dvander@2193
   152
dvander@2226
   153
void SourcePawnEngine::SetReadWriteExecute(void *ptr)
dvander@2226
   154
{
dvander@2226
   155
//:TODO:	g_ExeMemory.SetRWE(ptr);
dvander@2226
   156
	SetReadExecute(ptr);
dvander@2226
   157
}
dvander@2226
   158
dvander@2193
   159
void *SourcePawnEngine::BaseAlloc(size_t size)
dvander@2193
   160
{
dvander@2193
   161
	return malloc(size);
dvander@2193
   162
}
dvander@2193
   163
dvander@2193
   164
void SourcePawnEngine::BaseFree(void *memory)
dvander@2193
   165
{
dvander@2193
   166
	free(memory);
dvander@2193
   167
}
dvander@2193
   168
dvander@2193
   169
sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
dvander@2193
   170
{
dvander@2193
   171
	if (err != NULL)
dvander@2193
   172
	{
dvander@2193
   173
		*err = SP_ERROR_ABORTED;
dvander@2193
   174
	}
dvander@2193
   175
dvander@2193
   176
	return NULL;
dvander@2193
   177
}
dvander@2193
   178
dvander@2193
   179
sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
dvander@2193
   180
{
dvander@2193
   181
	if (err != NULL)
dvander@2193
   182
	{
dvander@2193
   183
		*err = SP_ERROR_ABORTED;
dvander@2193
   184
	}
dvander@2193
   185
dvander@2193
   186
	return NULL;
dvander@2193
   187
}
dvander@2193
   188
dvander@2193
   189
int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
dvander@2193
   190
{
dvander@2193
   191
	return SP_ERROR_ABORTED;
dvander@2193
   192
}
dvander@2193
   193
dvander@2193
   194
IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
dvander@2193
   195
{
dvander@2193
   196
	IDebugListener *old = m_pDebugHook;
dvander@2193
   197
dvander@2193
   198
	m_pDebugHook = pListener;
dvander@2193
   199
dvander@2193
   200
	return old;
dvander@2193
   201
}
dvander@2193
   202
dvander@2193
   203
unsigned int SourcePawnEngine::GetEngineAPIVersion()
dvander@2193
   204
{
dvander@2193
   205
	return SOURCEPAWN_ENGINE_API_VERSION;
dvander@2193
   206
}
dvander@2193
   207
dvander@2226
   208
unsigned int SourcePawnEngine::GetContextCallCount()
dvander@2226
   209
{
dvander@2226
   210
	return 0;
dvander@2226
   211
}
dvander@2226
   212
dvander@2226
   213
void SourcePawnEngine::ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start)
dvander@2193
   214
{
dvander@2226
   215
	if (m_pDebugHook == NULL)
dvander@2226
   216
	{
dvander@2226
   217
		return;
dvander@2226
   218
	}
dvander@2226
   219
dvander@2226
   220
	CContextTrace trace(runtime, err, errstr, rp_start);
dvander@2226
   221
dvander@2226
   222
	m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
dvander@2226
   223
}
dvander@2226
   224
dvander@2226
   225
CContextTrace::CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) 
dvander@2226
   226
: m_pRuntime(pRuntime), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0)
dvander@2226
   227
{
dvander@2226
   228
	m_ctx = pRuntime->m_pCtx->GetCtx();
dvander@2226
   229
	m_pDebug = m_pRuntime->GetDebugInfo();
dvander@2193
   230
}
dvander@2193
   231
dvander@2193
   232
bool CContextTrace::DebugInfoAvailable()
dvander@2193
   233
{
dvander@2226
   234
	return (m_pDebug != NULL);
dvander@2193
   235
}
dvander@2193
   236
dvander@2193
   237
const char *CContextTrace::GetCustomErrorString()
dvander@2193
   238
{
dvander@2193
   239
	return m_pMsg;
dvander@2193
   240
}
dvander@2193
   241
dvander@2193
   242
int CContextTrace::GetErrorCode()
dvander@2193
   243
{
dvander@2193
   244
	return m_Error;
dvander@2193
   245
}
dvander@2193
   246
dvander@2193
   247
const char *CContextTrace::GetErrorString()
dvander@2193
   248
{
dvander@2226
   249
	if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1)
dvander@2193
   250
	{
dvander@2193
   251
		return "Invalid error code";
dvander@2226
   252
	}
dvander@2226
   253
	else
dvander@2226
   254
	{
dvander@2193
   255
		return g_ErrorMsgTable[m_Error];
dvander@2193
   256
	}
dvander@2193
   257
}
dvander@2193
   258
dvander@2193
   259
void CContextTrace::ResetTrace()
dvander@2193
   260
{
dvander@2226
   261
	m_Level = 0;
dvander@2193
   262
}
dvander@2193
   263
dvander@2193
   264
bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
dvander@2193
   265
{
dvander@2226
   266
	cell_t cip;
dvander@2226
   267
dvander@2226
   268
	if (m_Level == 0)
dvander@2193
   269
	{
dvander@2226
   270
		cip = m_ctx->err_cip;
dvander@2193
   271
	}
dvander@2226
   272
	else if (m_ctx->rp > 0)
dvander@2193
   273
	{
dvander@2226
   274
		/* Entries go from ctx.rp - 1 to m_StartRp */
dvander@2226
   275
		cell_t offs, start, end;
dvander@2226
   276
dvander@2226
   277
		offs = m_Level - 1;
dvander@2226
   278
		start = m_ctx->rp - 1;
dvander@2226
   279
		end = m_StartRp;
dvander@2193
   280
dvander@2226
   281
		if (start - offs < end)
dvander@2226
   282
		{
dvander@2226
   283
			return false;
dvander@2226
   284
		}
dvander@2193
   285
dvander@2226
   286
		cip = m_ctx->rstk_cips[start - offs];
dvander@2226
   287
	}
dvander@2226
   288
	else
dvander@2193
   289
	{
dvander@2193
   290
		return false;
dvander@2193
   291
	}
dvander@2193
   292
dvander@2226
   293
	if (trace == NULL)
dvander@2193
   294
	{
dvander@2226
   295
		m_Level++;
dvander@2193
   296
		return true;
dvander@2193
   297
	}
dvander@2193
   298
dvander@2226
   299
	if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE)
dvander@2193
   300
	{
dvander@2193
   301
		trace->filename = NULL;
dvander@2193
   302
	}
dvander@2193
   303
dvander@2226
   304
	if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE)
dvander@2193
   305
	{
dvander@2193
   306
		trace->function = NULL;
dvander@2193
   307
	}
dvander@2193
   308
dvander@2226
   309
	if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE)
dvander@2193
   310
	{
dvander@2193
   311
		trace->line = 0;
dvander@2193
   312
	}
dvander@2193
   313
dvander@2226
   314
	m_Level++;
dvander@2193
   315
dvander@2193
   316
	return true;
dvander@2193
   317
}
dvander@2193
   318
dvander@2193
   319
const char *CContextTrace::GetLastNative(uint32_t *index)
dvander@2193
   320
{
dvander@2226
   321
	if (m_ctx->n_err == SP_ERROR_NONE)
dvander@2193
   322
	{
dvander@2193
   323
		return NULL;
dvander@2193
   324
	}
dvander@2193
   325
dvander@2193
   326
	sp_native_t *native;
dvander@2226
   327
	if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
dvander@2193
   328
	{
dvander@2193
   329
		return NULL;
dvander@2193
   330
	}
dvander@2193
   331
dvander@2193
   332
	if (index)
dvander@2193
   333
	{
dvander@2226
   334
		*index = m_ctx->n_idx;
dvander@2193
   335
	}
dvander@2193
   336
dvander@2193
   337
	return native->name;
dvander@2193
   338
}
dvander@2226
   339
dvander@2226
   340
IDebugListener *SourcePawnEngine::GetDebugHook()
dvander@2226
   341
{
dvander@2226
   342
	return m_pDebugHook;
dvander@2226
   343
}
dvander@2226
   344