From 19cab0fe136db2b99d660ac10106f150d402d79b Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Tue, 10 May 2011 15:17:06 -0400 Subject: [PATCH] Add LLVM-based VM Signed-off-by: Matt Turner --- Makefile | 95 +++++++++++++++++++++++++++++----- code/qcommon/files.c | 12 ++++ code/qcommon/qcommon.h | 2 +- code/qcommon/vm.c | 41 ++++++++++++++- code/qcommon/vm_llvm.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++ code/qcommon/vm_llvm.h | 10 ++++ code/qcommon/vm_local.h | 5 ++ code/server/sv_client.c | 29 ++++++++--- code/server/sv_init.c | 12 +++-- 9 files changed, 308 insertions(+), 27 deletions(-) create mode 100644 code/qcommon/vm_llvm.cpp create mode 100644 code/qcommon/vm_llvm.h diff --git a/Makefile b/Makefile index d3e9e49..98cc365 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,10 @@ ifndef BUILD_MISSIONPACK BUILD_MISSIONPACK= endif +ifndef USE_LLVM + USE_LLVM=0 +endif + ifneq ($(PLATFORM),darwin) BUILD_CLIENT_SMP = 0 endif @@ -219,6 +223,11 @@ ifeq ($(wildcard .git/svn/.metadata),.git/svn/.metadata) endif endif +ifeq ($(USE_LLVM),1) + LD=$(CXX) +else + LD=$(CC) +endif ############################################################################# # SETUP AND BUILD -- LINUX @@ -271,14 +280,14 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu")) CLIENT_CFLAGS += -DUSE_CODEC_VORBIS endif - OPTIMIZEVM = -O3 -funroll-loops -fomit-frame-pointer + ifeq ($(USE_LLVM),1) + BASE_CFLAGS += -DUSE_LLVM + endif + + OPTIMIZEVM = -O3 -march=native -funroll-loops -fomit-frame-pointer OPTIMIZE = $(OPTIMIZEVM) -ffast-math ifeq ($(ARCH),x86_64) - OPTIMIZEVM = -O3 -fomit-frame-pointer -funroll-loops \ - -falign-loops=2 -falign-jumps=2 -falign-functions=2 \ - -fstrength-reduce - OPTIMIZE = $(OPTIMIZEVM) -ffast-math HAVE_VM_COMPILED = true else ifeq ($(ARCH),i386) @@ -342,6 +351,10 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu")) CLIENT_LIBS += -lrt endif + ifeq ($(USE_LLVM),1) + LIBS += -L/usr/lib64/llvm/ $(shell llvm-config --libs) -pthread + endif + ifeq ($(ARCH),i386) # linux32 make ... BASE_CFLAGS += -m32 @@ -779,6 +792,33 @@ ifeq ($(PLATFORM),sunos) else # ifeq sunos ############################################################################# +# SETUP AND BUILD -- LLVM +############################################################################# + +ifeq ($(PLATFORM),llvm) + + CC=clang + LD=llvm-link + ARCH=llvm + + HAVE_VM_COMPILED=true + + BASE_CFLAGS += -emit-llvm + DEBUG_CFLAGS = $(BASE_CFLAGS) -O0 + RELEASE_CFLAGS = $(BASE_CFLAGS) -DNDEBUG $(OPTIMIZE) + + LDFLAGS= + SHLIBEXT=bc + SHLIBLDFLAGS=$(LDFLAGS) + + BUILD_CLIENT = 0 + BUILD_CLIENT_SMP = 0 + BUILD_SERVER = 0 + BUILD_GAME_QVM = 0 + +else # ifeq llvm + +############################################################################# # SETUP AND BUILD -- GENERIC ############################################################################# BASE_CFLAGS=-DNO_VM_COMPILED @@ -796,6 +836,7 @@ endif #OpenBSD endif #NetBSD endif #IRIX endif #SunOS +endif #LLVM TARGETS = @@ -895,6 +936,11 @@ $(echo_cmd) "CC $<" $(Q)$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) $(CLIENT_CFLAGS) $(OPTIMIZE) -o $@ -c $< endef +define DO_CXX +$(echo_cmd) "CXX $<" +$(Q)$(CXX) $(NOTSHLIBCFLAGS) $(CFLAGS) -o $@ -c $< +endef + define DO_SMP_CC $(echo_cmd) "SMP_CC $<" $(Q)$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) $(CLIENT_CFLAGS) $(OPTIMIZE) -DSMP -o $@ -c $< @@ -967,6 +1013,11 @@ $(echo_cmd) "DED_CC $<" $(Q)$(CC) $(NOTSHLIBCFLAGS) -DDEDICATED $(CFLAGS) $(SERVER_CFLAGS) $(OPTIMIZE) -o $@ -c $< endef +define DO_DED_CXX +$(echo_cmd) "DED_CXX $<" +$(Q)$(CXX) $(NOTSHLIBCFLAGS) -DDEDICATED $(CFLAGS) -o $@ -c $< +endef + define DO_WINDRES $(echo_cmd) "WINDRES $<" $(Q)$(WINDRES) -i $< -o $@ @@ -1001,6 +1052,8 @@ targets: makedirs @echo " COMPILE_PLATFORM: $(COMPILE_PLATFORM)" @echo " COMPILE_ARCH: $(COMPILE_ARCH)" @echo " CC: $(CC)" + @echo " CXX: $(CXX)" + @echo " LD: $(LD)" @echo "" @echo " CFLAGS:" -@for i in $(CFLAGS); \ @@ -1402,6 +1455,10 @@ ifeq ($(ARCH),x86) $(B)/client/snapvectora.o endif +ifeq ($(USE_LLVM),1) + Q3OBJ += $(B)/client/vm_llvm.o +endif + ifeq ($(HAVE_VM_COMPILED),true) ifeq ($(ARCH),i386) Q3OBJ += $(B)/client/vm_x86.o @@ -1456,13 +1513,13 @@ Q3POBJ_SMP += \ $(B)/ioquake3$(FULLBINEXT): $(Q3OBJ) $(Q3POBJ) $(LIBSDLMAIN) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) \ + $(Q)$(LD) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) \ -o $@ $(Q3OBJ) $(Q3POBJ) \ $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) $(B)/ioquake3-smp$(FULLBINEXT): $(Q3OBJ) $(Q3POBJ_SMP) $(LIBSDLMAIN) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) $(THREAD_LDFLAGS) \ + $(Q)$(LD) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) $(THREAD_LDFLAGS) \ -o $@ $(Q3OBJ) $(Q3POBJ_SMP) \ $(THREAD_LIBS) $(LIBSDLMAIN) $(CLIENT_LIBS) $(LIBS) @@ -1563,6 +1620,10 @@ ifeq ($(ARCH),x86) $(B)/ded/matha.o endif +ifeq ($(USE_LLVM),1) + Q3DOBJ += $(B)/ded/vm_llvm.o +endif + ifeq ($(HAVE_VM_COMPILED),true) ifeq ($(ARCH),i386) Q3DOBJ += $(B)/ded/vm_x86.o @@ -1608,7 +1669,7 @@ endif $(B)/ioq3ded$(FULLBINEXT): $(Q3DOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(Q3DOBJ) $(LIBS) + $(Q)$(LD) $(CFLAGS) $(LDFLAGS) -o $@ $(Q3DOBJ) $(LIBS) @@ -1648,7 +1709,7 @@ Q3CGVMOBJ = $(Q3CGOBJ_:%.o=%.asm) $(B)/baseq3/cgame$(SHLIBNAME): $(Q3CGOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ) $(B)/baseq3/vm/cgame.qvm: $(Q3CGVMOBJ) $(CGDIR)/cg_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1692,7 +1753,7 @@ MPCGVMOBJ = $(MPCGOBJ_:%.o=%.asm) $(B)/missionpack/cgame$(SHLIBNAME): $(MPCGOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ) $(B)/missionpack/vm/cgame.qvm: $(MPCGVMOBJ) $(CGDIR)/cg_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1745,7 +1806,7 @@ Q3GVMOBJ = $(Q3GOBJ_:%.o=%.asm) $(B)/baseq3/qagame$(SHLIBNAME): $(Q3GOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ) $(B)/baseq3/vm/qagame.qvm: $(Q3GVMOBJ) $(GDIR)/g_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1796,7 +1857,7 @@ MPGVMOBJ = $(MPGOBJ_:%.o=%.asm) $(B)/missionpack/qagame$(SHLIBNAME): $(MPGOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ) $(B)/missionpack/vm/qagame.qvm: $(MPGVMOBJ) $(GDIR)/g_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1859,7 +1920,7 @@ Q3UIVMOBJ = $(Q3UIOBJ_:%.o=%.asm) $(B)/baseq3/ui$(SHLIBNAME): $(Q3UIOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ) $(B)/baseq3/vm/ui.qvm: $(Q3UIVMOBJ) $(UIDIR)/ui_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1887,7 +1948,7 @@ MPUIVMOBJ = $(MPUIOBJ_:%.o=%.asm) $(B)/missionpack/ui$(SHLIBNAME): $(MPUIOBJ) $(echo_cmd) "LD $@" - $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(MPUIOBJ) + $(Q)$(LD) $(SHLIBLDFLAGS) -o $@ $(MPUIOBJ) $(B)/missionpack/vm/ui.qvm: $(MPUIVMOBJ) $(UIDIR)/ui_syscalls.asm $(Q3ASM) $(echo_cmd) "Q3ASM $@" @@ -1941,6 +2002,8 @@ $(B)/client/%.o: $(SYSDIR)/%.m $(B)/client/%.o: $(SYSDIR)/%.rc $(DO_WINDRES) +$(B)/client/vm_llvm.o: $(CMDIR)/vm_llvm.cpp + $(DO_CXX) $(B)/ded/%.o: $(ASMDIR)/%.s $(DO_AS) @@ -1969,6 +2032,9 @@ $(B)/ded/%.o: $(SYSDIR)/%.rc $(B)/ded/%.o: $(NDIR)/%.c $(DO_DED_CC) +$(B)/ded/vm_llvm.o: $(CMDIR)/vm_llvm.cpp + $(DO_DED_CXX) + # Extra dependencies to ensure the SVN version is incorporated ifeq ($(USE_SVN),1) $(B)/client/cl_console.o : .svn/entries @@ -1976,6 +2042,7 @@ ifeq ($(USE_SVN),1) $(B)/ded/common.o : .svn/entries endif +$(CMDIR)/vm_llvm.c: ############################################################################# ## GAME MODULE RULES diff --git a/code/qcommon/files.c b/code/qcommon/files.c index f6d8e3d..e3ba011 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -1185,6 +1185,18 @@ int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueF pak->referenced |= FS_UI_REF; } + // LLVM - if we were asked to open an llvm, reference that. + // this assumes that we don't reference both, qvm AND llvm! + if (!(pak->referenced & FS_QAGAME_REF) && strstr(filename, "qagamellvm.bc")) { + pak->referenced |= FS_QAGAME_REF; + } + if (!(pak->referenced & FS_CGAME_REF) && strstr(filename, "cgamellvm.bc")) { + pak->referenced |= FS_CGAME_REF; + } + if (!(pak->referenced & FS_UI_REF) && strstr(filename, "uillvm.bc")) { + pak->referenced |= FS_UI_REF; + } + if ( uniqueFILE ) { // open a new file on the pakfile fsh[*file].handleFiles.file.z = unzOpen (pak->pakFilename); diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index c20edde..1f65bca 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -348,7 +348,7 @@ typedef enum { void VM_Init( void ); vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ); -// module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" +// module should be bare: "cgame", not "cgame.dll", "vm/cgame.qvm" or "cgamellvm.bc" void VM_Free( vm_t *vm ); void VM_Clear(void); diff --git a/code/qcommon/vm.c b/code/qcommon/vm.c index d3fb85b..5ca3582 100644 --- a/code/qcommon/vm.c +++ b/code/qcommon/vm.c @@ -35,6 +35,9 @@ and one exported function: Perform #include "vm_local.h" +#if USE_LLVM +#include "vm_llvm.h" +#endif // USE_LLVM vm_t *currentVM = NULL; vm_t *lastVM = NULL; @@ -485,7 +488,12 @@ vm_t *VM_Restart( vm_t *vm ) { vmHeader_t *header; // DLL's can't be restarted in place - if ( vm->dllHandle ) { + if ( vm->dllHandle +#if USE_LLVM + // llvm's neither, at least not for now + || vm->llvmModule +#endif + ) { char name[MAX_QPATH]; intptr_t (*systemCall)( intptr_t *parms ); @@ -564,10 +572,25 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), return vm; } +#if USE_LLVM + Com_Printf( "Failed to load dll, looking for llvm.\n" ); +#else Com_Printf( "Failed to load dll, looking for qvm.\n" ); +#endif // USE_LLVM interpret = VMI_COMPILED; } +#if USE_LLVM + // try to load the llvm + Com_Printf( "Loading llvm file %s.\n", vm->name ); + vm->llvmModule = VM_LoadLLVM( vm, VM_DllSyscall ); + if ( vm->llvmModule ) { + return vm; + } + + Com_Printf( "Failed to load llvm, looking for qvm.\n" ); +#endif // USE_LLVM + // load the image if( !( header = VM_LoadQVM( vm, qtrue ) ) ) { return NULL; @@ -641,6 +664,14 @@ void VM_Free( vm_t *vm ) { Sys_UnloadDll( vm->dllHandle ); Com_Memset( vm, 0, sizeof( *vm ) ); } + +#if USE_LLVM + if (vm->llvmModule ) { + VM_UnloadLLVM( vm->llvmModule ); + Com_Memset( vm, 0, sizeof( *vm ) ); + } +#endif + #if 0 // now automatically freed by hunk if ( vm->codeBase ) { Z_Free( vm->codeBase ); @@ -885,6 +916,14 @@ void VM_VmInfo_f( void ) { Com_Printf( "native\n" ); continue; } + +#if USE_LLVM + if ( vm->llvmModule ) { + Com_Printf( "llvm\n" ); + continue; + } +#endif // USE_LLVM + if ( vm->compiled ) { Com_Printf( "compiled on load\n" ); } else { diff --git a/code/qcommon/vm_llvm.cpp b/code/qcommon/vm_llvm.cpp new file mode 100644 index 0000000..8d666a8 --- /dev/null +++ b/code/qcommon/vm_llvm.cpp @@ -0,0 +1,129 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "vm_local.h" +#ifdef __cplusplus +} +#endif + +#include "vm_llvm.h" + +#define INT64_C(C) ((int64_t) C ## LL) +#define UINT64_C(C) ((uint64_t) C ## ULL) + +#define __STDC_CONSTANT_MACROS +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#ifdef __cplusplus +extern "C" { +#endif + +static ExecutionEngine *engine = NULL; + +void *VM_LoadLLVM( vm_t *vm, intptr_t (*systemcalls)(intptr_t, ...) ) { + char name[MAX_QPATH]; + char filename[MAX_QPATH]; + char *bytes; + std::string error; + + COM_StripExtension( vm->name, name, sizeof(name) ); + Com_sprintf( filename, sizeof(filename), "%sllvm.bc", name ); + int len = FS_ReadFile( filename, (void **)&bytes ); + + if ( !bytes ) { + Com_Printf( "Couldn't load llvm file: %s\n", filename ); + return NULL; + } + + MemoryBuffer *buffer = MemoryBuffer::getMemBuffer(StringRef(bytes, len)); + Module *module = ParseBitcodeFile( buffer, getGlobalContext(), &error ); + delete buffer; + FS_FreeFile( bytes ); + + if ( !module ) { + Com_Printf( "Couldn't parse llvm file: %s: %s\n", filename, error.c_str() ); + return NULL; + } + + PassManager p; + llvm::Pass *InliningPass = createFunctionInliningPass(275); + p.add( new TargetData( module ) ); + createStandardModulePasses(&p, 3, false, true, true, true, false, InliningPass); + p.run( *module ); + + if ( !engine ) { + InitializeNativeTarget(); + + std::string str; + /* LLVM has gone through some churn since the initial version of the patch was + * written in 2009. For some unknown reason, the last argument of create() has + * to be false, otherwise LLVM will seg fault when JIT compiling vmMain in the + * call to engine->getPointerToFunction below. This is OK though, since the + * parameter is only there for backwards compatibility and they plan to reverse + * its default to false at some point in the future. */ + engine = ExecutionEngine::create( module, false, &str, CodeGenOpt::Default, false ); + if ( !engine ) { + Com_Printf("Couldn't create ExecutionEngine: %s\n", str.c_str()); + return NULL; + } + } else { + engine->addModule( module ); + } + + Function *dllEntry_ptr = module->getFunction( std::string("dllEntry") ); + if ( !dllEntry_ptr ) { + Com_Printf("Couldn't get function address of dllEntry\n"); + return NULL; + } + void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ) = (void (*)(intptr_t (*syscallptr)(intptr_t, ...)))engine->getPointerToFunction( dllEntry_ptr ); + dllEntry( systemcalls ); + + Function *vmMain_ptr = module->getFunction( std::string("vmMain") ); + if ( !vmMain_ptr ) { + Com_Printf("Couldn't get function address of vmMain\n"); + return NULL; + } + intptr_t(*fp)(int, ...) = (intptr_t(*)(int, ...))engine->getPointerToFunction( vmMain_ptr ); + + vm->entryPoint = fp; + + if ( com_developer->integer ) { + Com_Printf("Loaded LLVM %s with module==%p\n", name, module); + } + + return module; +} + +void VM_UnloadLLVM( void *llvmModule ) { + if ( !llvmModule ) { + Com_Printf( "VM_UnloadLLVM called with NULL pointer\n" ); + return; + } + + if ( com_developer->integer ) { + Com_Printf( "Unloading LLVM with module==%p\n", llvmModule ); + } + + if ( !engine->removeModule( (Module *)llvmModule ) ) { + Com_Printf( "Couldn't remove llvm\n" ); + return; + } + delete (Module *)llvmModule; +} + +#ifdef __cplusplus +} +#endif diff --git a/code/qcommon/vm_llvm.h b/code/qcommon/vm_llvm.h new file mode 100644 index 0000000..1490e2b --- /dev/null +++ b/code/qcommon/vm_llvm.h @@ -0,0 +1,10 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void *VM_LoadLLVM( vm_t *vm, intptr_t (*systemcalls)(intptr_t, ...) ); +void VM_UnloadLLVM( void *llvmModule ); + +#ifdef __cplusplus +} +#endif diff --git a/code/qcommon/vm_local.h b/code/qcommon/vm_local.h index f6db701..61f4d41 100644 --- a/code/qcommon/vm_local.h +++ b/code/qcommon/vm_local.h @@ -146,6 +146,11 @@ struct vm_s { intptr_t (QDECL *entryPoint)( int callNum, ... ); void (*destroy)(vm_t* self); +#if USE_LLVM + // for llvm modules + void *llvmModule; +#endif // USE_LLVM + // for interpreted modules qboolean currentlyInterpreting; diff --git a/code/server/sv_client.c b/code/server/sv_client.c index f4ebe60..8c31fe0 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -1169,11 +1169,6 @@ static void SV_VerifyPaks_f( client_t *cl ) { if ( sv_pure->integer != 0 ) { bGood = qtrue; - nChkSum1 = nChkSum2 = 0; - // we run the game, so determine which cgame and ui the client "should" be running - bGood = (FS_FileIsInPAK("vm/cgame.qvm", &nChkSum1) == 1); - if (bGood) - bGood = (FS_FileIsInPAK("vm/ui.qvm", &nChkSum2) == 1); nClientPaks = Cmd_Argc(); @@ -1207,16 +1202,36 @@ static void SV_VerifyPaks_f( client_t *cl ) { } // verify first to be the cgame checksum pArg = Cmd_Argv(nCurArg++); - if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum1 ) { + if (!pArg || *pArg == '@' ) { bGood = qfalse; break; } + // we run the game, so determine which cgame the client "should" be running + nChkSum1 = atoi(pArg); + if (FS_FileIsInPAK("vm/cgame.qvm", &nChkSum2) != 1 || nChkSum2 != nChkSum1) { + // LLVM - if the client is not running QVM, it might still be running llvm. + if (FS_FileIsInPAK("cgamellvm.bc", &nChkSum2) != 1 || nChkSum2 != nChkSum1) { + bGood = qfalse; + break; + } + } + // verify the second to be the ui checksum pArg = Cmd_Argv(nCurArg++); - if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum2 ) { + if (!pArg || *pArg == '@') { bGood = qfalse; break; } + // we run the game, so determine which ui the client "should" be running + nChkSum1 = atoi(pArg); + if (FS_FileIsInPAK("vm/ui.qvm", &nChkSum2) != 1 || nChkSum2 != nChkSum1) { + // LLVM - if the client is not running QVM, it might still be running llvm. + if (FS_FileIsInPAK("uillvm.bc", &nChkSum2) != 1 || nChkSum2 != nChkSum1) { + bGood = qfalse; + break; + } + } + // should be sitting at the delimeter now pArg = Cmd_Argv(nCurArg++); if (*pArg != '@') { diff --git a/code/server/sv_init.c b/code/server/sv_init.c index 5bf1302..de9d0c0 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -376,15 +376,19 @@ static void SV_ClearServer(void) { ================ SV_TouchCGame - touch the cgame.vm so that a pure client can load it if it's in a seperate pk3 + touch cgame so that a pure client can load it if it's in a seperate pk3 ================ */ static void SV_TouchCGame(void) { fileHandle_t f; - char filename[MAX_QPATH]; - Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", "cgame" ); - FS_FOpenFileRead( filename, &f, qfalse ); + FS_FOpenFileRead( "vm/cgame.qvm", &f, qfalse ); + if ( f ) { + FS_FCloseFile( f ); + } + + // LLVM - even if the server doesn't use llvm itself, it should still add the references. + FS_FOpenFileRead( "cgamellvm.bc", &f, qfalse ); if ( f ) { FS_FCloseFile( f ); } -- 1.7.3.4