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; | ||||
| 
 | ||||
| 	ICompilation *co = NULL; | ||||
| 
 | ||||
| 	if (pPlugin->m_status == Plugin_Uncompiled) | ||||
| 	{ | ||||
| 		co = g_pSourcePawn2->StartCompilation(); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Do the actual compiling */ | ||||
| 	if (co != NULL) | ||||
| 	{ | ||||
| 		char fullpath[PLATFORM_MAX_PATH]; | ||||
| 		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 (error) | ||||
|  | ||||
| @ -274,26 +274,7 @@ namespace SourcePawn | ||||
| 		virtual int LookupLine(ucell_t addr, uint32_t *line) =0; | ||||
| 	}; | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * @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; | ||||
| 	}; | ||||
| 	class ICompilation; | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * @brief Interface to managing a runtime plugin. | ||||
| @ -425,11 +406,9 @@ namespace SourcePawn | ||||
| 		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				Error code (SP_ERROR_NONE on success). | ||||
| 		 * @return	                        Returns SP_ERROR_NONE. | ||||
| 		 */ | ||||
| 		virtual int ApplyCompilationOptions(ICompilation *co) =0; | ||||
| 		 | ||||
| @ -1194,9 +1173,9 @@ namespace SourcePawn | ||||
| 		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; | ||||
| 
 | ||||
| @ -1206,7 +1185,7 @@ namespace SourcePawn | ||||
| 		 * If a compilation object is supplied, it is destroyed upon  | ||||
| 		 * 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 err		Error code (filled on failure); required. | ||||
| 		 * @return		New runtime pointer, or NULL on failure. | ||||
|  | ||||
| @ -31,6 +31,7 @@ library = setup(builder.compiler.StaticLibrary('sourcepawn')) | ||||
| library.sources += [ | ||||
|   'api.cpp', | ||||
|   'code-allocator.cpp', | ||||
|   'code-stubs.cpp', | ||||
|   'plugin-runtime.cpp', | ||||
|   'compiled-function.cpp', | ||||
|   'debug-trace.cpp', | ||||
| @ -40,7 +41,9 @@ library.sources += [ | ||||
|   'opcodes.cpp', | ||||
|   'interpreter.cpp', | ||||
|   'watchdog_timer.cpp', | ||||
|   'x86/code-stubs-x86.cpp', | ||||
|   'x86/jit_x86.cpp', | ||||
|   'x86/x86-utils.cpp', | ||||
|   'zlib/adler32.c', | ||||
|   'zlib/compress.c', | ||||
|   'zlib/crc32.c', | ||||
|  | ||||
| @ -33,6 +33,7 @@ | ||||
| #endif | ||||
| 
 | ||||
| #include <sourcemod_version.h> | ||||
| #include "code-stubs.h" | ||||
| 
 | ||||
| using namespace sp; | ||||
| using namespace SourcePawn; | ||||
| @ -185,6 +186,12 @@ SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err) | ||||
|   size_t ignore; | ||||
|   PluginRuntime *pRuntime; | ||||
| 
 | ||||
|   if (co) { | ||||
|     if (err) | ||||
|       *err = SP_ERROR_PARAM; | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   FILE *fp = fopen(file, "rb"); | ||||
| 
 | ||||
|   if (!fp) { | ||||
| @ -275,8 +282,6 @@ SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err) | ||||
|   if (!pRuntime->plugin()->name) | ||||
|     pRuntime->SetName(file); | ||||
| 
 | ||||
|   pRuntime->ApplyCompilationOptions(co); | ||||
| 
 | ||||
|   fclose(fp); | ||||
| 
 | ||||
|   return pRuntime; | ||||
| @ -294,13 +299,13 @@ return_error: | ||||
| SPVM_NATIVE_FUNC | ||||
| SourcePawnEngine2::CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData) | ||||
| { | ||||
|   return g_Jit.CreateFakeNative(callback, pData); | ||||
|   return Environment::get()->stubs()->CreateFakeNativeStub(callback, pData); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func) | ||||
| { | ||||
|   g_Jit.DestroyFakeNative(func); | ||||
|   return Environment::get()->FreeCode((void *)func); | ||||
| } | ||||
| 
 | ||||
| const char * | ||||
| @ -332,7 +337,7 @@ SourcePawnEngine2::GetAPIVersion() | ||||
| ICompilation * | ||||
| SourcePawnEngine2::StartCompilation() | ||||
| { | ||||
|   return g_Jit.StartCompilation(); | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| const char * | ||||
| @ -364,9 +369,6 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory) | ||||
|   } | ||||
| 
 | ||||
