sourcepawn/jit/sp_vm_function.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 15 05:22:26 2008 +0000 (2008-08-15)
changeset 2226 d10b38524be3
parent 2193 db7c6362f3f2
child 2825 811ec7a539c3
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 <stdio.h>
dvander@2193
    33
#include <string.h>
dvander@2193
    34
#include "sp_vm_function.h"
dvander@2193
    35
#include "BaseRuntime.h"
dvander@2193
    36
dvander@2193
    37
/********************
dvander@2193
    38
* FUNCTION CALLING *
dvander@2193
    39
********************/
dvander@2193
    40
dvander@2226
    41
void CFunction::Set(BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id)
dvander@2193
    42
{
dvander@2193
    43
	m_pRuntime = runtime;
dvander@2193
    44
	m_curparam = 0;
dvander@2193
    45
	m_errorstate = SP_ERROR_NONE;
dvander@2193
    46
	m_FnId = fnid;
dvander@2193
    47
}
dvander@2193
    48
dvander@2193
    49
bool CFunction::IsRunnable()
dvander@2193
    50
{
dvander@2193
    51
	return !m_pRuntime->IsPaused();
dvander@2193
    52
}
dvander@2193
    53
dvander@2193
    54
int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
dvander@2193
    55
{
dvander@2193
    56
	return CallFunction2(m_pRuntime->GetDefaultContext(), params, num_params, result);
dvander@2193
    57
}
dvander@2193
    58
dvander@2193
    59
int CFunction::CallFunction2(IPluginContext *pContext, const cell_t *params, unsigned int num_params, cell_t *result)
dvander@2193
    60
{
dvander@2193
    61
	return pContext->Execute2(this, params, num_params, result);
dvander@2193
    62
}
dvander@2193
    63
dvander@2193
    64
IPluginContext *CFunction::GetParentContext()
dvander@2193
    65
{
dvander@2193
    66
	return m_pRuntime->GetDefaultContext();
dvander@2193
    67
}
dvander@2193
    68
dvander@2226
    69
CFunction::CFunction(BaseRuntime *runtime, funcid_t id, uint32_t pub_id) : 
dvander@2226
    70
	m_curparam(0), m_errorstate(SP_ERROR_NONE), m_FnId(id)
dvander@2193
    71
{
dvander@2193
    72
	m_pRuntime = runtime;
dvander@2193
    73
}
dvander@2193
    74
dvander@2193
    75
int CFunction::PushCell(cell_t cell)
dvander@2193
    76
{
dvander@2193
    77
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2193
    78
	{
dvander@2193
    79
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2193
    80
	}
dvander@2193
    81
dvander@2193
    82
	m_info[m_curparam].marked = false;
dvander@2193
    83
	m_params[m_curparam] = cell;
dvander@2193
    84
	m_curparam++;
dvander@2193
    85
dvander@2193
    86
	return SP_ERROR_NONE;
dvander@2193
    87
}
dvander@2193
    88
dvander@2193
    89
int CFunction::PushCellByRef(cell_t *cell, int flags)
dvander@2193
    90
{
dvander@2193
    91
	return PushArray(cell, 1, flags);
dvander@2193
    92
}
dvander@2193
    93
dvander@2193
    94
int CFunction::PushFloat(float number)
dvander@2193
    95
{
dvander@2193
    96
	cell_t val = *(cell_t *)&number;
dvander@2193
    97
dvander@2193
    98
	return PushCell(val);
dvander@2193
    99
}
dvander@2193
   100
dvander@2193
   101
int CFunction::PushFloatByRef(float *number, int flags)
dvander@2193
   102
{
dvander@2193
   103
	return PushCellByRef((cell_t *)number, flags);
dvander@2193
   104
}
dvander@2193
   105
dvander@2193
   106
int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
dvander@2193
   107
{
dvander@2193
   108
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2193
   109
	{
dvander@2193
   110
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2193
   111
	}
dvander@2193
   112
dvander@2193
   113
	ParamInfo *info = &m_info[m_curparam];
dvander@2193
   114
dvander@2193
   115
	info->flags = inarray ? copyback : 0;
dvander@2193
   116
	info->marked = true;
dvander@2193
   117
	info->size = cells;
dvander@2193
   118
	info->str.is_sz = false;
dvander@2193
   119
	info->orig_addr = inarray;
dvander@2193
   120
dvander@2193
   121
	m_curparam++;
dvander@2193
   122
dvander@2193
   123
	return SP_ERROR_NONE;
dvander@2193
   124
}
dvander@2193
   125
dvander@2193
   126
int CFunction::PushString(const char *string)
dvander@2193
   127
{
dvander@2193
   128
	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
dvander@2193
   129
}
dvander@2193
   130
dvander@2193
   131
