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