|   rt->SetName(name != NULL ? name : "<anonymous>"); | ||||
| 
 | ||||
|   rt->ApplyCompilationOptions(NULL); | ||||
|    | ||||
|   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/.
 | ||||
| //
 | ||||
| #include "compiled-function.h" | ||||
| #include "x86/jit_x86.h" | ||||
| #include "environment.h" | ||||
| 
 | ||||
| using namespace sp; | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| #include "watchdog_timer.h" | ||||
| #include "debug-trace.h" | ||||
| #include "api.h" | ||||
| #include "code-stubs.h" | ||||
| #include "watchdog_timer.h" | ||||
| 
 | ||||
| using namespace sp; | ||||
| @ -63,13 +64,14 @@ Environment::Initialize() | ||||
| { | ||||
|   api_v1_ = new SourcePawnEngine(); | ||||
|   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) | ||||
|     return false; | ||||
| 
 | ||||
|   // Safe to initialize JIT now that we have the code cache.
 | ||||
|   if (!g_Jit.InitializeJIT()) | ||||
|   // Safe to initialize code now that we have the code cache.
 | ||||
|   if (!code_stubs_->Initialize()) | ||||
|     return false; | ||||
| 
 | ||||
|   return true; | ||||
| @ -79,7 +81,7 @@ void | ||||
| Environment::Shutdown() | ||||
| { | ||||
|   watchdog_timer_->Shutdown(); | ||||
|   g_Jit.ShutdownJIT(); | ||||
|   code_stubs_->Shutdown(); | ||||
|   Knight::KE_DestroyCodeCache(code_pool_); | ||||
| 
 | ||||
|   assert(sEnvironment == this); | ||||
| @ -181,3 +183,72 @@ Environment::FreeCode(void *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 <am-utility.h> // Replace with am-cxx later. | ||||
| #include <am-inlinelist.h> | ||||
| #include <am-thread-utils.h> | ||||
| #include "code-allocator.h" | ||||
| #include "plugin-runtime.h" | ||||
| 
 | ||||
| class PluginRuntime; | ||||
| 
 | ||||
| @ -23,6 +26,7 @@ namespace sp { | ||||
| 
 | ||||
| using namespace SourcePawn; | ||||
| 
 | ||||
| class CodeStubs; | ||||
| class WatchdogTimer; | ||||
| 
 | ||||
| // An Environment encapsulates everything that's needed to load and run
 | ||||
| @ -58,6 +62,19 @@ class Environment : public ISourcePawnEnvironment | ||||
|   // Allocate and free executable memory.
 | ||||
|   void *AllocateCode(size_t size); | ||||
|   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.
 | ||||
|   void SetProfiler(IProfilingTool *profiler) { | ||||
| @ -89,6 +106,21 @@ class Environment : public ISourcePawnEnvironment | ||||
|     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: | ||||
|   bool Initialize(); | ||||
| 
 | ||||
| @ -96,6 +128,7 @@ class Environment : public ISourcePawnEnvironment | ||||
|   ke::AutoPtr<ISourcePawnEngine> api_v1_; | ||||
|   ke::AutoPtr<ISourcePawnEngine2> api_v2_; | ||||
|   ke::AutoPtr<WatchdogTimer> watchdog_timer_; | ||||
|   ke::Mutex mutex_; | ||||
| 
 | ||||
|   IDebugListener *debugger_; | ||||
|   IProfilingTool *profiler_; | ||||
| @ -103,6 +136,12 @@ class Environment : public ISourcePawnEnvironment | ||||
|   bool profiling_enabled_; | ||||
| 
 | ||||
|   Knight::KeCodeCache *code_pool_; | ||||
|   ke::InlineList<PluginRuntime> runtimes_; | ||||
| 
 | ||||
|   uintptr_t frame_id_; | ||||
|   uintptr_t invoke_depth_; | ||||
| 
 | ||||
|   ke::AutoPtr<CodeStubs> code_stubs_; | ||||
| }; | ||||
| 
 | ||||
| class EnterProfileScope | ||||
| @ -121,6 +160,6 @@ class EnterProfileScope | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } // namespace sp
 | ||||
| 
 | ||||
| #endif // _include_sourcepawn_vm_environment_h_
 | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| #include "plugin-runtime.h" | ||||
| #include "x86/jit_x86.h" | ||||
| #include "sp_vm_basecontext.h" | ||||
| #include "environment.h" | ||||
| 
 | ||||
| #include "md5/md5.h" | ||||
| 
 | ||||
| @ -34,7 +35,6 @@ PluginRuntime::PluginRuntime() | ||||
|     m_pCtx(NULL),  | ||||
|     m_PubFuncs(NULL), | ||||
|     m_PubJitFuncs(NULL), | ||||
|     co_(NULL), | ||||
|     m_CompSerial(0) | ||||
| { | ||||
|   memset(&m_plugin, 0, sizeof(m_plugin)); | ||||
| @ -48,8 +48,8 @@ PluginRuntime::PluginRuntime() | ||||
|   memset(m_CodeHash, 0, sizeof(m_CodeHash)); | ||||
|   memset(m_DataHash, 0, sizeof(m_DataHash)); | ||||
| 
 | ||||
|   ke::AutoLock lock(g_Jit.Mutex()); | ||||
|   g_Jit.RegisterRuntime(this); | ||||
|   ke::AutoLock lock(Environment::get()->lock()); | ||||
|   Environment::get()->RegisterRuntime(this); | ||||
| } | ||||
| 
 | ||||
| PluginRuntime::~PluginRuntime() | ||||
| @ -58,9 +58,9 @@ PluginRuntime::~PluginRuntime() | ||||
|   // 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
 | ||||
|   // 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++) | ||||
|     delete m_PubFuncs[i]; | ||||
| @ -74,8 +74,6 @@ PluginRuntime::~PluginRuntime() | ||||
|     delete m_JitFunctions[i]; | ||||
| 
 | ||||
|   delete m_pCtx; | ||||
|   if (co_) | ||||
|     co_->Abort(); | ||||
| 
 | ||||
|   free(m_plugin.base); | ||||
|   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); | ||||
| 
 | ||||
|   m_pCtx = new BaseContext(this); | ||||
|   co_ = g_Jit.StartCompilation(this); | ||||
| 
 | ||||
|   SetupFloatNativeRemapping(); | ||||
|   function_map_size_ = m_plugin.pcode_size / sizeof(cell_t) + 1; | ||||
| @ -586,12 +583,6 @@ BaseContext *PluginRuntime::GetBaseContext() | ||||
| int | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| @ -608,7 +599,6 @@ PluginRuntime::CreateBlank(uint32_t heastk) | ||||
|   m_plugin.memory = new uint8_t[heastk]; | ||||
| 
 | ||||
|   m_pCtx = new BaseContext(this); | ||||
|   co_ = g_Jit.StartCompilation(this); | ||||
| 
 | ||||
|   return SP_ERROR_NONE; | ||||
| } | ||||
|  | ||||
| @ -115,9 +115,6 @@ class PluginRuntime | ||||
|   ScriptedInvoker **m_PubFuncs; | ||||
|   CompiledFunction **m_PubJitFuncs; | ||||
| 
 | ||||
