Merge branch 'cc3'
This commit is contained in:
commit
f0aa177bf8
@ -931,20 +931,12 @@ LoadRes CPluginManager::_LoadPlugin(CPlugin **aResult, const char *path, bool de
|
|||||||
|
|
||||||
pPlugin->m_type = PluginType_MapUpdated;
|
pPlugin->m_type = PluginType_MapUpdated;
|
||||||
|
|
||||||
ICompilation *co = NULL;
|
|
||||||
|
|
||||||
if (pPlugin->m_status == Plugin_Uncompiled)
|
if (pPlugin->m_status == Plugin_Uncompiled)
|
||||||
{
|
|
||||||
co = g_pSourcePawn2->StartCompilation();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the actual compiling */
|
|
||||||
if (co != NULL)
|
|
||||||
{
|
{
|
||||||
char fullpath[PLATFORM_MAX_PATH];
|
char fullpath[PLATFORM_MAX_PATH];
|
||||||
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
|
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
|
||||||
|
|
||||||
pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
|
pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(nullptr, fullpath, &err);
|
||||||
if (pPlugin->m_pRuntime == NULL)
|
if (pPlugin->m_pRuntime == NULL)
|
||||||
{
|
{
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -274,26 +274,7 @@ namespace SourcePawn
|
|||||||
virtual int LookupLine(ucell_t addr, uint32_t *line) =0;
|
virtual int LookupLine(ucell_t addr, uint32_t *line) =0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class ICompilation;
|
||||||
* @brief Represents a JIT compilation or plugin loading options.
|
|
||||||
*/
|
|
||||||
class ICompilation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Sets a compilation option.
|
|
||||||
*
|
|
||||||
* @param key Option name.
|
|
||||||
* @param val Option value.
|
|
||||||
* @return True on success, false on failure.
|
|
||||||
*/
|
|
||||||
virtual bool SetOption(const char *key, const char *val) =0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Aborts the compilation and destroys this object.
|
|
||||||
*/
|
|
||||||
virtual void Abort() =0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Interface to managing a runtime plugin.
|
* @brief Interface to managing a runtime plugin.
|
||||||
@ -425,11 +406,9 @@ namespace SourcePawn
|
|||||||
virtual bool IsDebugging() =0;
|
virtual bool IsDebugging() =0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Applies new compilation/runtime settings to the runtime code.
|
* @brief If |co| is non-NULL, destroys |co|. No other action is taken.
|
||||||
*
|
*
|
||||||
* The compilation object is destroyed once this function completes.
|
* @return Returns SP_ERROR_NONE.
|
||||||
*
|
|
||||||
* @return Error code (SP_ERROR_NONE on success).
|
|
||||||
*/
|
*/
|
||||||
virtual int ApplyCompilationOptions(ICompilation *co) =0;
|
virtual int ApplyCompilationOptions(ICompilation *co) =0;
|
||||||
|
|
||||||
@ -1194,9 +1173,9 @@ namespace SourcePawn
|
|||||||
virtual const char *GetVersionString() =0;
|
virtual const char *GetVersionString() =0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new compilation options object.
|
* @brief Deprecated. Returns null.
|
||||||
*
|
*
|
||||||
* @return Compilation options object.
|
* @return Null.
|
||||||
*/
|
*/
|
||||||
virtual ICompilation *StartCompilation() =0;
|
virtual ICompilation *StartCompilation() =0;
|
||||||
|
|
||||||
@ -1206,10 +1185,10 @@ namespace SourcePawn
|
|||||||
* If a compilation object is supplied, it is destroyed upon
|
* If a compilation object is supplied, it is destroyed upon
|
||||||
* the function's return.
|
* the function's return.
|
||||||
*
|
*
|
||||||
* @param co Compilation options, or NULL for defaults.
|
* @param co Must be NULL.
|
||||||
* @param file Path to the file to compile.
|
* @param file Path to the file to compile.
|
||||||
* @param err Error code (filled on failure); required.
|
* @param err Error code (filled on failure); required.
|
||||||
* @return New runtime pointer, or NULL on failure.
|
* @return New runtime pointer, or NULL on failure.
|
||||||
*/
|
*/
|
||||||
virtual IPluginRuntime *LoadPlugin(ICompilation *co, const char *file, int *err) =0;
|
virtual IPluginRuntime *LoadPlugin(ICompilation *co, const char *file, int *err) =0;
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ library = setup(builder.compiler.StaticLibrary('sourcepawn'))
|
|||||||
library.sources += [
|
library.sources += [
|
||||||
'api.cpp',
|
'api.cpp',
|
||||||
'code-allocator.cpp',
|
'code-allocator.cpp',
|
||||||
|
'code-stubs.cpp',
|
||||||
'plugin-runtime.cpp',
|
'plugin-runtime.cpp',
|
||||||
'compiled-function.cpp',
|
'compiled-function.cpp',
|
||||||
'debug-trace.cpp',
|
'debug-trace.cpp',
|
||||||
@ -40,7 +41,9 @@ library.sources += [
|
|||||||
'opcodes.cpp',
|
'opcodes.cpp',
|
||||||
'interpreter.cpp',
|
'interpreter.cpp',
|
||||||
'watchdog_timer.cpp',
|
'watchdog_timer.cpp',
|
||||||
|
'x86/code-stubs-x86.cpp',
|
||||||
'x86/jit_x86.cpp',
|
'x86/jit_x86.cpp',
|
||||||
|
'x86/x86-utils.cpp',
|
||||||
'zlib/adler32.c',
|
'zlib/adler32.c',
|
||||||
'zlib/compress.c',
|
'zlib/compress.c',
|
||||||
'zlib/crc32.c',
|
'zlib/crc32.c',
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <sourcemod_version.h>
|
#include <sourcemod_version.h>
|
||||||
|
#include "code-stubs.h"
|
||||||
|
|
||||||
using namespace sp;
|
using namespace sp;
|
||||||
using namespace SourcePawn;
|
using namespace SourcePawn;
|
||||||
@ -185,6 +186,12 @@ SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err)
|
|||||||
size_t ignore;
|
size_t ignore;
|
||||||
PluginRuntime *pRuntime;
|
PluginRuntime *pRuntime;
|
||||||
|
|
||||||
|
if (co) {
|
||||||
|
if (err)
|
||||||
|
*err = SP_ERROR_PARAM;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *fp = fopen(file, "rb");
|
FILE *fp = fopen(file, "rb");
|
||||||
|
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
@ -275,8 +282,6 @@ SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err)
|
|||||||
if (!pRuntime->plugin()->name)
|
if (!pRuntime->plugin()->name)
|
||||||
pRuntime->SetName(file);
|
pRuntime->SetName(file);
|
||||||
|
|
||||||
pRuntime->ApplyCompilationOptions(co);
|
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
return pRuntime;
|
return pRuntime;
|
||||||
@ -294,13 +299,13 @@ return_error:
|
|||||||
SPVM_NATIVE_FUNC
|
SPVM_NATIVE_FUNC
|
||||||
SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData)
|
SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData)
|
||||||
{
|
{
|
||||||
return g_Jit.CreateFakeNative(callback, pData);
|
return Environment::get()->stubs()->CreateFakeNativeStub(callback, pData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func)
|
SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func)
|
||||||
{
|
{
|
||||||
g_Jit.DestroyFakeNative(func);
|
return Environment::get()->FreeCode((void *)func);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
@ -332,7 +337,7 @@ SourcePawnEngine2::GetAPIVersion()
|
|||||||
ICompilation *
|
ICompilation *
|
||||||
SourcePawnEngine2::StartCompilation()
|
SourcePawnEngine2::StartCompilation()
|
||||||
{
|
{
|
||||||
return g_Jit.StartCompilation();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
@ -364,9 +369,6 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
rt->SetName(name != NULL ? name : "<anonymous>");
|
rt->SetName(name != NULL ? name : "<anonymous>");
|
||||||
|
|
||||||
rt->ApplyCompilationOptions(NULL);
|
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
sourcepawn/jit/code-stubs.cpp
Normal file
41
sourcepawn/jit/code-stubs.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||||
|
//
|
||||||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||||
|
//
|
||||||
|
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||||
|
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
// License as published by the Free Software Foundation, either version 3 of
|
||||||
|
// the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along with
|
||||||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
|
//
|
||||||
|
#include "code-stubs.h"
|
||||||
|
#include "environment.h"
|
||||||
|
|
||||||
|
using namespace sp;
|
||||||
|
|
||||||
|
CodeStubs::CodeStubs(Environment *env)
|
||||||
|
: env_(env),
|
||||||
|
invoke_stub_(nullptr),
|
||||||
|
return_stub_(nullptr),
|
||||||
|
timeout_stub_(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CodeStubs::Initialize()
|
||||||
|
{
|
||||||
|
if (!InitializeFeatureDetection())
|
||||||
|
return false;
|
||||||
|
if (!CompileInvokeStub())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeStubs::Shutdown()
|
||||||
|
{
|
||||||
|
if (invoke_stub_)
|
||||||
|
env_->FreeCode(invoke_stub_);
|
||||||
|
}
|
61
sourcepawn/jit/code-stubs.h
Normal file
61
sourcepawn/jit/code-stubs.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||||
|
//
|
||||||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||||
|
//
|
||||||
|
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||||
|
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
// License as published by the Free Software Foundation, either version 3 of
|
||||||
|
// the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along with
|
||||||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
|
//
|
||||||
|
#ifndef _include_sourcepawn_vm_code_stubs_h_
|
||||||
|
#define _include_sourcepawn_vm_code_stubs_h_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sp_vm_api.h>
|
||||||
|
|
||||||
|
typedef struct sp_context_s sp_context_t;
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
class Environment;
|
||||||
|
|
||||||
|
typedef int (*InvokeStubFn)(sp_context_t *ctx, uint8_t *memory, void *code);
|
||||||
|
|
||||||
|
class CodeStubs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CodeStubs(Environment *env);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Initialize();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
SPVM_NATIVE_FUNC CreateFakeNativeStub(SPVM_FAKENATIVE_FUNC callback, void *userData);
|
||||||
|
|
||||||
|
InvokeStubFn InvokeStub() const {
|
||||||
|
return (InvokeStubFn)invoke_stub_;
|
||||||
|
}
|
||||||
|
void *ReturnStub() const {
|
||||||
|
return return_stub_;
|
||||||
|
}
|
||||||
|
void *TimeoutStub() const {
|
||||||
|
return return_stub_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool InitializeFeatureDetection();
|
||||||
|
bool CompileInvokeStub();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Environment *env_;
|
||||||
|
void *invoke_stub_;
|
||||||
|
void *return_stub_; // Owned by invoke_stub_.
|
||||||
|
void *timeout_stub_; // Owned by invoke_stub_.
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_sourcepawn_vm_code_stubs_h_
|
@ -11,6 +11,7 @@
|
|||||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
//
|
//
|
||||||
#include "compiled-function.h"
|
#include "compiled-function.h"
|
||||||
|
#include "x86/jit_x86.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
|
||||||
using namespace sp;
|
using namespace sp;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "watchdog_timer.h"
|
#include "watchdog_timer.h"
|
||||||
#include "debug-trace.h"
|
#include "debug-trace.h"
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
#include "code-stubs.h"
|
||||||
#include "watchdog_timer.h"
|
#include "watchdog_timer.h"
|
||||||
|
|
||||||
using namespace sp;
|
using namespace sp;
|
||||||
@ -63,13 +64,14 @@ Environment::Initialize()
|
|||||||
{
|
{
|
||||||
api_v1_ = new SourcePawnEngine();
|
api_v1_ = new SourcePawnEngine();
|
||||||
api_v2_ = new SourcePawnEngine2();
|
api_v2_ = new SourcePawnEngine2();
|
||||||
watchdog_timer_ = new WatchdogTimer();
|
code_stubs_ = new CodeStubs(this);
|
||||||
|
watchdog_timer_ = new WatchdogTimer(this);
|
||||||
|
|
||||||
if ((code_pool_ = Knight::KE_CreateCodeCache()) == nullptr)
|
if ((code_pool_ = Knight::KE_CreateCodeCache()) == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Safe to initialize JIT now that we have the code cache.
|
// Safe to initialize code now that we have the code cache.
|
||||||
if (!g_Jit.InitializeJIT())
|
if (!code_stubs_->Initialize())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -79,7 +81,7 @@ void
|
|||||||
Environment::Shutdown()
|
Environment::Shutdown()
|
||||||
{
|
{
|
||||||
watchdog_timer_->Shutdown();
|
watchdog_timer_->Shutdown();
|
||||||
g_Jit.ShutdownJIT();
|
code_stubs_->Shutdown();
|
||||||
Knight::KE_DestroyCodeCache(code_pool_);
|
Knight::KE_DestroyCodeCache(code_pool_);
|
||||||
|
|
||||||
assert(sEnvironment == this);
|
assert(sEnvironment == this);
|
||||||
@ -181,3 +183,72 @@ Environment::FreeCode(void *code)
|
|||||||
{
|
{
|
||||||
Knight::KE_FreeCode(code_pool_, code);
|
Knight::KE_FreeCode(code_pool_, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Environment::RegisterRuntime(PluginRuntime *rt)
|
||||||
|
{
|
||||||
|
mutex_.AssertCurrentThreadOwns();
|
||||||
|
runtimes_.append(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Environment::DeregisterRuntime(PluginRuntime *rt)
|
||||||
|
{
|
||||||
|
mutex_.AssertCurrentThreadOwns();
|
||||||
|
runtimes_.remove(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Environment::PatchAllJumpsForTimeout()
|
||||||
|
{
|
||||||
|
mutex_.AssertCurrentThreadOwns();
|
||||||
|
for (ke::InlineList<PluginRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
|
||||||
|
PluginRuntime *rt = *iter;
|
||||||
|
for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
|
||||||
|
CompiledFunction *fun = rt->GetJitFunction(i);
|
||||||
|
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||||
|
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||||
|
int32_t diff = intptr_t(code_stubs_->TimeoutStub()) - intptr_t(base + e.offset);
|
||||||
|
*reinterpret_cast<int32_t *>(base + e.offset - 4) = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Environment::UnpatchAllJumpsFromTimeout()
|
||||||
|
{
|
||||||
|
mutex_.AssertCurrentThreadOwns();
|
||||||
|
for (ke::InlineList<PluginRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
|
||||||
|
PluginRuntime *rt = *iter;
|
||||||
|
for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
|
||||||
|
CompiledFunction *fun = rt->GetJitFunction(i);
|
||||||
|
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||||
|
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||||
|
*reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Environment::Invoke(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result)
|
||||||
|
{
|
||||||
|
sp_context_t *ctx = runtime->GetBaseContext()->GetCtx();
|
||||||
|
|
||||||
|
// Note that cip, hp, sp are saved and restored by Execute2().
|
||||||
|
ctx->cip = fn->GetCodeOffset();
|
||||||
|
|
||||||
|
InvokeStubFn invoke = code_stubs_->InvokeStub();
|
||||||
|
|
||||||
|
EnterInvoke();
|
||||||
|
int err = invoke(ctx, runtime->plugin()->memory, fn->GetEntryAddress());
|
||||||
|
LeaveInvoke();
|
||||||
|
|
||||||
|
*result = ctx->rval;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
|
|
||||||
#include <sp_vm_api.h>
|
#include <sp_vm_api.h>
|
||||||
#include <am-utility.h> // Replace with am-cxx later.
|
#include <am-utility.h> // Replace with am-cxx later.
|
||||||
|
#include <am-inlinelist.h>
|
||||||
|
#include <am-thread-utils.h>
|
||||||
#include "code-allocator.h"
|
#include "code-allocator.h"
|
||||||
|
#include "plugin-runtime.h"
|
||||||
|
|
||||||
class PluginRuntime;
|
class PluginRuntime;
|
||||||
|
|
||||||
@ -23,6 +26,7 @@ namespace sp {
|
|||||||
|
|
||||||
using namespace SourcePawn;
|
using namespace SourcePawn;
|
||||||
|
|
||||||
|
class CodeStubs;
|
||||||
class WatchdogTimer;
|
class WatchdogTimer;
|
||||||
|
|
||||||
// An Environment encapsulates everything that's needed to load and run
|
// An Environment encapsulates everything that's needed to load and run
|
||||||
@ -58,6 +62,19 @@ class Environment : public ISourcePawnEnvironment
|
|||||||
// Allocate and free executable memory.
|
// Allocate and free executable memory.
|
||||||
void *AllocateCode(size_t size);
|
void *AllocateCode(size_t size);
|
||||||
void FreeCode(void *code);
|
void FreeCode(void *code);
|
||||||
|
CodeStubs *stubs() {
|
||||||
|
return code_stubs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runtime management.
|
||||||
|
void RegisterRuntime(PluginRuntime *rt);
|
||||||
|
void DeregisterRuntime(PluginRuntime *rt);
|
||||||
|
void PatchAllJumpsForTimeout();
|
||||||
|
void UnpatchAllJumpsFromTimeout();
|
||||||
|
ke::Mutex *lock() {
|
||||||
|
return &mutex_;
|
||||||
|
}
|
||||||
|
int Invoke(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result);
|
||||||
|
|
||||||
// Helpers.
|
// Helpers.
|
||||||
void SetProfiler(IProfilingTool *profiler) {
|
void SetProfiler(IProfilingTool *profiler) {
|
||||||
@ -89,6 +106,21 @@ class Environment : public ISourcePawnEnvironment
|
|||||||
return watchdog_timer_;
|
return watchdog_timer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are indicators used for the watchdog timer.
|
||||||
|
uintptr_t FrameId() const {
|
||||||
|
return frame_id_;
|
||||||
|
}
|
||||||
|
bool RunningCode() const {
|
||||||
|
return invoke_depth_ != 0;
|
||||||
|
}
|
||||||
|
void EnterInvoke() {
|
||||||
|
if (invoke_depth_++ == 0)
|
||||||
|
frame_id_++;
|
||||||
|
}
|
||||||
|
void LeaveInvoke() {
|
||||||
|
invoke_depth_--;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
@ -96,6 +128,7 @@ class Environment : public ISourcePawnEnvironment
|
|||||||
ke::AutoPtr<ISourcePawnEngine> api_v1_;
|
ke::AutoPtr<ISourcePawnEngine> api_v1_;
|
||||||
ke::AutoPtr<ISourcePawnEngine2> api_v2_;
|
ke::AutoPtr<ISourcePawnEngine2> api_v2_;
|
||||||
ke::AutoPtr<WatchdogTimer> watchdog_timer_;
|
ke::AutoPtr<WatchdogTimer> watchdog_timer_;
|
||||||
|
ke::Mutex mutex_;
|
||||||
|
|
||||||
IDebugListener *debugger_;
|
IDebugListener *debugger_;
|
||||||
IProfilingTool *profiler_;
|
IProfilingTool *profiler_;
|
||||||
@ -103,6 +136,12 @@ class Environment : public ISourcePawnEnvironment
|
|||||||
bool profiling_enabled_;
|
bool profiling_enabled_;
|
||||||
|
|
||||||
Knight::KeCodeCache *code_pool_;
|
Knight::KeCodeCache *code_pool_;
|
||||||
|
ke::InlineList<PluginRuntime> runtimes_;
|
||||||
|
|
||||||
|
uintptr_t frame_id_;
|
||||||
|
uintptr_t invoke_depth_;
|
||||||
|
|
||||||
|
ke::AutoPtr<CodeStubs> code_stubs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EnterProfileScope
|
class EnterProfileScope
|
||||||
@ -121,6 +160,6 @@ class EnterProfileScope
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace sp
|
||||||
|
|
||||||
#endif // _include_sourcepawn_vm_environment_h_
|
#endif // _include_sourcepawn_vm_environment_h_
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "plugin-runtime.h"
|
#include "plugin-runtime.h"
|
||||||
#include "x86/jit_x86.h"
|
#include "x86/jit_x86.h"
|
||||||
#include "sp_vm_basecontext.h"
|
#include "sp_vm_basecontext.h"
|
||||||
|
#include "environment.h"
|
||||||
|
|
||||||
#include "md5/md5.h"
|
#include "md5/md5.h"
|
||||||
|
|
||||||
@ -34,7 +35,6 @@ PluginRuntime::PluginRuntime()
|
|||||||
m_pCtx(NULL),
|
m_pCtx(NULL),
|
||||||
m_PubFuncs(NULL),
|
m_PubFuncs(NULL),
|
||||||
m_PubJitFuncs(NULL),
|
m_PubJitFuncs(NULL),
|
||||||
co_(NULL),
|
|
||||||
m_CompSerial(0)
|
m_CompSerial(0)
|
||||||
{
|
{
|
||||||
memset(&m_plugin, 0, sizeof(m_plugin));
|
memset(&m_plugin, 0, sizeof(m_plugin));
|
||||||
@ -48,8 +48,8 @@ PluginRuntime::PluginRuntime()
|
|||||||
memset(m_CodeHash, 0, sizeof(m_CodeHash));
|
memset(m_CodeHash, 0, sizeof(m_CodeHash));
|
||||||
memset(m_DataHash, 0, sizeof(m_DataHash));
|
memset(m_DataHash, 0, sizeof(m_DataHash));
|
||||||
|
|
||||||
ke::AutoLock lock(g_Jit.Mutex());
|
ke::AutoLock lock(Environment::get()->lock());
|
||||||
g_Jit.RegisterRuntime(this);
|
Environment::get()->RegisterRuntime(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginRuntime::~PluginRuntime()
|
PluginRuntime::~PluginRuntime()
|
||||||
@ -58,9 +58,9 @@ PluginRuntime::~PluginRuntime()
|
|||||||
// runtimes. It is not enough to ensure that the unlinking of the runtime is
|
// runtimes. It is not enough to ensure that the unlinking of the runtime is
|
||||||
// protected; we cannot delete functions or code while the watchdog might be
|
// protected; we cannot delete functions or code while the watchdog might be
|
||||||
// executing. Therefore, the entire destructor is guarded.
|
// executing. Therefore, the entire destructor is guarded.
|
||||||
ke::AutoLock lock(g_Jit.Mutex());
|
ke::AutoLock lock(Environment::get()->lock());
|
||||||
|
|
||||||
g_Jit.DeregisterRuntime(this);
|
Environment::get()->DeregisterRuntime(this);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_plugin.num_publics; i++)
|
for (uint32_t i = 0; i < m_plugin.num_publics; i++)
|
||||||
delete m_PubFuncs[i];
|
delete m_PubFuncs[i];
|
||||||
@ -74,8 +74,6 @@ PluginRuntime::~PluginRuntime()
|
|||||||
delete m_JitFunctions[i];
|
delete m_JitFunctions[i];
|
||||||
|
|
||||||
delete m_pCtx;
|
delete m_pCtx;
|
||||||
if (co_)
|
|
||||||
co_->Abort();
|
|
||||||
|
|
||||||
free(m_plugin.base);
|
free(m_plugin.base);
|
||||||
delete [] m_plugin.memory;
|
delete [] m_plugin.memory;
|
||||||
@ -303,7 +301,6 @@ int PluginRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
|
|||||||
md5_data.raw_digest(m_DataHash);
|
md5_data.raw_digest(m_DataHash);
|
||||||
|
|
||||||
m_pCtx = new BaseContext(this);
|
m_pCtx = new BaseContext(this);
|
||||||
co_ = g_Jit.StartCompilation(this);
|
|
||||||
|
|
||||||
SetupFloatNativeRemapping();
|
SetupFloatNativeRemapping();
|
||||||
function_map_size_ = m_plugin.pcode_size / sizeof(cell_t) + 1;
|
function_map_size_ = m_plugin.pcode_size / sizeof(cell_t) + 1;
|
||||||
@ -586,12 +583,6 @@ BaseContext *PluginRuntime::GetBaseContext()
|
|||||||
int
|
int
|
||||||
PluginRuntime::ApplyCompilationOptions(ICompilation *co)
|
PluginRuntime::ApplyCompilationOptions(ICompilation *co)
|
||||||
{
|
{
|
||||||
if (co == NULL)
|
|
||||||
return SP_ERROR_NONE;
|
|
||||||
|
|
||||||
co_ = g_Jit.ApplyOptions(co_, co);
|
|
||||||
m_plugin.prof_flags = ((CompData *)co_)->profile;
|
|
||||||
|
|
||||||
return SP_ERROR_NONE;
|
return SP_ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +599,6 @@ PluginRuntime::CreateBlank(uint32_t heastk)
|
|||||||
m_plugin.memory = new uint8_t[heastk];
|
m_plugin.memory = new uint8_t[heastk];
|
||||||
|
|
||||||
m_pCtx = new BaseContext(this);
|
m_pCtx = new BaseContext(this);
|
||||||
co_ = g_Jit.StartCompilation(this);
|
|
||||||
|
|
||||||
return SP_ERROR_NONE;
|
return SP_ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
@ -115,9 +115,6 @@ class PluginRuntime
|
|||||||
ScriptedInvoker **m_PubFuncs;
|
ScriptedInvoker **m_PubFuncs;
|
||||||
CompiledFunction **m_PubJitFuncs;
|
CompiledFunction **m_PubJitFuncs;
|
||||||
|
|
||||||
private:
|
|
||||||
ICompilation *co_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
unsigned int m_CompSerial;
|
unsigned int m_CompSerial;
|
||||||
|
|
||||||
|
@ -58,12 +58,18 @@ BaseContext::BaseContext(PluginRuntime *pRuntime)
|
|||||||
m_ctx.n_idx = SP_ERROR_NONE;
|
m_ctx.n_idx = SP_ERROR_NONE;
|
||||||
m_ctx.rp = 0;
|
m_ctx.rp = 0;
|
||||||
|
|
||||||
g_Jit.SetupContextVars(m_pRuntime, this, &m_ctx);
|
m_ctx.tracker = new tracker_t;
|
||||||
|
m_ctx.tracker->pBase = (ucell_t *)malloc(1024);
|
||||||
|
m_ctx.tracker->pCur = m_ctx.tracker->pBase;
|
||||||
|
m_ctx.tracker->size = 1024 / sizeof(cell_t);
|
||||||
|
m_ctx.basecx = this;
|
||||||
|
m_ctx.plugin = const_cast<sp_plugin_t *>(pRuntime->plugin());
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseContext::~BaseContext()
|
BaseContext::~BaseContext()
|
||||||
{
|
{
|
||||||
g_Jit.FreeContextVars(&m_ctx);
|
free(m_ctx.tracker->pBase);
|
||||||
|
delete m_ctx.tracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
IVirtualMachine *
|
IVirtualMachine *
|
||||||
@ -562,7 +568,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned
|
|||||||
if (fn) {
|
if (fn) {
|
||||||
m_pRuntime->m_PubJitFuncs[public_id] = fn;
|
m_pRuntime->m_PubJitFuncs[public_id] = fn;
|
||||||
} else {
|
} else {
|
||||||
if ((fn = g_Jit.CompileFunction(m_pRuntime, cfun->Public()->code_offs, &ir)) == NULL)
|
if ((fn = CompileFunction(m_pRuntime, cfun->Public()->code_offs, &ir)) == NULL)
|
||||||
return ir;
|
return ir;
|
||||||
m_pRuntime->m_PubJitFuncs[public_id] = fn;
|
m_pRuntime->m_PubJitFuncs[public_id] = fn;
|
||||||
}
|
}
|
||||||
@ -597,10 +603,10 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned
|
|||||||
m_CustomMsg = false;
|
m_CustomMsg = false;
|
||||||
m_InExec = true;
|
m_InExec = true;
|
||||||
|
|
||||||
/* Start the frame tracer */
|
// Enter the execution engine.
|
||||||
|
Environment *env = Environment::get();
|
||||||
if (Environment::get()->IsJitEnabled())
|
if (env->IsJitEnabled())
|
||||||
ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
|
ir = env->Invoke(m_pRuntime, fn, result);
|
||||||
else
|
else
|
||||||
ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result);
|
ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result);
|
||||||
|
|
||||||
|
@ -15,15 +15,17 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "watchdog_timer.h"
|
#include "watchdog_timer.h"
|
||||||
#include "x86/jit_x86.h"
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "environment.h"
|
||||||
|
//#include "x86/jit_x86.h"
|
||||||
|
|
||||||
WatchdogTimer::WatchdogTimer()
|
WatchdogTimer::WatchdogTimer(Environment *env)
|
||||||
: terminate_(false),
|
: env_(env),
|
||||||
mainthread_(ke::GetCurrentThreadId()),
|
terminate_(false),
|
||||||
last_frame_id_(0),
|
mainthread_(ke::GetCurrentThreadId()),
|
||||||
second_timeout_(false),
|
last_frame_id_(0),
|
||||||
timedout_(false)
|
second_timeout_(false),
|
||||||
|
timedout_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ WatchdogTimer::Run()
|
|||||||
ke::AutoLock lock(&cv_);
|
ke::AutoLock lock(&cv_);
|
||||||
|
|
||||||
// Initialize the frame id, so we don't have to wait longer on startup.
|
// Initialize the frame id, so we don't have to wait longer on startup.
|
||||||
last_frame_id_ = g_Jit.FrameId();
|
last_frame_id_ = env_->FrameId();
|
||||||
|
|
||||||
while (!terminate_) {
|
while (!terminate_) {
|
||||||
ke::WaitResult rv = cv_.Wait(timeout_ms_ / 2);
|
ke::WaitResult rv = cv_.Wait(timeout_ms_ / 2);
|
||||||
@ -89,8 +91,8 @@ WatchdogTimer::Run()
|
|||||||
// Note that it's okay if these two race: it's just a heuristic, and
|
// Note that it's okay if these two race: it's just a heuristic, and
|
||||||
// worst case, we'll miss something that might have timed out but
|
// worst case, we'll miss something that might have timed out but
|
||||||
// ended up resuming.
|
// ended up resuming.
|
||||||
uintptr_t frame_id = g_Jit.FrameId();
|
uintptr_t frame_id = env_->FrameId();
|
||||||
if (frame_id != last_frame_id_ || !g_Jit.RunningCode()) {
|
if (frame_id != last_frame_id_ || !env_->RunningCode()) {
|
||||||
last_frame_id_ = frame_id;
|
last_frame_id_ = frame_id;
|
||||||
second_timeout_ = false;
|
second_timeout_ = false;
|
||||||
continue;
|
continue;
|
||||||
@ -105,7 +107,7 @@ WatchdogTimer::Run()
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Prevent the JIT from linking or destroying runtimes and functions.
|
// Prevent the JIT from linking or destroying runtimes and functions.
|
||||||
ke::AutoLock lock(g_Jit.Mutex());
|
ke::AutoLock lock(env_->lock());
|
||||||
|
|
||||||
// Set the timeout notification bit. If this is detected before any patched
|
// Set the timeout notification bit. If this is detected before any patched
|
||||||
// JIT backedges are reached, the main thread will attempt to acquire the
|
// JIT backedges are reached, the main thread will attempt to acquire the
|
||||||
@ -115,7 +117,7 @@ WatchdogTimer::Run()
|
|||||||
// Patch all jumps. This can race with the main thread's execution since
|
// Patch all jumps. This can race with the main thread's execution since
|
||||||
// all code writes are 32-bit integer instruction operands, which are
|
// all code writes are 32-bit integer instruction operands, which are
|
||||||
// guaranteed to be atomic on x86.
|
// guaranteed to be atomic on x86.
|
||||||
g_Jit.PatchAllJumpsForTimeout();
|
env_->PatchAllJumpsForTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The JIT will be free to compile new functions while we wait, but it will
|
// The JIT will be free to compile new functions while we wait, but it will
|
||||||
@ -141,8 +143,8 @@ WatchdogTimer::NotifyTimeoutReceived()
|
|||||||
// notification, and is therefore blocked. We take the JIT lock
|
// notification, and is therefore blocked. We take the JIT lock
|
||||||
// anyway for sanity.
|
// anyway for sanity.
|
||||||
{
|
{
|
||||||
ke::AutoLock lock(g_Jit.Mutex());
|
ke::AutoLock lock(env_->lock());
|
||||||
g_Jit.UnpatchAllJumpsFromTimeout();
|
env_->UnpatchAllJumpsFromTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
timedout_ = false;
|
timedout_ = false;
|
||||||
|
@ -23,12 +23,14 @@
|
|||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
|
||||||
|
class Environment;
|
||||||
|
|
||||||
typedef bool (*WatchdogCallback)();
|
typedef bool (*WatchdogCallback)();
|
||||||
|
|
||||||
class WatchdogTimer : public ke::IRunnable
|
class WatchdogTimer : public ke::IRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WatchdogTimer();
|
WatchdogTimer(Environment *env);
|
||||||
~WatchdogTimer();
|
~WatchdogTimer();
|
||||||
|
|
||||||
bool Initialize(size_t timeout_ms);
|
bool Initialize(size_t timeout_ms);
|
||||||
@ -43,6 +45,8 @@ class WatchdogTimer : public ke::IRunnable
|
|||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Environment *env_;
|
||||||
|
|
||||||
bool terminate_;
|
bool terminate_;
|
||||||
size_t timeout_ms_;
|
size_t timeout_ms_;
|
||||||
ke::ThreadId mainthread_;
|
ke::ThreadId mainthread_;
|
||||||
|
135
sourcepawn/jit/x86/code-stubs-x86.cpp
Normal file
135
sourcepawn/jit/x86/code-stubs-x86.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||||
|
//
|
||||||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||||
|
//
|
||||||
|
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||||
|
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
// License as published by the Free Software Foundation, either version 3 of
|
||||||
|
// the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along with
|
||||||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
|
//
|
||||||
|
#include <sp_vm_api.h>
|
||||||
|
#include "code-stubs.h"
|
||||||
|
#include "x86-utils.h"
|
||||||
|
#include "jit_shared.h"
|
||||||
|
#include "jit_x86.h"
|
||||||
|
|
||||||
|
using namespace sp;
|
||||||
|
using namespace SourcePawn;
|
||||||
|
|
||||||
|
#define __ masm.
|
||||||
|
|
||||||
|
bool
|
||||||
|
CodeStubs::InitializeFeatureDetection()
|
||||||
|
{
|
||||||
|
MacroAssemblerX86 masm;
|
||||||
|
MacroAssemblerX86::GenerateFeatureDetection(masm);
|
||||||
|
void *code = LinkCode(env_, masm);
|
||||||
|
if (!code)
|
||||||
|
return false;
|
||||||
|
MacroAssemblerX86::RunFeatureDetection(code);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
CodeStubs::CompileInvokeStub()
|
||||||
|
{
|
||||||
|
AssemblerX86 masm;
|
||||||
|
|
||||||
|
__ push(ebp);
|
||||||
|
__ movl(ebp, esp);
|
||||||
|
|
||||||
|
__ push(esi); // ebp - 4
|
||||||
|
__ push(edi); // ebp - 8
|
||||||
|
__ push(ebx); // ebp - 12
|
||||||
|
__ push(esp); // ebp - 16
|
||||||
|
|
||||||
|
__ movl(ebx, Operand(ebp, 8 + 4 * 0));
|
||||||
|
__ movl(eax, Operand(ebp, 8 + 4 * 1));
|
||||||
|
__ movl(ecx, Operand(ebp, 8 + 4 * 2));
|
||||||
|
|
||||||
|
// Set up run-time registers.
|
||||||
|
__ movl(edi, Operand(ebx, offsetof(sp_context_t, sp)));
|
||||||
|
__ addl(edi, eax);
|
||||||
|
__ movl(esi, eax);
|
||||||
|
__ movl(ebx, edi);
|
||||||
|
|
||||||
|
// Align the stack.
|
||||||
|
__ andl(esp, 0xfffffff0);
|
||||||
|
|
||||||
|
// Call into plugin (align the stack first).
|
||||||
|
__ call(ecx);
|
||||||
|
|
||||||
|
// Get input context, store rval.
|
||||||
|
__ movl(ecx, Operand(ebp, 8 + 4 * 0));
|
||||||
|
__ movl(Operand(ecx, offsetof(sp_context_t, rval)), pri);
|
||||||
|
|
||||||
|
// Set no error.
|
||||||
|
__ movl(eax, SP_ERROR_NONE);
|
||||||
|
|
||||||
|
// Store latest stk. If we have an error code, we'll jump directly to here,
|
||||||
|
// so eax will already be set.
|
||||||
|
Label ret;
|
||||||
|
__ bind(&ret);
|
||||||
|
__ subl(stk, dat);
|
||||||
|
__ movl(Operand(ecx, offsetof(sp_context_t, sp)), stk);
|
||||||
|
|
||||||
|
// Restore stack.
|
||||||
|
__ movl(esp, Operand(ebp, -16));
|
||||||
|
|
||||||
|
// Restore registers and gtfo.
|
||||||
|
__ pop(ebx);
|
||||||
|
__ pop(edi);
|
||||||
|
__ pop(esi);
|
||||||
|
__ pop(ebp);
|
||||||
|
__ ret();
|
||||||
|
|
||||||
|
// The universal emergency return will jump to here.
|
||||||
|
Label error;
|
||||||
|
__ bind(&error);
|
||||||
|
__ movl(ecx, Operand(ebp, 8 + 4 * 0)); // ret-path expects ecx = ctx
|
||||||
|
__ jmp(&ret);
|
||||||
|
|
||||||
|
Label timeout;
|
||||||
|
__ bind(&timeout);
|
||||||
|
__ movl(eax, SP_ERROR_TIMEOUT);
|
||||||
|
__ jmp(&error);
|
||||||
|
|
||||||
|
invoke_stub_ = LinkCode(env_, masm);
|
||||||
|
if (!invoke_stub_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return_stub_ = reinterpret_cast<uint8_t *>(invoke_stub_) + error.offset();
|
||||||
|
timeout_stub_ = reinterpret_cast<uint8_t *>(invoke_stub_) + timeout.offset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPVM_NATIVE_FUNC
|
||||||
|
CodeStubs::CreateFakeNativeStub(SPVM_FAKENATIVE_FUNC callback, void *pData)
|
||||||
|
{
|
||||||
|
AssemblerX86 masm;
|
||||||
|
|
||||||
|
__ push(ebx);
|
||||||
|
__ push(edi);
|
||||||
|
__ push(esi);
|
||||||
|
__ movl(edi, Operand(esp, 16)); // store ctx
|
||||||
|
__ movl(esi, Operand(esp, 20)); // store params
|
||||||
|
__ movl(ebx, esp);
|
||||||
|
__ andl(esp, 0xfffffff0);
|
||||||
|
__ subl(esp, 4);
|
||||||
|
|
||||||
|
__ push(intptr_t(pData));
|
||||||
|
__ push(esi);
|
||||||
|
__ push(edi);
|
||||||
|
__ call(ExternalAddress((void *)callback));
|
||||||
|
__ movl(esp, ebx);
|
||||||
|
__ pop(esi);
|
||||||
|
__ pop(edi);
|
||||||
|
__ pop(ebx);
|
||||||
|
__ ret();
|
||||||
|
|
||||||
|
return (SPVM_NATIVE_FUNC)LinkCode(env_, masm);
|
||||||
|
}
|
@ -38,6 +38,8 @@
|
|||||||
#include "watchdog_timer.h"
|
#include "watchdog_timer.h"
|
||||||
#include "interpreter.h"
|
#include "interpreter.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "code-stubs.h"
|
||||||
|
#include "x86-utils.h"
|
||||||
|
|
||||||
using namespace sp;
|
using namespace sp;
|
||||||
|
|
||||||
@ -47,22 +49,6 @@ using namespace sp;
|
|||||||
|
|
||||||
#define __ masm.
|
#define __ masm.
|
||||||
|
|
||||||
JITX86 g_Jit;
|
|
||||||
|
|
||||||
static inline uint8_t *
|
|
||||||
LinkCode(AssemblerX86 &masm)
|
|
||||||
{
|
|
||||||
if (masm.outOfMemory())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
void *code = Environment::get()->AllocateCode(masm.length());
|
|
||||||
if (!code)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
masm.emitToExecutableMemory(code);
|
|
||||||
return reinterpret_cast<uint8_t *>(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline ConditionCode
|
static inline ConditionCode
|
||||||
OpToCondition(OPCODE op)
|
OpToCondition(OPCODE op)
|
||||||
{
|
{
|
||||||
@ -267,6 +253,22 @@ GetFunctionName(const sp_plugin_t *plugin, uint32_t offs)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CompiledFunction *
|
||||||
|
CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err)
|
||||||
|
{
|
||||||
|
Compiler cc(prt, pcode_offs);
|
||||||
|
CompiledFunction *fun = cc.emit(err);
|
||||||
|
if (!fun)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Grab the lock before linking code in, since the watchdog timer will look
|
||||||
|
// at this list on another thread.
|
||||||
|
ke::AutoLock lock(Environment::get()->lock());
|
||||||
|
|
||||||
|
prt->AddJittedFunction(fun);
|
||||||
|
return fun;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char *pc)
|
CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char *pc)
|
||||||
{
|
{
|
||||||
@ -279,7 +281,7 @@ CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char *
|
|||||||
CompiledFunction *fn = runtime->GetJittedFunctionByOffset(pcode_offs);
|
CompiledFunction *fn = runtime->GetJittedFunctionByOffset(pcode_offs);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
int err;
|
int err;
|
||||||
fn = g_Jit.CompileFunction(runtime, pcode_offs, &err);
|
fn = CompileFunction(runtime, pcode_offs, &err);
|
||||||
if (!fn)
|
if (!fn)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -299,7 +301,8 @@ CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs)
|
Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs)
|
||||||
: rt_(rt),
|
: env_(Environment::get()),
|
||||||
|
rt_(rt),
|
||||||
plugin_(rt->plugin()),
|
plugin_(rt->plugin()),
|
||||||
error_(SP_ERROR_NONE),
|
error_(SP_ERROR_NONE),
|
||||||
pcode_start_(pcode_offs),
|
pcode_start_(pcode_offs),
|
||||||
@ -365,7 +368,7 @@ Compiler::emit(int *errp)
|
|||||||
emitCallThunks();
|
emitCallThunks();
|
||||||
emitErrorPaths();
|
emitErrorPaths();
|
||||||
|
|
||||||
uint8_t *code = LinkCode(masm);
|
uint8_t *code = LinkCode(env_, masm);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
*errp = SP_ERROR_OUT_OF_MEMORY;
|
*errp = SP_ERROR_OUT_OF_MEMORY;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1532,7 +1535,7 @@ Compiler::emitCallThunks()
|
|||||||
|
|
||||||
__ bind(&error);
|
__ bind(&error);
|
||||||
__ movl(Operand(cipAddr()), thunk->pcode_offset);
|
__ movl(Operand(cipAddr()), thunk->pcode_offset);
|
||||||
__ jmp(g_Jit.GetUniversalReturn());
|
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1727,7 +1730,7 @@ Compiler::emitErrorPath(Label *dest, int code)
|
|||||||
if (dest->used()) {
|
if (dest->used()) {
|
||||||
__ bind(dest);
|
__ bind(dest);
|
||||||
__ movl(eax, code);
|
__ movl(eax, code);
|
||||||
__ jmp(g_Jit.GetUniversalReturn());
|
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1797,296 +1800,6 @@ Compiler::emitErrorPaths()
|
|||||||
__ bind(&extern_error_);
|
__ bind(&extern_error_);
|
||||||
__ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx()));
|
__ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx()));
|
||||||
__ movl(eax, Operand(eax, offsetof(sp_context_t, n_err)));
|
__ movl(eax, Operand(eax, offsetof(sp_context_t, n_err)));
|
||||||
__ jmp(g_Jit.GetUniversalReturn());
|
__ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef int (*JIT_EXECUTE)(sp_context_t *ctx, uint8_t *memory, void *code);
|
|
||||||
|
|
||||||
static void *
|
|
||||||
GenerateEntry(void **retp, void **timeoutp)
|
|
||||||
{
|
|
||||||
AssemblerX86 masm;
|
|
||||||
|
|
||||||
__ push(ebp);
|
|
||||||
__ movl(ebp, esp);
|
|
||||||
|
|
||||||
__ push(esi); // ebp - 4
|
|
||||||
__ push(edi); // ebp - 8
|
|
||||||
__ push(ebx); // ebp - 12
|
|
||||||
__ push(esp); // ebp - 16
|
|
||||||
|
|
||||||
__ movl(ebx, Operand(ebp, 8 + 4 * 0));
|
|
||||||
__ movl(eax, Operand(ebp, 8 + 4 * 1));
|
|
||||||
__ movl(ecx, Operand(ebp, 8 + 4 * 2));
|
|
||||||
|
|
||||||
// Set up run-time registers.
|
|
||||||
__ movl(edi, Operand(ebx, offsetof(sp_context_t, sp)));
|
|
||||||
__ addl(edi, eax);
|
|
||||||
__ movl(esi, eax);
|
|
||||||
__ movl(ebx, edi);
|
|
||||||
|
|
||||||
// Align the stack.
|
|
||||||
__ andl(esp, 0xfffffff0);
|
|
||||||
|
|
||||||
// Call into plugin (align the stack first).
|
|
||||||
__ call(ecx);
|
|
||||||
|
|
||||||
// Get input context, store rval.
|
|
||||||
__ movl(ecx, Operand(ebp, 8 + 4 * 0));
|
|
||||||
__ movl(Operand(ecx, offsetof(sp_context_t, rval)), pri);
|
|
||||||
|
|
||||||
// Set no error.
|
|
||||||
__ movl(eax, SP_ERROR_NONE);
|
|
||||||
|
|
||||||
// Store latest stk. If we have an error code, we'll jump directly to here,
|
|
||||||
// so eax will already be set.
|
|
||||||
Label ret;
|
|
||||||
__ bind(&ret);
|
|
||||||
__ subl(stk, dat);
|
|
||||||
__ movl(Operand(ecx, offsetof(sp_context_t, sp)), stk);
|
|
||||||
|
|
||||||
// Restore stack.
|
|
||||||
__ movl(esp, Operand(ebp, -16));
|
|
||||||
|
|
||||||
// Restore registers and gtfo.
|
|
||||||
__ pop(ebx);
|
|
||||||
__ pop(edi);
|
|
||||||
__ pop(esi);
|
|
||||||
__ pop(ebp);
|
|
||||||
__ ret();
|
|
||||||
|
|
||||||
// The universal emergency return will jump to here.
|
|
||||||
Label error;
|
|
||||||
__ bind(&error);
|
|
||||||
__ movl(ecx, Operand(ebp, 8 + 4 * 0)); // ret-path expects ecx = ctx
|
|
||||||
__ jmp(&ret);
|
|
||||||
|
|
||||||
Label timeout;
|
|
||||||
__ bind(&timeout);
|
|
||||||
__ movl(eax, SP_ERROR_TIMEOUT);
|
|
||||||
__ jmp(&error);
|
|
||||||
|
|
||||||
void *code = LinkCode(masm);
|
|
||||||
if (!code)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
*retp = reinterpret_cast<uint8_t *>(code) + error.offset();
|
|
||||||
*timeoutp = reinterpret_cast<uint8_t *>(code) + timeout.offset();
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICompilation *JITX86::ApplyOptions(ICompilation *_IN, ICompilation *_OUT)
|
|
||||||
{
|
|
||||||
if (_IN == NULL)
|
|
||||||
return _OUT;
|
|
||||||
|
|
||||||
CompData *_in = (CompData * )_IN;
|
|
||||||
CompData *_out = (CompData * )_OUT;
|
|
||||||
|
|
||||||
_in->inline_level = _out->inline_level;
|
|
||||||
_in->profile = _out->profile;
|
|
||||||
|
|
||||||
_out->Abort();
|
|
||||||
return _in;
|
|
||||||
}
|
|
||||||
|
|
||||||
JITX86::JITX86()
|
|
||||||
{
|
|
||||||
m_pJitEntry = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
JITX86::InitializeJIT()
|
|
||||||
{
|
|
||||||
m_pJitEntry = GenerateEntry(&m_pJitReturn, &m_pJitTimeout);
|
|
||||||
if (!m_pJitEntry)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MacroAssemblerX86 masm;
|
|
||||||
MacroAssemblerX86::GenerateFeatureDetection(masm);
|
|
||||||
void *code = LinkCode(masm);
|
|
||||||
if (!code)
|
|
||||||
return false;
|
|
||||||
MacroAssemblerX86::RunFeatureDetection(code);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::ShutdownJIT()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CompiledFunction *
|
|
||||||
JITX86::CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err)
|
|
||||||
{
|
|
||||||
Compiler cc(prt, pcode_offs);
|
|
||||||
CompiledFunction *fun = cc.emit(err);
|
|
||||||
if (!fun)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Grab the lock before linking code in, since the watchdog timer will look
|
|
||||||
// at this list on another thread.
|
|
||||||
ke::AutoLock lock(g_Jit.Mutex());
|
|
||||||
|
|
||||||
prt->AddJittedFunction(fun);
|
|
||||||
return fun;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::SetupContextVars(PluginRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx)
|
|
||||||
{
|
|
||||||
ctx->tracker = new tracker_t;
|
|
||||||
ctx->tracker->pBase = (ucell_t *)malloc(1024);
|
|
||||||
ctx->tracker->pCur = ctx->tracker->pBase;
|
|
||||||
ctx->tracker->size = 1024 / sizeof(cell_t);
|
|
||||||
ctx->basecx = pCtx;
|
|
||||||
ctx->plugin = const_cast<sp_plugin_t *>(runtime->plugin());
|
|
||||||
}
|
|
||||||
|
|
||||||
SPVM_NATIVE_FUNC
|
|
||||||
JITX86::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData)
|
|
||||||
{
|
|
||||||
AssemblerX86 masm;
|
|
||||||
|
|
||||||
__ push(ebx);
|
|
||||||
__ push(edi);
|
|
||||||
__ push(esi);
|
|
||||||
__ movl(edi, Operand(esp, 16)); // store ctx
|
|
||||||
__ movl(esi, Operand(esp, 20)); // store params
|
|
||||||
__ movl(ebx, esp);
|
|
||||||
__ andl(esp, 0xfffffff0);
|
|
||||||
__ subl(esp, 4);
|
|
||||||
|
|
||||||
__ push(intptr_t(pData));
|
|
||||||
__ push(esi);
|
|
||||||
__ push(edi);
|
|
||||||
__ call(ExternalAddress((void *)callback));
|
|
||||||
__ movl(esp, ebx);
|
|
||||||
__ pop(esi);
|
|
||||||
__ pop(edi);
|
|
||||||
__ pop(ebx);
|
|
||||||
__ ret();
|
|
||||||
|
|
||||||
return (SPVM_NATIVE_FUNC)LinkCode(masm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func)
|
|
||||||
{
|
|
||||||
Environment::get()->FreeCode((void *)func);
|
|
||||||
}
|
|
||||||
|
|
||||||
ICompilation *
|
|
||||||
JITX86::StartCompilation()
|
|
||||||
{
|
|
||||||
return new CompData;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICompilation *
|
|
||||||
JITX86::StartCompilation(PluginRuntime *runtime)
|
|
||||||
{
|
|
||||||
return new CompData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CompData::Abort()
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::FreeContextVars(sp_context_t *ctx)
|
|
||||||
{
|
|
||||||
free(ctx->tracker->pBase);
|
|
||||||
delete ctx->tracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CompData::SetOption(const char *key, const char *val)
|
|
||||||
{
|
|
||||||
if (strcmp(key, SP_JITCONF_DEBUG) == 0)
|
|
||||||
return true;
|
|
||||||
if (strcmp(key, SP_JITCONF_PROFILE) == 0) {
|
|
||||||
profile = atoi(val);
|
|
||||||
|
|
||||||
/** Callbacks must be profiled to profile functions! */
|
|
||||||
if ((profile & SP_PROF_FUNCTIONS) == SP_PROF_FUNCTIONS)
|
|
||||||
profile |= SP_PROF_CALLBACKS;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
JITX86::InvokeFunction(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result)
|
|
||||||
{
|
|
||||||
sp_context_t *ctx = runtime->GetBaseContext()->GetCtx();
|
|
||||||
|
|
||||||
// Note that cip, hp, sp are saved and restored by Execute2().
|
|
||||||
ctx->cip = fn->GetCodeOffset();
|
|
||||||
|
|
||||||
JIT_EXECUTE pfn = (JIT_EXECUTE)m_pJitEntry;
|
|
||||||
|
|
||||||
if (level_++ == 0)
|
|
||||||
frame_id_++;
|
|
||||||
int err = pfn(ctx, runtime->plugin()->memory, fn->GetEntryAddress());
|
|
||||||
level_--;
|
|
||||||
|
|
||||||
*result = ctx->rval;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::RegisterRuntime(PluginRuntime *rt)
|
|
||||||
{
|
|
||||||
mutex_.AssertCurrentThreadOwns();
|
|
||||||
runtimes_.append(rt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::DeregisterRuntime(PluginRuntime *rt)
|
|
||||||
{
|
|
||||||
mutex_.AssertCurrentThreadOwns();
|
|
||||||
runtimes_.remove(rt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::PatchAllJumpsForTimeout()
|
|
||||||
{
|
|
||||||
mutex_.AssertCurrentThreadOwns();
|
|
||||||
for (ke::InlineList<PluginRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
|
|
||||||
PluginRuntime *rt = *iter;
|
|
||||||
for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
|
|
||||||
CompiledFunction *fun = rt->GetJitFunction(i);
|
|
||||||
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
|
||||||
|
|
||||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
|
||||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
|
||||||
int32_t diff = intptr_t(m_pJitTimeout) - intptr_t(base + e.offset);
|
|
||||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JITX86::UnpatchAllJumpsFromTimeout()
|
|
||||||
{
|
|
||||||
mutex_.AssertCurrentThreadOwns();
|
|
||||||
for (ke::InlineList<PluginRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
|
|
||||||
PluginRuntime *rt = *iter;
|
|
||||||
for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
|
|
||||||
CompiledFunction *fun = rt->GetJitFunction(i);
|
|
||||||
uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
|
|
||||||
|
|
||||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
|
||||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
|
||||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,13 @@
|
|||||||
#include "sp_vm_basecontext.h"
|
#include "sp_vm_basecontext.h"
|
||||||
#include "compiled-function.h"
|
#include "compiled-function.h"
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
#include <am-thread-utils.h>
|
|
||||||
|
|
||||||
using namespace SourcePawn;
|
using namespace SourcePawn;
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
class Environment;
|
||||||
|
}
|
||||||
|
|
||||||
#define JIT_INLINE_ERRORCHECKS (1<<0)
|
#define JIT_INLINE_ERRORCHECKS (1<<0)
|
||||||
#define JIT_INLINE_NATIVES (1<<1)
|
#define JIT_INLINE_NATIVES (1<<1)
|
||||||
#define STACK_MARGIN 64 //8 parameters of safety, I guess
|
#define STACK_MARGIN 64 //8 parameters of safety, I guess
|
||||||
@ -62,25 +65,6 @@ struct CallThunk
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CompData : public ICompilation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CompData()
|
|
||||||
: profile(0),
|
|
||||||
inline_level(0)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
bool SetOption(const char *key, const char *val);
|
|
||||||
void Abort();
|
|
||||||
public:
|
|
||||||
cell_t cur_func; /* current func pcode offset */
|
|
||||||
/* Options */
|
|
||||||
int profile; /* profiling flags */
|
|
||||||
int inline_level; /* inline optimization level */
|
|
||||||
/* Per-compilation properties */
|
|
||||||
unsigned int func_idx; /* current function index */
|
|
||||||
};
|
|
||||||
|
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -121,6 +105,7 @@ class Compiler
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AssemblerX86 masm;
|
AssemblerX86 masm;
|
||||||
|
sp::Environment *env_;
|
||||||
PluginRuntime *rt_;
|
PluginRuntime *rt_;
|
||||||
const sp_plugin_t *plugin_;
|
const sp_plugin_t *plugin_;
|
||||||
int error_;
|
int error_;
|
||||||
@ -145,53 +130,6 @@ class Compiler
|
|||||||
ke::Vector<CallThunk *> thunks_; //:TODO: free
|
ke::Vector<CallThunk *> thunks_; //:TODO: free
|
||||||
};
|
};
|
||||||
|
|
||||||
class JITX86
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
JITX86();
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool InitializeJIT();
|
|
||||||
void ShutdownJIT();
|
|
||||||
ICompilation *StartCompilation(PluginRuntime *runtime);
|
|
||||||
ICompilation *StartCompilation();
|
|
||||||
void SetupContextVars(PluginRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx);
|
|
||||||
void FreeContextVars(sp_context_t *ctx);
|
|
||||||
SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData);
|
|
||||||
void DestroyFakeNative(SPVM_NATIVE_FUNC func);
|
|
||||||
CompiledFunction *CompileFunction(PluginRuntime *runtime, cell_t pcode_offs, int *err);
|
|
||||||
ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT);
|
|
||||||
int InvokeFunction(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result);
|
|
||||||
|
|
||||||
void RegisterRuntime(PluginRuntime *rt);
|
|
||||||
void DeregisterRuntime(PluginRuntime *rt);
|
|
||||||
void PatchAllJumpsForTimeout();
|
|
||||||
void UnpatchAllJumpsFromTimeout();
|
|
||||||
|
|
||||||
public:
|
|
||||||
ExternalAddress GetUniversalReturn() {
|
|
||||||
return ExternalAddress(m_pJitReturn);
|
|
||||||
}
|
|
||||||
uintptr_t FrameId() const {
|
|
||||||
return frame_id_;
|
|
||||||
}
|
|
||||||
bool RunningCode() const {
|
|
||||||
return level_ != 0;
|
|
||||||
}
|
|
||||||
ke::Mutex *Mutex() {
|
|
||||||
return &mutex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *m_pJitEntry; /* Entry function */
|
|
||||||
void *m_pJitReturn; /* Universal return address */
|
|
||||||
void *m_pJitTimeout; /* Universal timeout address */
|
|
||||||
ke::InlineList<PluginRuntime> runtimes_;
|
|
||||||
uintptr_t frame_id_;
|
|
||||||
uintptr_t level_;
|
|
||||||
ke::Mutex mutex_;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Register pri = eax;
|
const Register pri = eax;
|
||||||
const Register alt = edx;
|
const Register alt = edx;
|
||||||
const Register stk = edi;
|
const Register stk = edi;
|
||||||
@ -199,7 +137,8 @@ const Register dat = esi;
|
|||||||
const Register tmp = ecx;
|
const Register tmp = ecx;
|
||||||
const Register frm = ebx;
|
const Register frm = ebx;
|
||||||
|
|
||||||
extern JITX86 g_Jit;
|
CompiledFunction *
|
||||||
|
CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err);
|
||||||
|
|
||||||
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_
|
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_
|
||||||
|
|
||||||
|
30
sourcepawn/jit/x86/x86-utils.cpp
Normal file
30
sourcepawn/jit/x86/x86-utils.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||||
|
//
|
||||||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||||
|
//
|
||||||
|
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||||
|
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
// License as published by the Free Software Foundation, either version 3 of
|
||||||
|
// the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along with
|
||||||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
|
//
|
||||||
|
#include "environment.h"
|
||||||
|
#include "x86-utils.h"
|
||||||
|
|
||||||
|
using namespace sp;
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
sp::LinkCode(Environment *env, AssemblerX86 &masm)
|
||||||
|
{
|
||||||
|
if (masm.outOfMemory())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
void *code = env->AllocateCode(masm.length());
|
||||||
|
if (!code)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
masm.emitToExecutableMemory(code);
|
||||||
|
return reinterpret_cast<uint8_t *>(code);
|
||||||
|
}
|
27
sourcepawn/jit/x86/x86-utils.h
Normal file
27
sourcepawn/jit/x86/x86-utils.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||||
|
//
|
||||||
|
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||||
|
//
|
||||||
|
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||||
|
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
|
// License as published by the Free Software Foundation, either version 3 of
|
||||||
|
// the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along with
|
||||||
|
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||||
|
//
|
||||||
|
#ifndef _include_sourcepawn_vm_x86_utils_h_
|
||||||
|
#define _include_sourcepawn_vm_x86_utils_h_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <macro-assembler-x86.h>
|
||||||
|
|
||||||
|
namespace sp {
|
||||||
|
|
||||||
|
class Environment;
|
||||||
|
|
||||||
|
uint8_t *LinkCode(Environment *env, AssemblerX86 &masm);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_sourcepawn_vm_x86_utils_h_
|
Loading…
Reference in New Issue
Block a user