int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
dvander@2193
   132
{
dvander@2193
   133
	return _PushString(buffer, sz_flags, cp_flags, length);
dvander@2193
   134
}
dvander@2193
   135
dvander@2193
   136
int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
dvander@2193
   137
{
dvander@2193
   138
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2193
   139
	{
dvander@2193
   140
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2193
   141
	}
dvander@2193
   142
dvander@2193
   143
	ParamInfo *info = &m_info[m_curparam];
dvander@2193
   144
dvander@2193
   145
	info->marked = true;
dvander@2193
   146
	info->orig_addr = (cell_t *)string;
dvander@2193
   147
	info->flags = cp_flags;
dvander@2193
   148
	info->size = len;
dvander@2193
   149
	info->str.sz_flags = sz_flags;
dvander@2193
   150
	info->str.is_sz = true;
dvander@2193
   151
dvander@2193
   152
	m_curparam++;
dvander@2193
   153
dvander@2193
   154
	return SP_ERROR_NONE;
dvander@2193
   155
}
dvander@2193
   156
dvander@2193
   157
void CFunction::Cancel()
dvander@2193
   158
{
dvander@2193
   159
	if (!m_curparam)
dvander@2193
   160
	{
dvander@2193
   161
		return;
dvander@2193
   162
	}
dvander@2193
   163
dvander@2193
   164
	m_errorstate = SP_ERROR_NONE;
dvander@2193
   165
	m_curparam = 0;
dvander@2193
   166
}
dvander@2193
   167
dvander@2193
   168
int CFunction::Execute(cell_t *result)
dvander@2193
   169
{
dvander@2193
   170
	return Execute2(m_pRuntime->GetDefaultContext(), result);
dvander@2193
   171
}
dvander@2193
   172
dvander@2193
   173