|  private: | ||||
|   ICompilation *co_; | ||||
| 
 | ||||
|  public: | ||||
|   unsigned int m_CompSerial; | ||||
|    | ||||
|  | ||||
| @ -58,12 +58,18 @@ BaseContext::BaseContext(PluginRuntime *pRuntime) | ||||
|   m_ctx.n_idx = SP_ERROR_NONE; | ||||
|   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() | ||||
| { | ||||
|   g_Jit.FreeContextVars(&m_ctx); | ||||
|   free(m_ctx.tracker->pBase); | ||||
|   delete m_ctx.tracker; | ||||
| } | ||||
| 
 | ||||
| IVirtualMachine * | ||||
| @ -562,7 +568,7 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned | ||||
|     if (fn) { | ||||
|       m_pRuntime->m_PubJitFuncs[public_id] = fn; | ||||
|     } 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; | ||||
|       m_pRuntime->m_PubJitFuncs[public_id] = fn; | ||||
|     } | ||||
| @ -597,10 +603,10 @@ BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned | ||||
|   m_CustomMsg = false; | ||||
|   m_InExec = true; | ||||
| 
 | ||||
|   /* Start the frame tracer */ | ||||
| 
 | ||||
|   if (Environment::get()->IsJitEnabled()) | ||||
|     ir = g_Jit.InvokeFunction(m_pRuntime, fn, result); | ||||
|   // Enter the execution engine.
 | ||||
