diff --git a/README.md b/README.md index 99a6ac332..9b8e707f0 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,7 @@ checkout** — the `C:\dev\...` / `~/dev/...` values are only examples. | Variable | Points to | | --- | --- | | `GC_LIB_PATH` | Boehm GC library (the garbage collector) | -| `LLVM_LIB_PATH` | LLVM/MLIR libraries | +| `LLVM_LIB_PATH` | Not needed any more: programs no longer link an LLVM library. Still accepted so older scripts keep working | | `TSLANG_LIB_PATH` | TSLANG runtime library | | `DEFAULT_LIB_PATH` | Default library | diff --git a/docs/how/debug/debug-shared.bat b/docs/how/debug/debug-shared.bat index ff5874334..ac3fea707 100644 --- a/docs/how/debug/debug-shared.bat +++ b/docs/how/debug/debug-shared.bat @@ -30,7 +30,7 @@ set "SDKPATH=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x64" set "UCRTPATH=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\ucrt\x64" rem --- STATIC CRT link line (matches the test-runner.cpp fix) --- -set "LIBS=libcmtd.lib libvcruntimed.lib libucrtd.lib ntdll.lib TypeScriptAsyncRuntime.lib gc.lib LLVMSupport.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib" +set "LIBS=libcmtd.lib libvcruntimed.lib libucrtd.lib TypeScriptAsyncRuntime.lib gc.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib" set "LIBPATHS=/libpath:"%GC_LIB_PATH%" /libpath:"%LLVM_LIB_PATH%" /libpath:"%tslang_LIB_PATH%" /libpath:"%LIBPATH%" /libpath:"%SDKPATH%" /libpath:"%UCRTPATH%"" cd /d "%WORK%" diff --git a/tslang/include/TypeScript/VSCodeTemplate/Files.h b/tslang/include/TypeScript/VSCodeTemplate/Files.h index a83737359..3cd39dce9 100644 --- a/tslang/include/TypeScript/VSCodeTemplate/Files.h +++ b/tslang/include/TypeScript/VSCodeTemplate/Files.h @@ -350,7 +350,7 @@ add_executable(${PROJECT_NAME} ) # required libs -set(TSLANG_LINK_LIBS "TypeScriptDefaultLib" "TypeScriptAsyncRuntime" "LLVMSupport") +set(TSLANG_LINK_LIBS "TypeScriptDefaultLib" "TypeScriptAsyncRuntime") # Boehm is only referenced by the gc default lib; the rc and none builds allocate through the # CRT and must not drag a collector in. @@ -358,11 +358,8 @@ if (TSLANG_MEMORY_MODEL STREQUAL "gc") list(APPEND TSLANG_LINK_LIBS "gc") endif() -# ntdll provides RtlGetLastNtStatus (pulled in by LLVMSupport) on Windows -if(WIN32) - list(APPEND TSLANG_LINK_LIBS "ntdll") -else() - list(APPEND TSLANG_LINK_LIBS "LLVMDemangle" "stdc++" "m" "pthread" "tinfo" "dl" "rt") +if(NOT WIN32) + list(APPEND TSLANG_LINK_LIBS "stdc++" "m" "pthread" "dl" "rt") endif() target_link_libraries(${PROJECT_NAME} ${TSLANG_LINK_LIBS}) diff --git a/tslang/lib/AsyncRuntimeCommon.inc b/tslang/lib/AsyncRuntimeCommon.inc index eb424bb0c..9cca2db09 100644 --- a/tslang/lib/AsyncRuntimeCommon.inc +++ b/tslang/lib/AsyncRuntimeCommon.inc @@ -17,18 +17,18 @@ // //===----------------------------------------------------------------------===// +#include #include #include #include +#include #include #include +#include #include #include #include -#include "llvm/ADT/StringMap.h" -#include "llvm/Support/ThreadPool.h" - #include "TypeScript/AsyncGCThreads.h" using namespace mlir::runtime; @@ -55,6 +55,103 @@ namespace // Forward declare class defined below. class RefCounted; + // -------------------------------------------------------------------------- // + // The pool the runtime resumes coroutines on. It stands in for llvm::DefaultThreadPool, which + // was the only thing that made every AOT executable link LLVMSupport (and LLVMDemangle on + // Linux). It behaves the same way: one worker per hardware thread, started only when a task + // is queued and no worker is idle, and `wait` returns once the queue is empty and no task is + // running, including tasks queued by other tasks. + // -------------------------------------------------------------------------- // + + class ThreadPool + { + public: + ThreadPool() : maxConcurrency((std::max)(1u, std::thread::hardware_concurrency())), activeTasks(0), stopping(false) + { + } + + ~ThreadPool() + { + { + std::unique_lock lock(mu); + stopping = true; + } + + taskAvailable.notify_all(); + for (auto &worker : workers) + { + worker.join(); + } + } + + ThreadPool(const ThreadPool &) = delete; + ThreadPool &operator=(const ThreadPool &) = delete; + + void async(std::function task) + { + { + std::unique_lock lock(mu); + tasks.push_back(std::move(task)); + // start a worker only if every existing one already has a task to run + if (workers.size() < (std::min)(activeTasks + tasks.size(), maxConcurrency)) + { + workers.emplace_back([this] { processTasks(); }); + } + } + + taskAvailable.notify_one(); + } + + void wait() + { + std::unique_lock lock(mu); + allDone.wait(lock, [this] { return tasks.empty() && activeTasks == 0; }); + } + + unsigned getMaxConcurrency() const + { + return maxConcurrency; + } + + private: + void processTasks() + { + std::unique_lock lock(mu); + while (true) + { + taskAvailable.wait(lock, [this] { return stopping || !tasks.empty(); }); + if (tasks.empty()) + { + // stopping, with nothing left to run + return; + } + + auto task = std::move(tasks.front()); + tasks.pop_front(); + ++activeTasks; + + lock.unlock(); + task(); + lock.lock(); + + --activeTasks; + if (tasks.empty() && activeTasks == 0) + { + allDone.notify_all(); + } + } + } + + const unsigned maxConcurrency; + std::mutex mu; + std::condition_variable taskAvailable; + std::condition_variable allDone; + std::deque> tasks; + std::vector workers; + size_t activeTasks; + bool stopping; + }; + // -------------------------------------------------------------------------- // // AsyncRuntime orchestrates all async operations and Async runtime API is built // on top of the default runtime instance. @@ -78,7 +175,7 @@ namespace return numRefCountedObjects.load(std::memory_order_relaxed); } - llvm::ThreadPoolInterface &getThreadPool() + ThreadPool &getThreadPool() { return threadPool; } @@ -98,7 +195,7 @@ namespace } std::atomic numRefCountedObjects; - llvm::DefaultThreadPool threadPool; + ThreadPool threadPool; }; // -------------------------------------------------------------------------- // diff --git a/tslang/lib/TypeScript/LowerToLLVM.cpp b/tslang/lib/TypeScript/LowerToLLVM.cpp index 38ada7de4..ec984e0f3 100644 --- a/tslang/lib/TypeScript/LowerToLLVM.cpp +++ b/tslang/lib/TypeScript/LowerToLLVM.cpp @@ -296,7 +296,7 @@ class LoadLibraryPermanentlyOpLowering : public TsLlvmPattern(op, loadLibraryPermanentlyFuncOp, ValueRange{transformed.getFilename()}); return success(); @@ -318,7 +318,7 @@ class SearchForAddressOfSymbolOpLowering : public TsLlvmPattern(op, searchForAddressOfSymbolFuncOp, ValueRange{transformed.getSymbolName()}); return success(); diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index ae35b9fc4..d5558bb73 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -1028,15 +1028,16 @@ namespace mlirgen return mlir::failure(); } - // The shared-lib load + symbol resolution call into LLVM's - // sys::DynamicLibrary, which uses std::vector. In debug builds STL + // The shared-lib load + symbol resolution call into the runtime's + // library list (LLVM's sys::DynamicLibrary under the JIT), which uses + // std::vector. In debug builds STL // iterators take a global lock that the CRT only initializes via its // own '_Init_locks'/'initlocks' dynamic initializer (in .CRT$XCU). // FIRST_GLOBAL_CONSTRUCTOR_PRIORITY (100) places this ctor BEFORE that // CRT init -> entering an uninitialized CRITICAL_SECTION -> crash. // Use the same band as the per-symbol __cctors (LAST) so it runs after // 'initlocks'; it is emitted before them, so it still loads the library - // before any LLVMSearchForAddressOfSymbol runs. + // before any tslang_search_for_address_of_symbol runs. addGlobalConstructor(location, fullInitGlobalFuncName); } diff --git a/tslang/lib/TypeScriptAsyncRuntime/CMakeLists.txt b/tslang/lib/TypeScriptAsyncRuntime/CMakeLists.txt index 178ff104b..a7164bc72 100644 --- a/tslang/lib/TypeScriptAsyncRuntime/CMakeLists.txt +++ b/tslang/lib/TypeScriptAsyncRuntime/CMakeLists.txt @@ -7,6 +7,7 @@ endif() add_mlir_library(TypeScriptAsyncRuntime STATIC AsyncRuntime.cpp + DynamicRuntime.cpp EXCLUDE_FROM_LIBMLIR diff --git a/tslang/lib/TypeScriptAsyncRuntime/DynamicRuntime.cpp b/tslang/lib/TypeScriptAsyncRuntime/DynamicRuntime.cpp new file mode 100644 index 000000000..c54129574 --- /dev/null +++ b/tslang/lib/TypeScriptAsyncRuntime/DynamicRuntime.cpp @@ -0,0 +1,119 @@ +//===- DynamicRuntime.cpp - Shared library loading for AOT executables -----===// +// +// A program that imports a shared library loads it from a global constructor and then resolves +// each imported symbol by name, through the two functions below. Under the JIT, TypeScriptRuntime +// supplies them by wrapping llvm::sys::DynamicLibrary (see TypeScriptRuntime/DynamicRuntime.cpp). +// An executable used to call LLVMLoadLibraryPermanently and LLVMSearchForAddressOfSymbol from +// LLVMSupport instead. These are here so that it does not have to link any LLVM library at all. +// +// They behave as LLVM's do for what an executable asks of them: a library is loaded once and +// kept for the life of the process, and a symbol is looked up in the loaded libraries in the +// order they were loaded. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif + +namespace +{ + +// Function-local statics: the library is loaded from a global constructor, which can run before +// this file's own namespace-scope objects are initialized. +std::mutex &handlesMutex() +{ + static std::mutex mu; + return mu; +} + +std::vector &loadedHandles() +{ + static std::vector handles; + return handles; +} + +void *openLibrary(const char *fileName) +{ +#ifdef _WIN32 + auto size = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, nullptr, 0); + if (size <= 0) + { + return nullptr; + } + + std::wstring wideFileName(size, L'\0'); + if (MultiByteToWideChar(CP_UTF8, 0, fileName, -1, wideFileName.data(), size) <= 0) + { + return nullptr; + } + + return reinterpret_cast(LoadLibraryW(wideFileName.c_str())); +#else + return dlopen(fileName, RTLD_LAZY | RTLD_GLOBAL); +#endif +} + +void *findSymbol(void *handle, const char *symbolName) +{ +#ifdef _WIN32 + return reinterpret_cast(GetProcAddress(reinterpret_cast(handle), symbolName)); +#else + return dlsym(handle, symbolName); +#endif +} + +} // namespace + +// 0 when the library is loaded (or already was), 1 when it cannot be. +extern "C" int tslang_load_library_permanently(const char *fileName) +{ + if (!fileName) + { + // LLVM reads a null name as "the process itself"; nothing the compiler emits asks for that + return 1; + } + + auto handle = openLibrary(fileName); + if (!handle) + { + return 1; + } + + std::lock_guard lock(handlesMutex()); + auto &handles = loadedHandles(); + for (auto loaded : handles) + { + if (loaded == handle) + { + // the loader counts every open, and a library loaded for good is never closed, so + // opening it again changes nothing + return 0; + } + } + + handles.push_back(handle); + return 0; +} + +extern "C" void *tslang_search_for_address_of_symbol(const char *symbolName) +{ + std::lock_guard lock(handlesMutex()); + for (auto handle : loadedHandles()) + { + if (auto address = findSymbol(handle, symbolName)) + { + return address; + } + } + + return nullptr; +} diff --git a/tslang/lib/TypeScriptRuntime/AsyncRuntime.cpp b/tslang/lib/TypeScriptRuntime/AsyncRuntime.cpp index ea0fe31ff..63ab7802b 100644 --- a/tslang/lib/TypeScriptRuntime/AsyncRuntime.cpp +++ b/tslang/lib/TypeScriptRuntime/AsyncRuntime.cpp @@ -21,6 +21,9 @@ #ifdef MLIR_ASYNCRUNTIME_DEFINE_FUNCTIONS +// before the .inc: gc.h, which it includes, brings windows.h and its min/max macros +#include "llvm/ADT/StringMap.h" + #include "../AsyncRuntimeCommon.inc" //===----------------------------------------------------------------------===// diff --git a/tslang/lib/TypeScriptRuntime/DynamicRuntime.cpp b/tslang/lib/TypeScriptRuntime/DynamicRuntime.cpp index 431ae256d..e4f018f73 100644 --- a/tslang/lib/TypeScriptRuntime/DynamicRuntime.cpp +++ b/tslang/lib/TypeScriptRuntime/DynamicRuntime.cpp @@ -10,11 +10,13 @@ namespace mlir namespace runtime { -extern "C" int LoadLibraryPermanently(const char* fileName) { +// Named as the generated code calls them: Linux has no .def to rename an export, so the shared +// object's own name for a function is the one the JIT finds. +extern "C" int tslang_load_library_permanently(const char* fileName) { return llvm::sys::DynamicLibrary::LoadLibraryPermanently(fileName); } -extern "C" void *SearchForAddressOfSymbol(const char* symbolName) { +extern "C" void *tslang_search_for_address_of_symbol(const char* symbolName) { return llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(symbolName); } @@ -34,8 +36,8 @@ void init_dynamicruntime(llvm::StringMap &exportSymbols) exportSymbols[name] = reinterpret_cast(ptr); }; - exportSymbol("LLVMLoadLibraryPermanently", &mlir::runtime::LoadLibraryPermanently); - exportSymbol("LLVMSearchForAddressOfSymbol", &mlir::runtime::SearchForAddressOfSymbol); + exportSymbol("tslang_load_library_permanently", &mlir::runtime::tslang_load_library_permanently); + exportSymbol("tslang_search_for_address_of_symbol", &mlir::runtime::tslang_search_for_address_of_symbol); } // NOLINTNEXTLINE(*-identifier-naming): externally called. diff --git a/tslang/lib/TypeScriptRuntime/TypeScriptRuntime.def b/tslang/lib/TypeScriptRuntime/TypeScriptRuntime.def index 572b8a425..0f60d133e 100644 --- a/tslang/lib/TypeScriptRuntime/TypeScriptRuntime.def +++ b/tslang/lib/TypeScriptRuntime/TypeScriptRuntime.def @@ -35,9 +35,9 @@ EXPORTS aligned_alloc=AlignedAlloc aligned_free=AlignedFree - ; --- dynamic-library runtime (DynamicRuntime.cpp, extern "C") --- - LLVMLoadLibraryPermanently=LoadLibraryPermanently - LLVMSearchForAddressOfSymbol=SearchForAddressOfSymbol + ; --- dynamic-library runtime (DynamicRuntime.cpp, extern "C", names already match) --- + tslang_load_library_permanently + tslang_search_for_address_of_symbol ; --- async runtime (AsyncRuntime.cpp, extern "C", names already match) --- mlirAsyncRuntimeAddRef diff --git a/tslang/test/tester/test-runner.cpp b/tslang/test/tester/test-runner.cpp index 8645307b4..01e32de7e 100644 --- a/tslang/test/tester/test-runner.cpp +++ b/tslang/test/tester/test-runner.cpp @@ -11,15 +11,13 @@ #endif #ifdef WIN32 #define TYPESCRIPT_LIB "TypeScriptAsyncRuntime.lib " -#define LLVM_LIBS "LLVMSupport.lib " //#define LIBS "msvcrt" _D_ ".lib ucrt" _D_ ".lib " // static CRT (/MT[d]) to match LLVM/TypeScript runtime libs and gc.lib; mixing static+dynamic CRT crashes at startup -#define LIBS "libcmt" _D_ ".lib libvcruntime" _D_ ".lib libucrt" _D_ ".lib ntdll.lib " +#define LIBS "libcmt" _D_ ".lib libvcruntime" _D_ ".lib libucrt" _D_ ".lib " #else // for Ubuntu 20.04 add -ldl and optionally -rdynamic -#define LIBS "-frtti -fexceptions -lstdc++ -lrt -ldl -lpthread -lm -ltinfo" +#define LIBS "-frtti -fexceptions -lstdc++ -lrt -ldl -lpthread -lm" #define TYPESCRIPT_LIB "-lTypeScriptAsyncRuntime " -#define LLVM_LIBS "-lLLVMSupport -lLLVMDemangle " #endif #ifdef WIN32 @@ -179,8 +177,8 @@ void createCompileBatchFile() batFile << "set GC_LIB_PATH=" << TEST_GCPATH << std::endl; batFile << "%TSLANGEXEPATH%\\tslang.exe --emit=obj --entry-point " << tslang_opt << " " << tslang_opt_ext << " %FILEPATH% -o=%FILENAME%.obj" << std::endl; batFile << "%LLVMEXEPATH%\\lld.exe -flavor link %FILENAME%.obj %LINKER_OPTS% " - << LIBS << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << CMAKE_C_STANDARD_LIBRARIES - << " /libpath:%GC_LIB_PATH% /libpath:%LLVM_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" + << LIBS << TYPESCRIPT_LIB << GC_LIB << CMAKE_C_STANDARD_LIBRARIES + << " /libpath:%GC_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" << " /libpath:%LIBPATH% /libpath:%SDKPATH% /libpath:%UCRTPATH%" << std::endl; batFile << "del %FILENAME%.obj" << std::endl; @@ -207,8 +205,8 @@ void createCompileBatchFile() batFile << "LLVM_LIBPATH=" << TEST_LLVM_LIBPATH << std::endl; batFile << "GC_LIB_PATH=" << TEST_GCPATH << std::endl; batFile << "$TSLANGEXEPATH/tslang --emit=obj --entry-point " << tslang_opt << " " << tslang_opt_ext << " $FILEPATH -relocation-model=pic -o=$FILENAME.o" << std::endl; - batFile << TEST_COMPILER << " -o $FILENAME $LINKER_OPTS -L$LLVM_LIBPATH -L$GC_LIB_PATH -L$TSLANG_LIB_PATH $FILENAME.o " - << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << LIBS << std::endl; + batFile << TEST_COMPILER << " -o $FILENAME $LINKER_OPTS -L$GC_LIB_PATH -L$TSLANG_LIB_PATH $FILENAME.o " + << TYPESCRIPT_LIB << GC_LIB << LIBS << std::endl; batFile << "./$FILENAME 1> $FILENAME.txt 2> $FILENAME.err" << std::endl; batFile << "echo $? > $FILENAME.code" << std::endl; batFile << "rm -f $FILENAME.o" << std::endl; @@ -425,8 +423,8 @@ void createMultiCompileBatchFile(std::string tempOutputFileNameNoExt, std::vecto } batFile << "%LLVMEXEPATH%\\lld.exe -flavor link /out:%FILENAME%.exe " << objs.str() << " " - << LIBS << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << CMAKE_C_STANDARD_LIBRARIES - << " /libpath:%GC_LIB_PATH% /libpath:%LLVM_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" + << LIBS << TYPESCRIPT_LIB << GC_LIB << CMAKE_C_STANDARD_LIBRARIES + << " /libpath:%GC_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" << " /libpath:%LIBPATH% /libpath:%SDKPATH% /libpath:%UCRTPATH%" << std::endl; @@ -462,8 +460,8 @@ void createMultiCompileBatchFile(std::string tempOutputFileNameNoExt, std::vecto } batFile << TEST_COMPILER << " -o $FILENAME " << objs.str() - << "-L$LLVM_LIBPATH -L$GC_LIB_PATH -L$TSLANG_LIB_PATH " - << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << LIBS << std::endl; + << "-L$GC_LIB_PATH -L$TSLANG_LIB_PATH " + << TYPESCRIPT_LIB << GC_LIB << LIBS << std::endl; batFile << "./$FILENAME 1> $FILENAME.txt 2> $FILENAME.err" << std::endl; batFile << "echo $? > $FILENAME.code" << std::endl; @@ -546,8 +544,8 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector batFile << sharedBat.str(); batFile << "%LLVMEXEPATH%\\lld.exe -flavor link /out:" << shared_filenameNoExt << ".dll " << linker_opt << " " << shared_objs.str() << " " - << LIBS << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << CMAKE_C_STANDARD_LIBRARIES - << " /libpath:%GC_LIB_PATH% /libpath:%LLVM_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" + << LIBS << TYPESCRIPT_LIB << GC_LIB << CMAKE_C_STANDARD_LIBRARIES + << " /libpath:%GC_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" << " /libpath:%LIBPATH% /libpath:%SDKPATH% /libpath:%UCRTPATH%" << std::endl; @@ -574,8 +572,8 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector batFile << shared_filenameNoExt << ".lib "; } - batFile << LIBS << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << CMAKE_C_STANDARD_LIBRARIES - << " /libpath:%GC_LIB_PATH% /libpath:%LLVM_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" + batFile << LIBS << TYPESCRIPT_LIB << GC_LIB << CMAKE_C_STANDARD_LIBRARIES + << " /libpath:%GC_LIB_PATH% /libpath:%TSLANG_LIB_PATH%" << " /libpath:%LIBPATH% /libpath:%SDKPATH% /libpath:%UCRTPATH%" << std::endl; @@ -648,8 +646,8 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector batFile << sharedBat.str(); batFile << TEST_COMPILER << " " << linker_opt << " -o lib" << shared_filenameNoExt << ".so " << shared_objs.str() - << "-L$LLVM_LIBPATH -L$GC_LIB_PATH -L$TSLANG_LIB_PATH " - << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << LIBS << std::endl; + << "-L$GC_LIB_PATH -L$TSLANG_LIB_PATH " + << TYPESCRIPT_LIB << GC_LIB << LIBS << std::endl; batFile << "rm -f " << shared_objs.str() << std::endl; if (jitRun) @@ -662,7 +660,7 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector { batFile << execBat.str(); batFile << TEST_COMPILER << " -o $FILENAME " << exec_objs.str() << " "; - batFile << "-L$LLVM_LIBPATH -L$GC_LIB_PATH -L$TSLANG_LIB_PATH "; + batFile << "-L$GC_LIB_PATH -L$TSLANG_LIB_PATH "; if (sharedLib) { // dynamics and compile-time shared modes both link the produced shared lib; @@ -670,7 +668,7 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector batFile << "-L`pwd` -Wl,-rpath=`pwd` -l" << shared_filenameNoExt << " "; } - batFile << TYPESCRIPT_LIB << GC_LIB << LLVM_LIBS << LIBS << std::endl; + batFile << TYPESCRIPT_LIB << GC_LIB << LIBS << std::endl; batFile << "rm -f " << exec_objs.str() << std::endl; diff --git a/tslang/tslang/exe.cpp b/tslang/tslang/exe.cpp index 27dfc06c0..80064765d 100644 --- a/tslang/tslang/exe.cpp +++ b/tslang/tslang/exe.cpp @@ -131,16 +131,6 @@ void checkGCLibPath(std::string path) checkFileExistsAtPath(path, libName); } -void checkLLVMLibPath(std::string path) -{ -#ifdef WIN32 - const auto libName = "LLVMSupport.lib"; -#else - const auto libName = "libLLVMSupport.a"; -#endif - checkFileExistsAtPath(path, libName); -} - void checkTslangLibPath(std::string path) { #ifdef WIN32 @@ -247,13 +237,11 @@ std::string getLLVMLibPath() { if (!llvmlibpath.empty()) { - checkLLVMLibPath(llvmlibpath); return llvmlibpath; } if (auto llvmLibEnvValue = llvm::sys::Process::GetEnv("LLVM_LIB_PATH")) { - checkLLVMLibPath(llvmLibEnvValue.value()); return llvmLibEnvValue.value(); } @@ -399,12 +387,10 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio std::string gcLibPathOpt; std::string tslangLibPathOpt; - std::string llvmLibPathOpt; std::string emsdkSysRootPathOpt; std::string defaultLibPathOpt; std::string defaultLibFileOpt; - auto isLLVMLibNeeded = true; auto isTslangLibNeeded = true; auto os = TheTriple.getOS(); @@ -418,7 +404,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio if (wasm) { - isLLVMLibNeeded = false; isTslangLibNeeded = false; } @@ -561,16 +546,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio } } - // add logic to detect if libs are used and needed - if (isLLVMLibNeeded) - { - llvmLibPathOpt = getLibsPathOpt(getLLVMLibPath()); - if (!llvmLibPathOpt.empty()) - { - args.push_back(llvmLibPathOpt.c_str()); - } - } - if (isTslangLibNeeded) { tslangLibPathOpt = getLibsPathOpt(getTslangLibPath()); @@ -604,8 +579,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio args.push_back("-llibvcruntimed"); args.push_back("-Wl,-nodefaultlib:libcmt"); } - - args.push_back("-lntdll"); } // tslang libs @@ -636,15 +609,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio args.push_back("-lTypeScriptAsyncRuntime"); } - if (isLLVMLibNeeded) - { - args.push_back("-lLLVMSupport"); - if (!win) - { - args.push_back("-lLLVMDemangle"); - } - } - if (!win && !wasm) { if (RM && *RM == llvm::Reloc::PIC_) @@ -675,7 +639,6 @@ int buildExe(int argc, char **argv, std::string objFileName, std::string additio args.push_back("-lstdc++"); args.push_back("-lm"); args.push_back("-lpthread"); - args.push_back("-ltinfo"); args.push_back("-ldl"); args.push_back("-lrt"); diff --git a/tslang/tslang/jit.cpp b/tslang/tslang/jit.cpp index 3bc0fbbb9..e3f7185f5 100644 --- a/tslang/tslang/jit.cpp +++ b/tslang/tslang/jit.cpp @@ -287,6 +287,21 @@ static void jitEnableGCThreads() } } +// A program importing a shared library loads it and resolves its symbols through these two +// (tslang_load_library_permanently / tslang_search_for_address_of_symbol). An executable gets +// them from TypeScriptAsyncRuntime; under the JIT they are defined here, for every memory model, +// because TypeScriptRuntime is only loaded for `gc`. They go through this process's own +// DynamicLibrary, so a lookup also sees the libraries passed with `--shared-libs`. +static int jitLoadLibraryPermanently(const char *fileName) +{ + return llvm::sys::DynamicLibrary::LoadLibraryPermanently(fileName); +} + +static void *jitSearchForAddressOfSymbol(const char *symbolName) +{ + return llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(symbolName); +} + // A failing `assert` in compiled code calls `_assert`, and under --emit=jit that call lands // in whichever CRT the process resolver reaches first - ucrtbase.dll, whose report mode is // nobody's to set from here, and which puts the failure up as a modal message box. In an @@ -677,6 +692,22 @@ int runJit(int argc, char **argv, mlir::ModuleOp module, CompileOptions &compile jit->getMainJITDylib().addGenerator(std::move(*generator)); + // Definitions win over the generator, so these also shadow TypeScriptRuntime's copies + { + llvm::orc::MangleAndInterner interner(jit->getExecutionSession(), jit->getDataLayout()); + llvm::orc::SymbolMap dynamicLibrarySymbols; + dynamicLibrarySymbols[interner("tslang_load_library_permanently")] = { + llvm::orc::ExecutorAddr::fromPtr(&jitLoadLibraryPermanently), llvm::JITSymbolFlags::Exported}; + dynamicLibrarySymbols[interner("tslang_search_for_address_of_symbol")] = { + llvm::orc::ExecutorAddr::fromPtr(&jitSearchForAddressOfSymbol), llvm::JITSymbolFlags::Exported}; + if (auto err = jit->getMainJITDylib().define(llvm::orc::absoluteSymbols(std::move(dynamicLibrarySymbols)))) + { + llvm::WithColor::error(llvm::errs(), "tslang") << "failed to define the shared library loader, error: " << err << "\n"; + llvm::consumeError(std::move(err)); + return -1; + } + } + #ifdef _WIN32 // Bind CRT entry points to tslang.exe's static CRT (/MT[d]) explicitly: the // process generator above resolves from DLL export tables only, so without diff --git a/tslang/tslang/tslang.cpp b/tslang/tslang/tslang.cpp index 6418e45da..3f28e56c0 100644 --- a/tslang/tslang/tslang.cpp +++ b/tslang/tslang/tslang.cpp @@ -139,7 +139,7 @@ cl::opt embedExportDeclarationsAction("embed-declarations", cl::desc("Embe cl::opt defaultlibpath("default-lib-path", cl::desc("JS library path. Should point to folder/directory with subfolder '" DEFAULT_LIB_DIR "' or DEFAULT_LIB_PATH environmental variable"), cl::value_desc("defaultlibpath"), cl::cat(TypeScriptCompilerBuildCategory)); cl::opt gclibpath("gc-lib-path", cl::desc("GC library path. Should point to file 'gc.lib' or GC_LIB_PATH environmental variable"), cl::value_desc("gclibpath"), cl::cat(TypeScriptCompilerBuildCategory)); cl::opt gcsharedlibpath("gc-shared-lib-path", cl::desc("Shared GC library path: the directory with gc.dll's import library 'gc.lib' (gc.dll beside it or in '../bin'). Used under -mm=gc for --emit=dll and for executables that import a shared library, so the process has one collector. Or GC_SHARED_LIB_PATH environmental variable; defaults to '/gcdll'"), cl::value_desc("gcsharedlibpath"), cl::cat(TypeScriptCompilerBuildCategory)); -cl::opt llvmlibpath("llvm-lib-path", cl::desc("LLVM library path. Should point to file 'LLVMSupport.lib' and 'LLVMDemangle' in linux or LLVM_LIB_PATH environmental variable"), cl::value_desc("llvmlibpath"), cl::cat(TypeScriptCompilerBuildCategory)); +cl::opt llvmlibpath("llvm-lib-path", cl::desc("LLVM library path (or LLVM_LIB_PATH environmental variable). Not needed any more: programs no longer link an LLVM library. Accepted so older scripts keep working"), cl::value_desc("llvmlibpath"), cl::cat(TypeScriptCompilerBuildCategory)); cl::opt tslanglibpath("tslang-lib-path", cl::desc("TypeScript Compiler Runtime library path. Should point to file 'TypeScriptAsyncRuntime.lib' or TSLANG_LIB_PATH environmental variable"), cl::value_desc("tslanglibpath"), cl::cat(TypeScriptCompilerBuildCategory)); cl::opt emsdksysrootpath("emsdk-sysroot-path", cl::desc("TypeScript Compiler Runtime library path. Should point to dir '<...>/emsdk/upstream/emscripten/cache/sysroot' or EMSDK_SYSROOT_PATH environmental variable. (used when '-mtriple=wasm32-pc-emscripten')"), cl::value_desc("emsdksysrootpath"), cl::cat(TypeScriptCompilerBuildCategory)); cl::list libs{"lib", cl::desc("Libraries to link statically. (used in --emit=exe)"), cl::ZeroOrMore, cl::MiscFlags::CommaSeparated, cl::cat(TypeScriptCompilerBuildCategory)};