int CFunction::Execute2(IPluginContext *ctx, cell_t *result)
dvander@2193
   174
{
dvander@2193
   175
	int err = SP_ERROR_NONE;
dvander@2193
   176
dvander@2193
   177
	if (!IsRunnable())
dvander@2193
   178
	{
dvander@2193
   179
		m_errorstate = SP_ERROR_NOT_RUNNABLE;
dvander@2193
   180
	}
dvander@2193
   181
dvander@2193
   182
	if (m_errorstate != SP_ERROR_NONE)
dvander@2193
   183
	{
dvander@2193
   184
		err = m_errorstate;
dvander@2193
   185
		Cancel();
dvander@2193
   186
		return err;
dvander@2193
   187
	}
dvander@2193
   188
dvander@2193
   189
	//This is for re-entrancy!
dvander@2193
   190
	cell_t temp_params[SP_MAX_EXEC_PARAMS];
dvander@2193
   191
	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
dvander@2193
   192
	unsigned int numparams = m_curparam;
dvander@2193
   193
	unsigned int i;
dvander@2193
   194
	bool docopies = true;
dvander@2193
   195
dvander@2193
   196
	if (numparams)
dvander@2193
   197
	{
dvander@2193
   198
		//Save the info locally, then reset it for re-entrant calls.
dvander@2193
   199
		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
dvander@2193
   200
	}
dvander@2193
   201
	m_curparam = 0;
dvander@2193
   202
dvander@2193
   203
	/* Browse the parameters and build arrays */
dvander@2193
   204
	for (i=0; i<numparams; i++)
dvander@2193
   205
	{
dvander@2193
   206
		/* Is this marked as an array? */
dvander@2193
   207
		if (temp_info[i].marked)
dvander@2193
   208
		{
dvander@2193
   209
			if (!temp_info[i].str.is_sz)
dvander@2193
   210
			{
dvander@2193
   211
				/* Allocate a normal/generic array */
dvander@2193
   212
				if ((err=ctx->HeapAlloc(temp_info[i].size, 
dvander@2193
   213
										   &(temp_info[i].local_addr),
dvander@2193
   214
										   &(temp_info[i].phys_addr)))
dvander@2193
   215
					!= SP_ERROR_NONE)
dvander@2193
   216
				{
dvander@2193
   217
					break;
dvander@2193
   218
				}
dvander@2193
   219
				if (temp_info[i].orig_addr)
dvander@2193
   220
				{
dvander@2193
   221
					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
dvander@2193
   222
				}
dvander@2193
   223
			}
dvander@2193
   224
			else
dvander@2193
   225
			{
dvander@2193
   226
				/* Calculate cells required for the string */
dvander@2193
   227
				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
dvander@2193
   228
dvander@2193
   229
				/* Allocate the buffer */
dvander@2193
   230
				if ((err=ctx->HeapAlloc(cells,
dvander@2193
   231
										&(temp_info[i].local_addr),
dvander@2193
   232
										&(temp_info[i].phys_addr)))
dvander@2193
   233
					!= SP_ERROR_NONE)
dvander@2193
   234
				{
dvander@2193
   235
					break;
dvander@2193
   236
				}
dvander@2193
   237
				/* Copy original string if necessary */
dvander@2193
   238
				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
dvander@2193
   239
				{
dvander@2193
   240
					/* Cut off UTF-8 properly */
dvander@2193
   241
					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
dvander@2193
   242
					{
dvander@2193
   243
						if ((err=ctx->StringToLocalUTF8(temp_info[i].local_addr, 
dvander@2193
   244
															temp_info[i].size, 
dvander@2193
   245
															(const char *)temp_info[i].orig_addr,
dvander@2193
   246
															NULL))
dvander@2193
   247
							!= SP_ERROR_NONE)
dvander@2193
   248
						{
dvander@2193
   249
							break;
dvander@2193
   250
						}
dvander@2193
   251
					}
dvander@2193
   252
					/* Copy a binary blob */
dvander@2193
   253
					else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
dvander@2193
   254
					{
dvander@2193
   255
						memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
dvander@2193
   256
					}
dvander@2193
   257
					/* Copy ASCII characters */
dvander@2193
   258
					else
dvander@2193
   259
					{
dvander@2193
   260
						if ((err=ctx->StringToLocal(temp_info[i].local_addr,
dvander@2193
   261
														temp_info[i].size,
dvander@2193
   262
														(const char *)temp_info[i].orig_addr))
dvander@2193
   263
							!= SP_ERROR_NONE)
dvander@2193
   264
						{
dvander@2193
   265
							break;
dvander@2193
   266
						}
dvander@2193
   267
					}
dvander@2193
   268
				}
dvander@2193
   269
			} /* End array/string calculation */
dvander@2193
   270
			/* Update the pushed parameter with the byref local address */
dvander@2193
   271
			temp_params[i] = temp_info[i].local_addr;
dvander@2193
   272
		}
dvander@2193
   273
		else
dvander@2193
   274
		{
dvander@2193
   275
			/* Just copy the value normally */
dvander@2193
   276
			temp_params[i] = m_params[i];
dvander@2193
   277
		}
dvander@2193
   278
	}
dvander@2193
   279
dvander@2193
   280
	/* Make the call if we can */
dvander@2193
   281
	if (err == SP_ERROR_NONE)
dvander@2193
   282
	{
dvander@2193
   283
		if ((err = CallFunction2(ctx, temp_params, numparams, result)) != SP_ERROR_NONE)
dvander@2193
   284
		{
dvander@2193
   285
			docopies = false;
dvander@2193
   286
		}
dvander@2193
   287
	}
dvander@2193
   288
	else
dvander@2193
   289
	{
dvander@2193
   290
		docopies = false;
dvander@2193
   291
	}
dvander@2193
   292
dvander@2193
   293
	/* i should be equal to the last valid parameter + 1 */
dvander@2193
   294
	while (i--)
dvander@2193
   295
	{
dvander@2193
   296
		if (!temp_info[i].marked)
dvander@2193
   297
		{
dvander@2193
   298
			continue;
dvander@2193
   299
		}
dvander@2193
   300
dvander@2193
   301
		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
dvander@2193
   302
		{
dvander@2193
   303
			if (temp_info[i].orig_addr)
dvander@2193
   304
			{
dvander@2193
   305
				if (temp_info[i].str.is_sz)
dvander@2193
   306
				{
dvander@2193
   307
					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
dvander@2193
   308
				
dvander@2193
   309
				}
dvander@2193
   310
				else
dvander@2193
   311
				{
dvander@2193
   312
					if (temp_info[i].size == 1)
dvander@2193
   313
					{
dvander@2193
   314
						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
dvander@2193
   315
					}
dvander@2193
   316
					else
dvander@2193
   317
					{
dvander@2193
   318
						memcpy(temp_info[i].orig_addr, 
dvander@2193
   319
								temp_info[i].phys_addr, 
dvander@2193
   320
								temp_info[i].size * sizeof(cell_t));
dvander@2193
   321
					}
dvander@2193
   322
				}
dvander@2193
   323
			}
dvander@2193
   324
		}
dvander@2193
   325
dvander@2193
   326
		if ((err=ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
dvander@2193
   327
		{
dvander@2193
   328
			return err;
dvander@2193
   329
		}
dvander@2193
   330
	}
dvander@2193
   331
dvander@2193
   332
	return err;
dvander@2193
   333
}
dvander@2193
   334
dvander@2193
   335
funcid_t CFunction::GetFunctionID()
dvander@2193
   336
{
dvander@2193
   337
	return m_FnId;
dvander@2193
   338
}
dvander@2193
   339
dvander@2193
   340
int CFunction::SetError(int err)
dvander@2193
   341
{
dvander@2193
   342
	m_errorstate = err;
dvander@2193
   343
dvander@2193
   344
	return err;
dvander@2193
   345
}
dvander@2193
   346