|   Environment *env = Environment::get(); | ||||
|   if (env->IsJitEnabled()) | ||||
|     ir = env->Invoke(m_pRuntime, fn, result); | ||||
|   else | ||||
|     ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result); | ||||
| 
 | ||||
|  | ||||
| @ -15,11 +15,13 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| #include "watchdog_timer.h" | ||||
| #include "x86/jit_x86.h" | ||||
| #include <string.h> | ||||
| #include "environment.h" | ||||
| //#include "x86/jit_x86.h"
 | ||||
| 
 | ||||
| WatchdogTimer::WatchdogTimer() | ||||
|   : terminate_(false), | ||||
| WatchdogTimer::WatchdogTimer(Environment *env) | ||||
|  : env_(env), | ||||
|    terminate_(false), | ||||
|    mainthread_(ke::GetCurrentThreadId()), | ||||
|    last_frame_id_(0), | ||||
|    second_timeout_(false), | ||||
| @ -68,7 +70,7 @@ WatchdogTimer::Run() | ||||
|   ke::AutoLock lock(&cv_); | ||||
| 
 | ||||
|   // 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_) { | ||||
|     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
 | ||||
|     // worst case, we'll miss something that might have timed out but
 | ||||
|     // ended up resuming.
 | ||||
|     uintptr_t frame_id = g_Jit.FrameId(); | ||||
|     if (frame_id != last_frame_id_ || !g_Jit.RunningCode()) { | ||||
|     uintptr_t frame_id = env_->FrameId(); | ||||
|     if (frame_id != last_frame_id_ || !env_->RunningCode()) { | ||||
|       last_frame_id_ = frame_id; | ||||
|       second_timeout_ = false; | ||||
|       continue; | ||||
| @ -105,7 +107,7 @@ WatchdogTimer::Run() | ||||
| 
 | ||||
|     { | ||||
|       // 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
 | ||||
|       // 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
 | ||||
|       // all code writes are 32-bit integer instruction operands, which are
 | ||||
|       // 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
 | ||||
| @ -141,8 +143,8 @@ WatchdogTimer::NotifyTimeoutReceived() | ||||
|   // notification, and is therefore blocked. We take the JIT lock
 | ||||
|   // anyway for sanity.
 | ||||
|   { | ||||
|     ke::AutoLock lock(g_Jit.Mutex()); | ||||
|     g_Jit.UnpatchAllJumpsFromTimeout(); | ||||
|     ke::AutoLock lock(env_->lock()); | ||||
|     env_->UnpatchAllJumpsFromTimeout(); | ||||
|   } | ||||
| 
 | ||||
|   timedout_ = false; | ||||
|  | ||||
| @ -23,12 +23,14 @@ | ||||
| 
 | ||||
| namespace sp { | ||||
| 
 | ||||
| class Environment; | ||||
| 
 | ||||
| typedef bool (*WatchdogCallback)(); | ||||
| 
 | ||||
| class WatchdogTimer : public ke::IRunnable | ||||
| { | ||||
|  public: | ||||
|   WatchdogTimer(); | ||||
|   WatchdogTimer(Environment *env); | ||||
|   ~WatchdogTimer(); | ||||
| 
 | ||||
|   bool Initialize(size_t timeout_ms); | ||||
| @ -43,6 +45,8 @@ class WatchdogTimer : public ke::IRunnable | ||||
|   void Run(); | ||||
| 
 | ||||
|  private: | ||||
|   Environment *env_; | ||||
| 
 | ||||
|   bool terminate_; | ||||
|   size_t timeout_ms_; | ||||
|   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 "interpreter.h" | ||||
| #include "environment.h" | ||||
| #include "code-stubs.h" | ||||
| #include "x86-utils.h" | ||||
| 
 | ||||
| using namespace sp; | ||||
| 
 | ||||
| @ -47,22 +49,6 @@ using namespace sp; | ||||
| 
 | ||||
| #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 | ||||
| OpToCondition(OPCODE op) | ||||
| { | ||||
| @ -267,6 +253,22 @@ GetFunctionName(const sp_plugin_t *plugin, uint32_t offs) | ||||
| } | ||||
| #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 | ||||
| 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); | ||||
|   if (!fn) { | ||||
|     int err; | ||||
|     fn = g_Jit.CompileFunction(runtime, pcode_offs, &err); | ||||
|     fn = CompileFunction(runtime, pcode_offs, &err); | ||||
|     if (!fn) | ||||
|       return err; | ||||
|   } | ||||
| @ -299,7 +301,8 @@ CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char * | ||||
| } | ||||
| 
 | ||||
| Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs) | ||||
|   : rt_(rt), | ||||
|   : env_(Environment::get()), | ||||
|     rt_(rt), | ||||
|     plugin_(rt->plugin()), | ||||
|     error_(SP_ERROR_NONE), | ||||
|     pcode_start_(pcode_offs), | ||||
| @ -365,7 +368,7 @@ Compiler::emit(int *errp) | ||||
|   emitCallThunks(); | ||||
|   emitErrorPaths(); | ||||
| 
 | ||||
|   uint8_t *code = LinkCode(masm); | ||||
|   uint8_t *code = LinkCode(env_, masm); | ||||
|   if (!code) { | ||||
|     *errp = SP_ERROR_OUT_OF_MEMORY; | ||||
|     return NULL; | ||||
| @ -1532,7 +1535,7 @@ Compiler::emitCallThunks() | ||||
| 
 | ||||
|     __ bind(&error); | ||||
|     __ 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()) { | ||||
|     __ bind(dest); | ||||
|     __ movl(eax, code); | ||||
|     __ jmp(g_Jit.GetUniversalReturn()); | ||||
|     __ jmp(ExternalAddress(env_->stubs()->ReturnStub())); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -1797,296 +1800,6 @@ Compiler::emitErrorPaths() | ||||
|     __ bind(&extern_error_); | ||||
|     __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx())); | ||||
|     __ movl(eax, Operand(eax, offsetof(sp_context_t, n_err))); | ||||
|     __ jmp(g_Jit.GetUniversalReturn()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|       } | ||||
|     } | ||||
|     __ jmp(ExternalAddress(env_->stubs()->ReturnStub())); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -26,10 +26,13 @@ | ||||
| #include "sp_vm_basecontext.h" | ||||
| #include "compiled-function.h" | ||||
| #include "opcodes.h" | ||||
| #include <am-thread-utils.h> | ||||
| 
 | ||||
| using namespace SourcePawn; | ||||
| 
 | ||||
| namespace sp { | ||||
| class Environment; | ||||
| } | ||||
| 
 | ||||
| #define JIT_INLINE_ERRORCHECKS  (1<<0) | ||||
| #define JIT_INLINE_NATIVES      (1<<1) | ||||
| #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 | ||||
| { | ||||
|  public: | ||||
| @ -121,6 +105,7 @@ class Compiler | ||||
| 
 | ||||
|  private: | ||||
|   AssemblerX86 masm; | ||||
|   sp::Environment *env_; | ||||
|   PluginRuntime *rt_; | ||||
|   const sp_plugin_t *plugin_; | ||||
|   int error_; | ||||
| @ -145,53 +130,6 @@ class Compiler | ||||
|   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 alt = edx; | ||||
| const Register stk = edi; | ||||
| @ -199,7 +137,8 @@ const Register dat = esi; | ||||
| const Register tmp = ecx; | ||||
| const Register frm = ebx; | ||||
| 
 | ||||
| extern JITX86 g_Jit; | ||||
| CompiledFunction * | ||||
| CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err); | ||||
| 
 | ||||
| #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