Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for WASI builds #91051

Closed
wants to merge 2 commits into from
Closed

Add support for WASI builds #91051

wants to merge 2 commits into from

Conversation

veluca93
Copy link

@veluca93 veluca93 commented May 4, 2024

This PR modifies the LLVM source code to compile (and run) in a WASI environment.

The question of whether having WASI support in LLVM is one that doesn't have a clear answer for me (although of course I can see some use cases), but since the patch ended up being small enough I figured I'd create a PR and see what the LLVM community would make of it :-)

The code compiles & runs successfully when compiled with an unmodified wasi-libc (I only tested running clang++ and wasm-ld).

Caveats:

  • a bunch of things are not supported. Most importantly, executing binaries is not supported, but also memory mapping, signals, dynamic linking, set/longjump, and JIT.
  • There are no tests. I also would not know how to go about adding tests.
  • Some projects are disabled on WASI.

@veluca93 veluca93 requested a review from a team as a code owner May 4, 2024 07:31
Copy link

github-actions bot commented May 4, 2024

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@veluca93
Copy link
Author

Ping

(I suspect that the assigned reviewers are likely not the most appropriate, but I would be unsure on how to change them and to whom)

@jyknight
Copy link
Member

https://discourse.llvm.org/t/rfc-building-llvm-for-webassembly/79073 and PR #92677 also just arrived at the scene with a different take on implementing the same thing. Looks like there's significant interest in this area!

@veluca93
Copy link
Author

https://discourse.llvm.org/t/rfc-building-llvm-for-webassembly/79073 and PR #92677 also just arrived at the scene with a different take on implementing the same thing. Looks like there's significant interest in this area!

That's cool! That PR seems significantly more polished than mine, happy to go with that one instead :-)

@llvmbot llvmbot added cmake Build system in general and CMake in particular clang Clang issues not falling into any other category libc++abi libc++abi C++ Runtime Library. Not libc++. clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' llvm:support llvm:transforms llvm:adt labels Jun 8, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jun 8, 2024

@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-libcxxabi

@llvm/pr-subscribers-clang

Author: Luca Versari (veluca93)

Changes

This PR modifies the LLVM source code to compile (and run) in a WASI environment.

The question of whether having WASI support in LLVM is one that doesn't have a clear answer for me (although of course I can see some use cases), but since the patch ended up being small enough I figured I'd create a PR and see what the LLVM community would make of it :-)

The code compiles & runs successfully when compiled with an unmodified wasi-libc (I only tested running clang++ and wasm-ld).

Caveats:

  • a bunch of things are not supported. Most importantly, executing binaries is not supported, but also memory mapping, signals, dynamic linking, set/longjump, and JIT.
  • There are no tests. I also would not know how to go about adding tests.
  • Some projects are disabled on WASI.

Patch is 35.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/91051.diff

19 Files Affected:

  • (modified) clang/CMakeLists.txt (+1-1)
  • (modified) clang/lib/Driver/Driver.cpp (+1-1)
  • (modified) libcxxabi/src/CMakeLists.txt (+2-2)
  • (modified) llvm/cmake/modules/HandleLLVMOptions.cmake (+4)
  • (modified) llvm/include/llvm/ADT/bit.h (+2-2)
  • (modified) llvm/include/llvm/Support/Memory.h (+2)
  • (modified) llvm/lib/CMakeLists.txt (+2)
  • (modified) llvm/lib/Support/CrashRecoveryContext.cpp (+30-18)
  • (modified) llvm/lib/Support/LockFileManager.cpp (+11-8)
  • (modified) llvm/lib/Support/Unix/Memory.inc (+20)
  • (modified) llvm/lib/Support/Unix/Path.inc (+41-3)
  • (modified) llvm/lib/Support/Unix/Process.inc (+14-1)
  • (modified) llvm/lib/Support/Unix/Program.inc (+16-1)
  • (modified) llvm/lib/Support/Unix/Signals.inc (+14)
  • (modified) llvm/lib/Support/Unix/Unix.h (+9-6)
  • (modified) llvm/lib/Support/Unix/Watchdog.inc (+2-2)
  • (modified) llvm/lib/Support/raw_socket_stream.cpp (+22-2)
  • (modified) llvm/lib/Transforms/CMakeLists.txt (+2)
  • (modified) llvm/tools/CMakeLists.txt (+4)
diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt
index 2ac0bccb42f50..e1da8297ddd5f 100644
--- a/clang/CMakeLists.txt
+++ b/clang/CMakeLists.txt
@@ -426,7 +426,7 @@ CMAKE_DEPENDENT_OPTION(CLANG_PLUGIN_SUPPORT
 # If libstdc++ is statically linked, clang-repl needs to statically link libstdc++
 # itself, which is not possible in many platforms because of current limitations in
 # JIT stack. (more platforms need to be supported by JITLink)
-if(NOT LLVM_STATIC_LINK_CXX_STDLIB)
+if(NOT LLVM_STATIC_LINK_CXX_STDLIB AND NOT WASI)
   set(HAVE_CLANG_REPL_SUPPORT ON)
 endif()
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f5ea73a04ae5c..4f117a1d3a5ca 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1577,7 +1577,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
     CrashDiagDir = "/";
   path::append(CrashDiagDir, "Library/Logs/DiagnosticReports");
   int PID =
-#if LLVM_ON_UNIX
+#if LLVM_ON_UNIX && !defined(__wasi__)
       getpid();
 #else
       0;
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index c54ced4dc3ea8..f96d23b3c0315 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -36,8 +36,8 @@ else()
   )
 endif()
 
-if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA) AND NOT (APPLE OR CYGWIN)
-    AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
+if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA OR WASI) AND NOT
+  (APPLE OR CYGWIN) AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
   list(APPEND LIBCXXABI_SOURCES
     cxa_thread_atexit.cpp
   )
diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake
index 5ca580fbb59c5..11cc1af78b9ba 100644
--- a/llvm/cmake/modules/HandleLLVMOptions.cmake
+++ b/llvm/cmake/modules/HandleLLVMOptions.cmake
@@ -217,6 +217,10 @@ elseif(FUCHSIA OR UNIX)
   else()
     set(LLVM_HAVE_LINK_VERSION_SCRIPT 1)
   endif()
+elseif(WASI)
+  set(LLVM_ON_WIN32 0)
+  set(LLVM_ON_UNIX 1)
+  set(LLVM_HAVE_LINK_VERSION_SCRIPT 0)
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic")
   set(LLVM_ON_WIN32 0)
   set(LLVM_ON_UNIX 0)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index c42b5e686bdc9..8dcf6a8bfbb0a 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -24,12 +24,12 @@
 #endif
 
 #if defined(_MSC_VER) && !defined(_DEBUG)
-#include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+#include <cstdlib> // for _byteswap_{ushort,ulong,uint64}
 #endif
 
 #if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) ||            \
     defined(__Fuchsia__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__) ||  \
-    defined(__OpenBSD__) || defined(__DragonFly__)
+    defined(__OpenBSD__) || defined(__DragonFly__) || defined(__wasi__)
 #include <endian.h>
 #elif defined(_AIX)
 #include <sys/machine.h>
diff --git a/llvm/include/llvm/Support/Memory.h b/llvm/include/llvm/Support/Memory.h
index d7d60371d315f..2d61f6da225b6 100644
--- a/llvm/include/llvm/Support/Memory.h
+++ b/llvm/include/llvm/Support/Memory.h
@@ -41,7 +41,9 @@ namespace sys {
   private:
     void *Address;    ///< Address of first byte of memory area
     size_t AllocatedSize; ///< Size, in bytes of the memory area
+#ifndef __wasi__
     unsigned Flags = 0;
+#endif
     friend class Memory;
   };
 
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 74e2d03c07953..b32411ae0b860 100644
--- a/llvm/lib/CMakeLists.txt
+++ b/llvm/lib/CMakeLists.txt
@@ -31,7 +31,9 @@ add_subdirectory(Remarks)
 add_subdirectory(Debuginfod)
 add_subdirectory(DebugInfo)
 add_subdirectory(DWP)
+if (NOT WASI)
 add_subdirectory(ExecutionEngine)
+endif ()
 add_subdirectory(Target)
 add_subdirectory(AsmParser)
 add_subdirectory(LineEditor)
diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp
index f53aea177d612..858ec64b7d53c 100644
--- a/llvm/lib/Support/CrashRecoveryContext.cpp
+++ b/llvm/lib/Support/CrashRecoveryContext.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/CrashRecoveryContext.h"
+
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ExitCodes.h"
@@ -14,7 +15,9 @@
 #include "llvm/Support/thread.h"
 #include <cassert>
 #include <mutex>
+#ifndef __wasi__
 #include <setjmp.h>
+#endif
 
 using namespace llvm;
 
@@ -31,7 +34,9 @@ struct CrashRecoveryContextImpl {
   const CrashRecoveryContextImpl *Next;
 
   CrashRecoveryContext *CRC;
+#ifndef __wasi__
   ::jmp_buf JumpBuffer;
+#endif
   volatile unsigned Failed : 1;
   unsigned SwitchedThread : 1;
   unsigned ValidJumpBuffer : 1;
@@ -72,9 +77,11 @@ struct CrashRecoveryContextImpl {
 
     CRC->RetCode = RetCode;
 
+#ifndef __wasi__
     // Jump back to the RunSafely we were called under.
     if (ValidJumpBuffer)
       longjmp(JumpBuffer, 1);
+#endif
 
     // Otherwise let the caller decide of the outcome of the crash. Currently
     // this occurs when using SEH on Windows with MSVC or clang-cl.
@@ -118,7 +125,7 @@ CrashRecoveryContext::~CrashRecoveryContext() {
   }
   IsRecoveringFromCrash = PC;
 
-  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
+  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl;
   delete CRCI;
 }
 
@@ -154,8 +161,8 @@ void CrashRecoveryContext::Disable() {
   uninstallExceptionOrSignalHandlers();
 }
 
-void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
-{
+void CrashRecoveryContext::registerCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (head)
@@ -164,16 +171,15 @@ void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
   head = cleanup;
 }
 
-void
-CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
+void CrashRecoveryContext::unregisterCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (cleanup == head) {
     head = cleanup->next;
     if (head)
       head->prev = nullptr;
-  }
-  else {
+  } else {
     cleanup->prev->next = cleanup->next;
     if (cleanup->next)
       cleanup->next->prev = cleanup->prev;
@@ -263,16 +269,14 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
 
 #include "llvm/Support/Windows/WindowsSupport.h"
 
-static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
-{
+static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
   // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
   // compilers and platforms, so we define it manually.
   constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
-  switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
-  {
+  switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
   case DBG_PRINTEXCEPTION_C:
   case DbgPrintExceptionWideC:
-  case 0x406D1388:  // set debugger thread name
+  case 0x406D1388: // set debugger thread name
     return EXCEPTION_CONTINUE_EXECUTION;
   }
 
@@ -307,7 +311,7 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
 // CrashRecoveryContext at all.  So we make use of a thread-local
 // exception table.  The handles contained in here will either be
 // non-NULL, valid VEH handles, or NULL.
-static LLVM_THREAD_LOCAL const void* sCurrentExceptionHandle;
+static LLVM_THREAD_LOCAL const void *sCurrentExceptionHandle;
 
 static void installExceptionOrSignalHandlers() {
   // We can set up vectored exception handling now.  We will install our
@@ -342,10 +346,11 @@ static void uninstallExceptionOrSignalHandlers() {
 // reliable fashion -- if we get a signal outside of a crash recovery context we
 // simply disable crash recovery and raise the signal again.
 
+#ifndef __wasi__
 #include <signal.h>
 
-static const int Signals[] =
-    { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
+static const int Signals[] = {SIGABRT, SIGBUS,  SIGFPE,
+                              SIGILL,  SIGSEGV, SIGTRAP};
 static const unsigned NumSignals = std::size(Signals);
 static struct sigaction PrevActions[NumSignals];
 
@@ -389,8 +394,10 @@ static void CrashRecoverySignalHandler(int Signal) {
   if (CRCI)
     const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
 }
+#endif
 
 static void installExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Setup the signal handler.
   struct sigaction Handler;
   Handler.sa_handler = CrashRecoverySignalHandler;
@@ -400,12 +407,15 @@ static void installExceptionOrSignalHandlers() {
   for (unsigned i = 0; i != NumSignals; ++i) {
     sigaction(Signals[i], &Handler, &PrevActions[i]);
   }
+#endif
 }
 
 static void uninstallExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Restore the previous signal handlers.
   for (unsigned i = 0; i != NumSignals; ++i)
     sigaction(Signals[i], &PrevActions[i], nullptr);
+#endif
 }
 
 #endif // !_WIN32
@@ -418,9 +428,11 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
     Impl = CRCI;
 
     CRCI->ValidJumpBuffer = true;
+#ifndef __wasi__
     if (setjmp(CRCI->JumpBuffer) != 0) {
       return false;
     }
+#endif
   }
 
   Fn();
@@ -469,7 +481,7 @@ bool CrashRecoveryContext::throwIfCrash(int RetCode) {
     return false;
 #if defined(_WIN32)
   ::RaiseException(RetCode, 0, 0, NULL);
-#else
+#elif !defined(__wasi__)
   llvm::sys::unregisterHandlers();
   raise(RetCode - 128);
 #endif
@@ -502,7 +514,7 @@ struct RunSafelyOnThreadInfo {
 
 static void RunSafelyOnThread_Dispatch(void *UserData) {
   RunSafelyOnThreadInfo *Info =
-    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
+      reinterpret_cast<RunSafelyOnThreadInfo *>(UserData);
 
   if (Info->UseBackgroundPriority)
     setThreadBackgroundPriority();
@@ -512,7 +524,7 @@ static void RunSafelyOnThread_Dispatch(void *UserData) {
 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
                                              unsigned RequestedStackSize) {
   bool UseBackgroundPriority = hasThreadBackgroundPriority();
-  RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
+  RunSafelyOnThreadInfo Info = {Fn, this, UseBackgroundPriority, false};
   llvm::thread Thread(RequestedStackSize == 0
                           ? std::nullopt
                           : std::optional<unsigned>(RequestedStackSize),
diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp
index 3169aa25ec0d9..06b778a1d9dc7 100644
--- a/llvm/lib/Support/LockFileManager.cpp
+++ b/llvm/lib/Support/LockFileManager.cpp
@@ -34,7 +34,9 @@
 #include <unistd.h>
 #endif
 
-#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
+#if defined(__APPLE__) &&                                                      \
+    defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&                  \
+    (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
 #define USE_OSX_GETHOSTUUID 1
 #else
 #define USE_OSX_GETHOSTUUID 0
@@ -94,7 +96,7 @@ static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
   StringRef UUIDRef(UUIDStr);
   HostID.append(UUIDRef.begin(), UUIDRef.end());
 
-#elif LLVM_ON_UNIX
+#elif LLVM_ON_UNIX && !defined(__wasi__)
   char HostName[256];
   HostName[255] = 0;
   HostName[0] = 0;
@@ -116,9 +118,11 @@ bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
   if (getHostID(StoredHostID))
     return true; // Conservatively assume it's executing on error.
 
-  // Check whether the process is dead. If so, we're done.
+    // Check whether the process is dead. If so, we're done.
+#ifndef __wasi__ // no other processes anyway
   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
     return false;
+#endif
 #endif
 
   return true;
@@ -136,9 +140,10 @@ namespace {
 class RemoveUniqueLockFileOnSignal {
   StringRef Filename;
   bool RemoveImmediately;
+
 public:
   RemoveUniqueLockFileOnSignal(StringRef Name)
-  : Filename(Name), RemoveImmediately(true) {
+      : Filename(Name), RemoveImmediately(true) {
     sys::RemoveFileOnSignal(Filename, nullptr);
   }
 
@@ -157,8 +162,7 @@ class RemoveUniqueLockFileOnSignal {
 
 } // end anonymous namespace
 
-LockFileManager::LockFileManager(StringRef FileName)
-{
+LockFileManager::LockFileManager(StringRef FileName) {
   this->FileName = FileName;
   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
     std::string S("failed to obtain absolute path for ");
@@ -217,8 +221,7 @@ LockFileManager::LockFileManager(StringRef FileName)
 
   while (true) {
     // Create a link from the lock file name. If this succeeds, we're done.
-    std::error_code EC =
-        sys::fs::create_link(UniqueLockFileName, LockFileName);
+    std::error_code EC = sys::fs::create_link(UniqueLockFileName, LockFileName);
     if (!EC) {
       RemoveUniqueFile.lockAcquired();
       return;
diff --git a/llvm/lib/Support/Unix/Memory.inc b/llvm/lib/Support/Unix/Memory.inc
index bac208a7d543c..4172886e66f82 100644
--- a/llvm/lib/Support/Unix/Memory.inc
+++ b/llvm/lib/Support/Unix/Memory.inc
@@ -36,6 +36,7 @@ extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
 extern "C" void __clear_cache(void *, void *);
 #endif
 
+#ifndef __wasi__
 static int getPosixProtectionFlags(unsigned Flags) {
   switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
   case llvm::sys::Memory::MF_READ:
@@ -66,6 +67,7 @@ static int getPosixProtectionFlags(unsigned Flags) {
   // Provide a default return value as required by some compilers.
   return PROT_NONE;
 }
+#endif
 
 namespace llvm {
 namespace sys {
@@ -77,6 +79,17 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   if (NumBytes == 0)
     return MemoryBlock();
 
+#ifdef __wasi__
+  MemoryBlock Result;
+  Result.Address = malloc(NumBytes);
+  if (!Result.Address) {
+    EC = errnoAsErrorCode();
+    Result.AllocatedSize = 0;
+  } else {
+    Result.AllocatedSize = NumBytes;
+  }
+  return Result;
+#else
   // On platforms that have it, we can use MAP_ANON to get a memory-mapped
   // page without file backing, but we need a fallback of opening /dev/zero
   // for strictly POSIX platforms instead.
@@ -146,14 +159,19 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   }
 
   return Result;
+#endif
 }
 
 std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
+#ifdef __wasi__
+  free(M.Address);
+#else
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
 
   if (0 != ::munmap(M.Address, M.AllocatedSize))
     return errnoAsErrorCode();
+#endif
 
   M.Address = nullptr;
   M.AllocatedSize = 0;
@@ -163,6 +181,7 @@ std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
 
 std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
                                             unsigned Flags) {
+#ifndef __wasi__
   static const Align PageSize = Align(Process::getPageSizeEstimate());
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
@@ -200,6 +219,7 @@ std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
 
   if (InvalidateCache)
     Memory::InvalidateInstructionCache(M.Address, M.AllocatedSize);
+#endif
 
   return std::error_code();
 }
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 6e679f74869f0..32d4b9e66682f 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -32,7 +32,9 @@
 #endif
 
 #include <dirent.h>
+#ifndef __wasi__
 #include <pwd.h>
+#endif
 #include <sys/file.h>
 
 #ifdef __APPLE__
@@ -191,7 +193,9 @@ static char *getprogpath(char ret[PATH_MAX], const char *bin) {
 /// GetMainExecutable - Return the path to the main executable, given the
 /// value of argv[0] from program startup.
 std::string getMainExecutable(const char *argv0, void *MainAddr) {
-#if defined(__APPLE__)
+#if defined(__wasi__)
+  return argv0;
+#elif defined(__APPLE__)
   // On OS X the executable path is saved to the stack by dyld. Reading it
   // from there is much faster than calling dladdr, especially for large
   // binaries with symbols.
@@ -505,7 +509,7 @@ static bool is_local_impl(struct STATVFS &Vfs) {
 #elif defined(__Fuchsia__)
   // Fuchsia doesn't yet support remote filesystem mounts.
   return true;
-#elif defined(__EMSCRIPTEN__)
+#elif defined(__EMSCRIPTEN__) || defined(__wasi__)
   // Emscripten doesn't currently support remote filesystem mounts.
   return true;
 #elif defined(__HAIKU__)
@@ -651,6 +655,7 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
 }
 
 static void expandTildeExpr(SmallVectorImpl<char> &Path) {
+#ifndef __wasi__
   StringRef PathStr(Path.begin(), Path.size());
   if (PathStr.empty() || !PathStr.starts_with("~"))
     return;
@@ -694,6 +699,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
   Path.clear();
   Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
   llvm::sys::path::append(Path, Storage);
+#endif
 }
 
 void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
@@ -770,11 +776,15 @@ std::error_code status(int FD, file_status &Result) {
 }
 
 unsigned getUmask() {
+#ifdef __wasi__
+  return 0644;
+#else
   // Chose arbitary new mask and reset the umask to the old mask.
   // umask(2) never fails so ignore the return of the second call.
   unsigned Mask = ::umask(0);
   (void)::umask(Mask);
   return Mask;
+#endif
 }
 
 std::error_code setPermissions(const Twine &Path, perms Permissions) {
@@ -829,6 +839,9 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime,
 
 std::error_code mapped_file_region::init(int FD, uint64_t Offset,
                                          mapmode Mode) {
+#ifdef __wasi__
+  return std::error_code(ENOTSUP, std::generic_category());
+#else
   assert(Size != 0);
 
   int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
@@ -860,6 +873,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
   if (Mapping == MAP_FAILED)
     return errnoAsErrorCode();
   return std::error_code();
+#endif
 }
 
 mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
@@ -872,11 +886,14 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
 }
 
 void mapped_file_region::unmapImpl() {
+#ifndef __wasi__
   if (Mapping)
     ::munmap(Mapping, Size);
+#endif
 }
 
 void mapped_file_region::dontNeedImpl() {
+#ifndef __wasi__
   assert(Mode == mapped_file_region::readonly);
   if (!Mapping)
     return;
@@ -887,6 +904,7 @@ void mapped_file_region::dontNeedImpl() {
 #else
   ::madvise(Mapping, Size, MADV_DONTNEED);
 #endif
+#endif
 }
 
 int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); }
@@ -920,7 +938,7 @@ static file_type direntType(dirent *Entry) {
   // Note that while glibc provides a macro to see if this is supported,
   // _DIRENT_HAVE_D_TYPE, it's not defined on BSD/Mac, so we test for the
   // d_type-to-mode_t conversion macro instead.
-#if defined(DTTOIF)
+#if defined(DTTOIF) && !defined(__wasi__)
   return typeForMode(DTTOIF(Entry->d_type));
 #else
   // Other platforms such as Solaris require a stat() to get the type.
@@ -1224,6 +1242,9 @@ Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
 }
 
 std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   auto Start = std::chrono::steady_clock::now();
   auto End = Start + Timeout;
   do {
@@ -1241,9 +1262,13 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
     usleep(1000);
   } while (std::chrono::steady_clock::now() < End);
   return make_error_code(errc::no_lock_available);
+#endif
 }
 
 std::error_code lockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   memset(&Lock, 0, sizeof(Lock));
   Lock.l_type = F_WRLCK;
@@ -1253,9 +1278,13 @@ std::error_code lockFile(int FD) {
   if (::fcntl(FD, F_SETLKW, &Lock) != -1)
     return std::error_code();
   return errnoAsErrorCode();
+#endif
 }
 
 std::error_code unlockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   Lock.l_type = F_UNLCK;
   Lock.l_whence = SEEK_SET;
@@ -1264,6 +1293,7 @@ std::error_code unlockFile(int FD) {
   if (::fcntl(FD, F_SETLK, ...
[truncated]

@llvmbot
Copy link
Collaborator

llvmbot commented Jun 8, 2024

@llvm/pr-subscribers-clang-driver

Author: Luca Versari (veluca93)

Changes

This PR modifies the LLVM source code to compile (and run) in a WASI environment.

The question of whether having WASI support in LLVM is one that doesn't have a clear answer for me (although of course I can see some use cases), but since the patch ended up being small enough I figured I'd create a PR and see what the LLVM community would make of it :-)

The code compiles & runs successfully when compiled with an unmodified wasi-libc (I only tested running clang++ and wasm-ld).

Caveats:

  • a bunch of things are not supported. Most importantly, executing binaries is not supported, but also memory mapping, signals, dynamic linking, set/longjump, and JIT.
  • There are no tests. I also would not know how to go about adding tests.
  • Some projects are disabled on WASI.

Patch is 35.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/91051.diff

19 Files Affected:

  • (modified) clang/CMakeLists.txt (+1-1)
  • (modified) clang/lib/Driver/Driver.cpp (+1-1)
  • (modified) libcxxabi/src/CMakeLists.txt (+2-2)
  • (modified) llvm/cmake/modules/HandleLLVMOptions.cmake (+4)
  • (modified) llvm/include/llvm/ADT/bit.h (+2-2)
  • (modified) llvm/include/llvm/Support/Memory.h (+2)
  • (modified) llvm/lib/CMakeLists.txt (+2)
  • (modified) llvm/lib/Support/CrashRecoveryContext.cpp (+30-18)
  • (modified) llvm/lib/Support/LockFileManager.cpp (+11-8)
  • (modified) llvm/lib/Support/Unix/Memory.inc (+20)
  • (modified) llvm/lib/Support/Unix/Path.inc (+41-3)
  • (modified) llvm/lib/Support/Unix/Process.inc (+14-1)
  • (modified) llvm/lib/Support/Unix/Program.inc (+16-1)
  • (modified) llvm/lib/Support/Unix/Signals.inc (+14)
  • (modified) llvm/lib/Support/Unix/Unix.h (+9-6)
  • (modified) llvm/lib/Support/Unix/Watchdog.inc (+2-2)
  • (modified) llvm/lib/Support/raw_socket_stream.cpp (+22-2)
  • (modified) llvm/lib/Transforms/CMakeLists.txt (+2)
  • (modified) llvm/tools/CMakeLists.txt (+4)
diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt
index 2ac0bccb42f50..e1da8297ddd5f 100644
--- a/clang/CMakeLists.txt
+++ b/clang/CMakeLists.txt
@@ -426,7 +426,7 @@ CMAKE_DEPENDENT_OPTION(CLANG_PLUGIN_SUPPORT
 # If libstdc++ is statically linked, clang-repl needs to statically link libstdc++
 # itself, which is not possible in many platforms because of current limitations in
 # JIT stack. (more platforms need to be supported by JITLink)
-if(NOT LLVM_STATIC_LINK_CXX_STDLIB)
+if(NOT LLVM_STATIC_LINK_CXX_STDLIB AND NOT WASI)
   set(HAVE_CLANG_REPL_SUPPORT ON)
 endif()
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f5ea73a04ae5c..4f117a1d3a5ca 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1577,7 +1577,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
     CrashDiagDir = "/";
   path::append(CrashDiagDir, "Library/Logs/DiagnosticReports");
   int PID =
-#if LLVM_ON_UNIX
+#if LLVM_ON_UNIX && !defined(__wasi__)
       getpid();
 #else
       0;
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index c54ced4dc3ea8..f96d23b3c0315 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -36,8 +36,8 @@ else()
   )
 endif()
 
-if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA) AND NOT (APPLE OR CYGWIN)
-    AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
+if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA OR WASI) AND NOT
+  (APPLE OR CYGWIN) AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
   list(APPEND LIBCXXABI_SOURCES
     cxa_thread_atexit.cpp
   )
diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake
index 5ca580fbb59c5..11cc1af78b9ba 100644
--- a/llvm/cmake/modules/HandleLLVMOptions.cmake
+++ b/llvm/cmake/modules/HandleLLVMOptions.cmake
@@ -217,6 +217,10 @@ elseif(FUCHSIA OR UNIX)
   else()
     set(LLVM_HAVE_LINK_VERSION_SCRIPT 1)
   endif()
+elseif(WASI)
+  set(LLVM_ON_WIN32 0)
+  set(LLVM_ON_UNIX 1)
+  set(LLVM_HAVE_LINK_VERSION_SCRIPT 0)
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic")
   set(LLVM_ON_WIN32 0)
   set(LLVM_ON_UNIX 0)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index c42b5e686bdc9..8dcf6a8bfbb0a 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -24,12 +24,12 @@
 #endif
 
 #if defined(_MSC_VER) && !defined(_DEBUG)
-#include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+#include <cstdlib> // for _byteswap_{ushort,ulong,uint64}
 #endif
 
 #if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) ||            \
     defined(__Fuchsia__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__) ||  \
-    defined(__OpenBSD__) || defined(__DragonFly__)
+    defined(__OpenBSD__) || defined(__DragonFly__) || defined(__wasi__)
 #include <endian.h>
 #elif defined(_AIX)
 #include <sys/machine.h>
diff --git a/llvm/include/llvm/Support/Memory.h b/llvm/include/llvm/Support/Memory.h
index d7d60371d315f..2d61f6da225b6 100644
--- a/llvm/include/llvm/Support/Memory.h
+++ b/llvm/include/llvm/Support/Memory.h
@@ -41,7 +41,9 @@ namespace sys {
   private:
     void *Address;    ///< Address of first byte of memory area
     size_t AllocatedSize; ///< Size, in bytes of the memory area
+#ifndef __wasi__
     unsigned Flags = 0;
+#endif
     friend class Memory;
   };
 
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 74e2d03c07953..b32411ae0b860 100644
--- a/llvm/lib/CMakeLists.txt
+++ b/llvm/lib/CMakeLists.txt
@@ -31,7 +31,9 @@ add_subdirectory(Remarks)
 add_subdirectory(Debuginfod)
 add_subdirectory(DebugInfo)
 add_subdirectory(DWP)
+if (NOT WASI)
 add_subdirectory(ExecutionEngine)
+endif ()
 add_subdirectory(Target)
 add_subdirectory(AsmParser)
 add_subdirectory(LineEditor)
diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp
index f53aea177d612..858ec64b7d53c 100644
--- a/llvm/lib/Support/CrashRecoveryContext.cpp
+++ b/llvm/lib/Support/CrashRecoveryContext.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/CrashRecoveryContext.h"
+
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ExitCodes.h"
@@ -14,7 +15,9 @@
 #include "llvm/Support/thread.h"
 #include <cassert>
 #include <mutex>
+#ifndef __wasi__
 #include <setjmp.h>
+#endif
 
 using namespace llvm;
 
@@ -31,7 +34,9 @@ struct CrashRecoveryContextImpl {
   const CrashRecoveryContextImpl *Next;
 
   CrashRecoveryContext *CRC;
+#ifndef __wasi__
   ::jmp_buf JumpBuffer;
+#endif
   volatile unsigned Failed : 1;
   unsigned SwitchedThread : 1;
   unsigned ValidJumpBuffer : 1;
@@ -72,9 +77,11 @@ struct CrashRecoveryContextImpl {
 
     CRC->RetCode = RetCode;
 
+#ifndef __wasi__
     // Jump back to the RunSafely we were called under.
     if (ValidJumpBuffer)
       longjmp(JumpBuffer, 1);
+#endif
 
     // Otherwise let the caller decide of the outcome of the crash. Currently
     // this occurs when using SEH on Windows with MSVC or clang-cl.
@@ -118,7 +125,7 @@ CrashRecoveryContext::~CrashRecoveryContext() {
   }
   IsRecoveringFromCrash = PC;
 
-  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
+  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl;
   delete CRCI;
 }
 
@@ -154,8 +161,8 @@ void CrashRecoveryContext::Disable() {
   uninstallExceptionOrSignalHandlers();
 }
 
-void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
-{
+void CrashRecoveryContext::registerCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (head)
@@ -164,16 +171,15 @@ void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
   head = cleanup;
 }
 
-void
-CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
+void CrashRecoveryContext::unregisterCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (cleanup == head) {
     head = cleanup->next;
     if (head)
       head->prev = nullptr;
-  }
-  else {
+  } else {
     cleanup->prev->next = cleanup->next;
     if (cleanup->next)
       cleanup->next->prev = cleanup->prev;
@@ -263,16 +269,14 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
 
 #include "llvm/Support/Windows/WindowsSupport.h"
 
-static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
-{
+static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
   // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
   // compilers and platforms, so we define it manually.
   constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
-  switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
-  {
+  switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
   case DBG_PRINTEXCEPTION_C:
   case DbgPrintExceptionWideC:
-  case 0x406D1388:  // set debugger thread name
+  case 0x406D1388: // set debugger thread name
     return EXCEPTION_CONTINUE_EXECUTION;
   }
 
@@ -307,7 +311,7 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
 // CrashRecoveryContext at all.  So we make use of a thread-local
 // exception table.  The handles contained in here will either be
 // non-NULL, valid VEH handles, or NULL.
-static LLVM_THREAD_LOCAL const void* sCurrentExceptionHandle;
+static LLVM_THREAD_LOCAL const void *sCurrentExceptionHandle;
 
 static void installExceptionOrSignalHandlers() {
   // We can set up vectored exception handling now.  We will install our
@@ -342,10 +346,11 @@ static void uninstallExceptionOrSignalHandlers() {
 // reliable fashion -- if we get a signal outside of a crash recovery context we
 // simply disable crash recovery and raise the signal again.
 
+#ifndef __wasi__
 #include <signal.h>
 
-static const int Signals[] =
-    { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
+static const int Signals[] = {SIGABRT, SIGBUS,  SIGFPE,
+                              SIGILL,  SIGSEGV, SIGTRAP};
 static const unsigned NumSignals = std::size(Signals);
 static struct sigaction PrevActions[NumSignals];
 
@@ -389,8 +394,10 @@ static void CrashRecoverySignalHandler(int Signal) {
   if (CRCI)
     const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
 }
+#endif
 
 static void installExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Setup the signal handler.
   struct sigaction Handler;
   Handler.sa_handler = CrashRecoverySignalHandler;
@@ -400,12 +407,15 @@ static void installExceptionOrSignalHandlers() {
   for (unsigned i = 0; i != NumSignals; ++i) {
     sigaction(Signals[i], &Handler, &PrevActions[i]);
   }
+#endif
 }
 
 static void uninstallExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Restore the previous signal handlers.
   for (unsigned i = 0; i != NumSignals; ++i)
     sigaction(Signals[i], &PrevActions[i], nullptr);
+#endif
 }
 
 #endif // !_WIN32
@@ -418,9 +428,11 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
     Impl = CRCI;
 
     CRCI->ValidJumpBuffer = true;
+#ifndef __wasi__
     if (setjmp(CRCI->JumpBuffer) != 0) {
       return false;
     }
+#endif
   }
 
   Fn();
@@ -469,7 +481,7 @@ bool CrashRecoveryContext::throwIfCrash(int RetCode) {
     return false;
 #if defined(_WIN32)
   ::RaiseException(RetCode, 0, 0, NULL);
-#else
+#elif !defined(__wasi__)
   llvm::sys::unregisterHandlers();
   raise(RetCode - 128);
 #endif
@@ -502,7 +514,7 @@ struct RunSafelyOnThreadInfo {
 
 static void RunSafelyOnThread_Dispatch(void *UserData) {
   RunSafelyOnThreadInfo *Info =
-    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
+      reinterpret_cast<RunSafelyOnThreadInfo *>(UserData);
 
   if (Info->UseBackgroundPriority)
     setThreadBackgroundPriority();
@@ -512,7 +524,7 @@ static void RunSafelyOnThread_Dispatch(void *UserData) {
 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
                                              unsigned RequestedStackSize) {
   bool UseBackgroundPriority = hasThreadBackgroundPriority();
-  RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
+  RunSafelyOnThreadInfo Info = {Fn, this, UseBackgroundPriority, false};
   llvm::thread Thread(RequestedStackSize == 0
                           ? std::nullopt
                           : std::optional<unsigned>(RequestedStackSize),
diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp
index 3169aa25ec0d9..06b778a1d9dc7 100644
--- a/llvm/lib/Support/LockFileManager.cpp
+++ b/llvm/lib/Support/LockFileManager.cpp
@@ -34,7 +34,9 @@
 #include <unistd.h>
 #endif
 
-#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
+#if defined(__APPLE__) &&                                                      \
+    defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&                  \
+    (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
 #define USE_OSX_GETHOSTUUID 1
 #else
 #define USE_OSX_GETHOSTUUID 0
@@ -94,7 +96,7 @@ static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
   StringRef UUIDRef(UUIDStr);
   HostID.append(UUIDRef.begin(), UUIDRef.end());
 
-#elif LLVM_ON_UNIX
+#elif LLVM_ON_UNIX && !defined(__wasi__)
   char HostName[256];
   HostName[255] = 0;
   HostName[0] = 0;
@@ -116,9 +118,11 @@ bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
   if (getHostID(StoredHostID))
     return true; // Conservatively assume it's executing on error.
 
-  // Check whether the process is dead. If so, we're done.
+    // Check whether the process is dead. If so, we're done.
+#ifndef __wasi__ // no other processes anyway
   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
     return false;
+#endif
 #endif
 
   return true;
@@ -136,9 +140,10 @@ namespace {
 class RemoveUniqueLockFileOnSignal {
   StringRef Filename;
   bool RemoveImmediately;
+
 public:
   RemoveUniqueLockFileOnSignal(StringRef Name)
-  : Filename(Name), RemoveImmediately(true) {
+      : Filename(Name), RemoveImmediately(true) {
     sys::RemoveFileOnSignal(Filename, nullptr);
   }
 
@@ -157,8 +162,7 @@ class RemoveUniqueLockFileOnSignal {
 
 } // end anonymous namespace
 
-LockFileManager::LockFileManager(StringRef FileName)
-{
+LockFileManager::LockFileManager(StringRef FileName) {
   this->FileName = FileName;
   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
     std::string S("failed to obtain absolute path for ");
@@ -217,8 +221,7 @@ LockFileManager::LockFileManager(StringRef FileName)
 
   while (true) {
     // Create a link from the lock file name. If this succeeds, we're done.
-    std::error_code EC =
-        sys::fs::create_link(UniqueLockFileName, LockFileName);
+    std::error_code EC = sys::fs::create_link(UniqueLockFileName, LockFileName);
     if (!EC) {
       RemoveUniqueFile.lockAcquired();
       return;
diff --git a/llvm/lib/Support/Unix/Memory.inc b/llvm/lib/Support/Unix/Memory.inc
index bac208a7d543c..4172886e66f82 100644
--- a/llvm/lib/Support/Unix/Memory.inc
+++ b/llvm/lib/Support/Unix/Memory.inc
@@ -36,6 +36,7 @@ extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
 extern "C" void __clear_cache(void *, void *);
 #endif
 
+#ifndef __wasi__
 static int getPosixProtectionFlags(unsigned Flags) {
   switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
   case llvm::sys::Memory::MF_READ:
@@ -66,6 +67,7 @@ static int getPosixProtectionFlags(unsigned Flags) {
   // Provide a default return value as required by some compilers.
   return PROT_NONE;
 }
+#endif
 
 namespace llvm {
 namespace sys {
@@ -77,6 +79,17 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   if (NumBytes == 0)
     return MemoryBlock();
 
+#ifdef __wasi__
+  MemoryBlock Result;
+  Result.Address = malloc(NumBytes);
+  if (!Result.Address) {
+    EC = errnoAsErrorCode();
+    Result.AllocatedSize = 0;
+  } else {
+    Result.AllocatedSize = NumBytes;
+  }
+  return Result;
+#else
   // On platforms that have it, we can use MAP_ANON to get a memory-mapped
   // page without file backing, but we need a fallback of opening /dev/zero
   // for strictly POSIX platforms instead.
@@ -146,14 +159,19 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   }
 
   return Result;
+#endif
 }
 
 std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
+#ifdef __wasi__
+  free(M.Address);
+#else
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
 
   if (0 != ::munmap(M.Address, M.AllocatedSize))
     return errnoAsErrorCode();
+#endif
 
   M.Address = nullptr;
   M.AllocatedSize = 0;
@@ -163,6 +181,7 @@ std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
 
 std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
                                             unsigned Flags) {
+#ifndef __wasi__
   static const Align PageSize = Align(Process::getPageSizeEstimate());
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
@@ -200,6 +219,7 @@ std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
 
   if (InvalidateCache)
     Memory::InvalidateInstructionCache(M.Address, M.AllocatedSize);
+#endif
 
   return std::error_code();
 }
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 6e679f74869f0..32d4b9e66682f 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -32,7 +32,9 @@
 #endif
 
 #include <dirent.h>
+#ifndef __wasi__
 #include <pwd.h>
+#endif
 #include <sys/file.h>
 
 #ifdef __APPLE__
@@ -191,7 +193,9 @@ static char *getprogpath(char ret[PATH_MAX], const char *bin) {
 /// GetMainExecutable - Return the path to the main executable, given the
 /// value of argv[0] from program startup.
 std::string getMainExecutable(const char *argv0, void *MainAddr) {
-#if defined(__APPLE__)
+#if defined(__wasi__)
+  return argv0;
+#elif defined(__APPLE__)
   // On OS X the executable path is saved to the stack by dyld. Reading it
   // from there is much faster than calling dladdr, especially for large
   // binaries with symbols.
@@ -505,7 +509,7 @@ static bool is_local_impl(struct STATVFS &Vfs) {
 #elif defined(__Fuchsia__)
   // Fuchsia doesn't yet support remote filesystem mounts.
   return true;
-#elif defined(__EMSCRIPTEN__)
+#elif defined(__EMSCRIPTEN__) || defined(__wasi__)
   // Emscripten doesn't currently support remote filesystem mounts.
   return true;
 #elif defined(__HAIKU__)
@@ -651,6 +655,7 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
 }
 
 static void expandTildeExpr(SmallVectorImpl<char> &Path) {
+#ifndef __wasi__
   StringRef PathStr(Path.begin(), Path.size());
   if (PathStr.empty() || !PathStr.starts_with("~"))
     return;
@@ -694,6 +699,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
   Path.clear();
   Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
   llvm::sys::path::append(Path, Storage);
+#endif
 }
 
 void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
@@ -770,11 +776,15 @@ std::error_code status(int FD, file_status &Result) {
 }
 
 unsigned getUmask() {
+#ifdef __wasi__
+  return 0644;
+#else
   // Chose arbitary new mask and reset the umask to the old mask.
   // umask(2) never fails so ignore the return of the second call.
   unsigned Mask = ::umask(0);
   (void)::umask(Mask);
   return Mask;
+#endif
 }
 
 std::error_code setPermissions(const Twine &Path, perms Permissions) {
@@ -829,6 +839,9 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime,
 
 std::error_code mapped_file_region::init(int FD, uint64_t Offset,
                                          mapmode Mode) {
+#ifdef __wasi__
+  return std::error_code(ENOTSUP, std::generic_category());
+#else
   assert(Size != 0);
 
   int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
@@ -860,6 +873,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
   if (Mapping == MAP_FAILED)
     return errnoAsErrorCode();
   return std::error_code();
+#endif
 }
 
 mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
@@ -872,11 +886,14 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
 }
 
 void mapped_file_region::unmapImpl() {
+#ifndef __wasi__
   if (Mapping)
     ::munmap(Mapping, Size);
+#endif
 }
 
 void mapped_file_region::dontNeedImpl() {
+#ifndef __wasi__
   assert(Mode == mapped_file_region::readonly);
   if (!Mapping)
     return;
@@ -887,6 +904,7 @@ void mapped_file_region::dontNeedImpl() {
 #else
   ::madvise(Mapping, Size, MADV_DONTNEED);
 #endif
+#endif
 }
 
 int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); }
@@ -920,7 +938,7 @@ static file_type direntType(dirent *Entry) {
   // Note that while glibc provides a macro to see if this is supported,
   // _DIRENT_HAVE_D_TYPE, it's not defined on BSD/Mac, so we test for the
   // d_type-to-mode_t conversion macro instead.
-#if defined(DTTOIF)
+#if defined(DTTOIF) && !defined(__wasi__)
   return typeForMode(DTTOIF(Entry->d_type));
 #else
   // Other platforms such as Solaris require a stat() to get the type.
@@ -1224,6 +1242,9 @@ Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
 }
 
 std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   auto Start = std::chrono::steady_clock::now();
   auto End = Start + Timeout;
   do {
@@ -1241,9 +1262,13 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
     usleep(1000);
   } while (std::chrono::steady_clock::now() < End);
   return make_error_code(errc::no_lock_available);
+#endif
 }
 
 std::error_code lockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   memset(&Lock, 0, sizeof(Lock));
   Lock.l_type = F_WRLCK;
@@ -1253,9 +1278,13 @@ std::error_code lockFile(int FD) {
   if (::fcntl(FD, F_SETLKW, &Lock) != -1)
     return std::error_code();
   return errnoAsErrorCode();
+#endif
 }
 
 std::error_code unlockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   Lock.l_type = F_UNLCK;
   Lock.l_whence = SEEK_SET;
@@ -1264,6 +1293,7 @@ std::error_code unlockFile(int FD) {
   if (::fcntl(FD, F_SETLK, ...
[truncated]

@llvmbot
Copy link
Collaborator

llvmbot commented Jun 8, 2024

@llvm/pr-subscribers-llvm-adt

Author: Luca Versari (veluca93)

Changes

This PR modifies the LLVM source code to compile (and run) in a WASI environment.

The question of whether having WASI support in LLVM is one that doesn't have a clear answer for me (although of course I can see some use cases), but since the patch ended up being small enough I figured I'd create a PR and see what the LLVM community would make of it :-)

The code compiles & runs successfully when compiled with an unmodified wasi-libc (I only tested running clang++ and wasm-ld).

Caveats:

  • a bunch of things are not supported. Most importantly, executing binaries is not supported, but also memory mapping, signals, dynamic linking, set/longjump, and JIT.
  • There are no tests. I also would not know how to go about adding tests.
  • Some projects are disabled on WASI.

Patch is 35.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/91051.diff

19 Files Affected:

  • (modified) clang/CMakeLists.txt (+1-1)
  • (modified) clang/lib/Driver/Driver.cpp (+1-1)
  • (modified) libcxxabi/src/CMakeLists.txt (+2-2)
  • (modified) llvm/cmake/modules/HandleLLVMOptions.cmake (+4)
  • (modified) llvm/include/llvm/ADT/bit.h (+2-2)
  • (modified) llvm/include/llvm/Support/Memory.h (+2)
  • (modified) llvm/lib/CMakeLists.txt (+2)
  • (modified) llvm/lib/Support/CrashRecoveryContext.cpp (+30-18)
  • (modified) llvm/lib/Support/LockFileManager.cpp (+11-8)
  • (modified) llvm/lib/Support/Unix/Memory.inc (+20)
  • (modified) llvm/lib/Support/Unix/Path.inc (+41-3)
  • (modified) llvm/lib/Support/Unix/Process.inc (+14-1)
  • (modified) llvm/lib/Support/Unix/Program.inc (+16-1)
  • (modified) llvm/lib/Support/Unix/Signals.inc (+14)
  • (modified) llvm/lib/Support/Unix/Unix.h (+9-6)
  • (modified) llvm/lib/Support/Unix/Watchdog.inc (+2-2)
  • (modified) llvm/lib/Support/raw_socket_stream.cpp (+22-2)
  • (modified) llvm/lib/Transforms/CMakeLists.txt (+2)
  • (modified) llvm/tools/CMakeLists.txt (+4)
diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt
index 2ac0bccb42f50..e1da8297ddd5f 100644
--- a/clang/CMakeLists.txt
+++ b/clang/CMakeLists.txt
@@ -426,7 +426,7 @@ CMAKE_DEPENDENT_OPTION(CLANG_PLUGIN_SUPPORT
 # If libstdc++ is statically linked, clang-repl needs to statically link libstdc++
 # itself, which is not possible in many platforms because of current limitations in
 # JIT stack. (more platforms need to be supported by JITLink)
-if(NOT LLVM_STATIC_LINK_CXX_STDLIB)
+if(NOT LLVM_STATIC_LINK_CXX_STDLIB AND NOT WASI)
   set(HAVE_CLANG_REPL_SUPPORT ON)
 endif()
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index f5ea73a04ae5c..4f117a1d3a5ca 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1577,7 +1577,7 @@ bool Driver::getCrashDiagnosticFile(StringRef ReproCrashFilename,
     CrashDiagDir = "/";
   path::append(CrashDiagDir, "Library/Logs/DiagnosticReports");
   int PID =
-#if LLVM_ON_UNIX
+#if LLVM_ON_UNIX && !defined(__wasi__)
       getpid();
 #else
       0;
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index c54ced4dc3ea8..f96d23b3c0315 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -36,8 +36,8 @@ else()
   )
 endif()
 
-if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA) AND NOT (APPLE OR CYGWIN)
-    AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
+if (LIBCXXABI_ENABLE_THREADS AND (UNIX OR FUCHSIA OR WASI) AND NOT
+  (APPLE OR CYGWIN) AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
   list(APPEND LIBCXXABI_SOURCES
     cxa_thread_atexit.cpp
   )
diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake
index 5ca580fbb59c5..11cc1af78b9ba 100644
--- a/llvm/cmake/modules/HandleLLVMOptions.cmake
+++ b/llvm/cmake/modules/HandleLLVMOptions.cmake
@@ -217,6 +217,10 @@ elseif(FUCHSIA OR UNIX)
   else()
     set(LLVM_HAVE_LINK_VERSION_SCRIPT 1)
   endif()
+elseif(WASI)
+  set(LLVM_ON_WIN32 0)
+  set(LLVM_ON_UNIX 1)
+  set(LLVM_HAVE_LINK_VERSION_SCRIPT 0)
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Generic")
   set(LLVM_ON_WIN32 0)
   set(LLVM_ON_UNIX 0)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index c42b5e686bdc9..8dcf6a8bfbb0a 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -24,12 +24,12 @@
 #endif
 
 #if defined(_MSC_VER) && !defined(_DEBUG)
-#include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+#include <cstdlib> // for _byteswap_{ushort,ulong,uint64}
 #endif
 
 #if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) ||            \
     defined(__Fuchsia__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__) ||  \
-    defined(__OpenBSD__) || defined(__DragonFly__)
+    defined(__OpenBSD__) || defined(__DragonFly__) || defined(__wasi__)
 #include <endian.h>
 #elif defined(_AIX)
 #include <sys/machine.h>
diff --git a/llvm/include/llvm/Support/Memory.h b/llvm/include/llvm/Support/Memory.h
index d7d60371d315f..2d61f6da225b6 100644
--- a/llvm/include/llvm/Support/Memory.h
+++ b/llvm/include/llvm/Support/Memory.h
@@ -41,7 +41,9 @@ namespace sys {
   private:
     void *Address;    ///< Address of first byte of memory area
     size_t AllocatedSize; ///< Size, in bytes of the memory area
+#ifndef __wasi__
     unsigned Flags = 0;
+#endif
     friend class Memory;
   };
 
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 74e2d03c07953..b32411ae0b860 100644
--- a/llvm/lib/CMakeLists.txt
+++ b/llvm/lib/CMakeLists.txt
@@ -31,7 +31,9 @@ add_subdirectory(Remarks)
 add_subdirectory(Debuginfod)
 add_subdirectory(DebugInfo)
 add_subdirectory(DWP)
+if (NOT WASI)
 add_subdirectory(ExecutionEngine)
+endif ()
 add_subdirectory(Target)
 add_subdirectory(AsmParser)
 add_subdirectory(LineEditor)
diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp
index f53aea177d612..858ec64b7d53c 100644
--- a/llvm/lib/Support/CrashRecoveryContext.cpp
+++ b/llvm/lib/Support/CrashRecoveryContext.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/CrashRecoveryContext.h"
+
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ExitCodes.h"
@@ -14,7 +15,9 @@
 #include "llvm/Support/thread.h"
 #include <cassert>
 #include <mutex>
+#ifndef __wasi__
 #include <setjmp.h>
+#endif
 
 using namespace llvm;
 
@@ -31,7 +34,9 @@ struct CrashRecoveryContextImpl {
   const CrashRecoveryContextImpl *Next;
 
   CrashRecoveryContext *CRC;
+#ifndef __wasi__
   ::jmp_buf JumpBuffer;
+#endif
   volatile unsigned Failed : 1;
   unsigned SwitchedThread : 1;
   unsigned ValidJumpBuffer : 1;
@@ -72,9 +77,11 @@ struct CrashRecoveryContextImpl {
 
     CRC->RetCode = RetCode;
 
+#ifndef __wasi__
     // Jump back to the RunSafely we were called under.
     if (ValidJumpBuffer)
       longjmp(JumpBuffer, 1);
+#endif
 
     // Otherwise let the caller decide of the outcome of the crash. Currently
     // this occurs when using SEH on Windows with MSVC or clang-cl.
@@ -118,7 +125,7 @@ CrashRecoveryContext::~CrashRecoveryContext() {
   }
   IsRecoveringFromCrash = PC;
 
-  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
+  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl;
   delete CRCI;
 }
 
@@ -154,8 +161,8 @@ void CrashRecoveryContext::Disable() {
   uninstallExceptionOrSignalHandlers();
 }
 
-void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
-{
+void CrashRecoveryContext::registerCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (head)
@@ -164,16 +171,15 @@ void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
   head = cleanup;
 }
 
-void
-CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
+void CrashRecoveryContext::unregisterCleanup(
+    CrashRecoveryContextCleanup *cleanup) {
   if (!cleanup)
     return;
   if (cleanup == head) {
     head = cleanup->next;
     if (head)
       head->prev = nullptr;
-  }
-  else {
+  } else {
     cleanup->prev->next = cleanup->next;
     if (cleanup->next)
       cleanup->next->prev = cleanup->prev;
@@ -263,16 +269,14 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
 
 #include "llvm/Support/Windows/WindowsSupport.h"
 
-static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
-{
+static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
   // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
   // compilers and platforms, so we define it manually.
   constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
-  switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
-  {
+  switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
   case DBG_PRINTEXCEPTION_C:
   case DbgPrintExceptionWideC:
-  case 0x406D1388:  // set debugger thread name
+  case 0x406D1388: // set debugger thread name
     return EXCEPTION_CONTINUE_EXECUTION;
   }
 
@@ -307,7 +311,7 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
 // CrashRecoveryContext at all.  So we make use of a thread-local
 // exception table.  The handles contained in here will either be
 // non-NULL, valid VEH handles, or NULL.
-static LLVM_THREAD_LOCAL const void* sCurrentExceptionHandle;
+static LLVM_THREAD_LOCAL const void *sCurrentExceptionHandle;
 
 static void installExceptionOrSignalHandlers() {
   // We can set up vectored exception handling now.  We will install our
@@ -342,10 +346,11 @@ static void uninstallExceptionOrSignalHandlers() {
 // reliable fashion -- if we get a signal outside of a crash recovery context we
 // simply disable crash recovery and raise the signal again.
 
+#ifndef __wasi__
 #include <signal.h>
 
-static const int Signals[] =
-    { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
+static const int Signals[] = {SIGABRT, SIGBUS,  SIGFPE,
+                              SIGILL,  SIGSEGV, SIGTRAP};
 static const unsigned NumSignals = std::size(Signals);
 static struct sigaction PrevActions[NumSignals];
 
@@ -389,8 +394,10 @@ static void CrashRecoverySignalHandler(int Signal) {
   if (CRCI)
     const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
 }
+#endif
 
 static void installExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Setup the signal handler.
   struct sigaction Handler;
   Handler.sa_handler = CrashRecoverySignalHandler;
@@ -400,12 +407,15 @@ static void installExceptionOrSignalHandlers() {
   for (unsigned i = 0; i != NumSignals; ++i) {
     sigaction(Signals[i], &Handler, &PrevActions[i]);
   }
+#endif
 }
 
 static void uninstallExceptionOrSignalHandlers() {
+#ifndef __wasi__
   // Restore the previous signal handlers.
   for (unsigned i = 0; i != NumSignals; ++i)
     sigaction(Signals[i], &PrevActions[i], nullptr);
+#endif
 }
 
 #endif // !_WIN32
@@ -418,9 +428,11 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
     Impl = CRCI;
 
     CRCI->ValidJumpBuffer = true;
+#ifndef __wasi__
     if (setjmp(CRCI->JumpBuffer) != 0) {
       return false;
     }
+#endif
   }
 
   Fn();
@@ -469,7 +481,7 @@ bool CrashRecoveryContext::throwIfCrash(int RetCode) {
     return false;
 #if defined(_WIN32)
   ::RaiseException(RetCode, 0, 0, NULL);
-#else
+#elif !defined(__wasi__)
   llvm::sys::unregisterHandlers();
   raise(RetCode - 128);
 #endif
@@ -502,7 +514,7 @@ struct RunSafelyOnThreadInfo {
 
 static void RunSafelyOnThread_Dispatch(void *UserData) {
   RunSafelyOnThreadInfo *Info =
-    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
+      reinterpret_cast<RunSafelyOnThreadInfo *>(UserData);
 
   if (Info->UseBackgroundPriority)
     setThreadBackgroundPriority();
@@ -512,7 +524,7 @@ static void RunSafelyOnThread_Dispatch(void *UserData) {
 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
                                              unsigned RequestedStackSize) {
   bool UseBackgroundPriority = hasThreadBackgroundPriority();
-  RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
+  RunSafelyOnThreadInfo Info = {Fn, this, UseBackgroundPriority, false};
   llvm::thread Thread(RequestedStackSize == 0
                           ? std::nullopt
                           : std::optional<unsigned>(RequestedStackSize),
diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp
index 3169aa25ec0d9..06b778a1d9dc7 100644
--- a/llvm/lib/Support/LockFileManager.cpp
+++ b/llvm/lib/Support/LockFileManager.cpp
@@ -34,7 +34,9 @@
 #include <unistd.h>
 #endif
 
-#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
+#if defined(__APPLE__) &&                                                      \
+    defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&                  \
+    (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
 #define USE_OSX_GETHOSTUUID 1
 #else
 #define USE_OSX_GETHOSTUUID 0
@@ -94,7 +96,7 @@ static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
   StringRef UUIDRef(UUIDStr);
   HostID.append(UUIDRef.begin(), UUIDRef.end());
 
-#elif LLVM_ON_UNIX
+#elif LLVM_ON_UNIX && !defined(__wasi__)
   char HostName[256];
   HostName[255] = 0;
   HostName[0] = 0;
@@ -116,9 +118,11 @@ bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
   if (getHostID(StoredHostID))
     return true; // Conservatively assume it's executing on error.
 
-  // Check whether the process is dead. If so, we're done.
+    // Check whether the process is dead. If so, we're done.
+#ifndef __wasi__ // no other processes anyway
   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
     return false;
+#endif
 #endif
 
   return true;
@@ -136,9 +140,10 @@ namespace {
 class RemoveUniqueLockFileOnSignal {
   StringRef Filename;
   bool RemoveImmediately;
+
 public:
   RemoveUniqueLockFileOnSignal(StringRef Name)
-  : Filename(Name), RemoveImmediately(true) {
+      : Filename(Name), RemoveImmediately(true) {
     sys::RemoveFileOnSignal(Filename, nullptr);
   }
 
@@ -157,8 +162,7 @@ class RemoveUniqueLockFileOnSignal {
 
 } // end anonymous namespace
 
-LockFileManager::LockFileManager(StringRef FileName)
-{
+LockFileManager::LockFileManager(StringRef FileName) {
   this->FileName = FileName;
   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
     std::string S("failed to obtain absolute path for ");
@@ -217,8 +221,7 @@ LockFileManager::LockFileManager(StringRef FileName)
 
   while (true) {
     // Create a link from the lock file name. If this succeeds, we're done.
-    std::error_code EC =
-        sys::fs::create_link(UniqueLockFileName, LockFileName);
+    std::error_code EC = sys::fs::create_link(UniqueLockFileName, LockFileName);
     if (!EC) {
       RemoveUniqueFile.lockAcquired();
       return;
diff --git a/llvm/lib/Support/Unix/Memory.inc b/llvm/lib/Support/Unix/Memory.inc
index bac208a7d543c..4172886e66f82 100644
--- a/llvm/lib/Support/Unix/Memory.inc
+++ b/llvm/lib/Support/Unix/Memory.inc
@@ -36,6 +36,7 @@ extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
 extern "C" void __clear_cache(void *, void *);
 #endif
 
+#ifndef __wasi__
 static int getPosixProtectionFlags(unsigned Flags) {
   switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
   case llvm::sys::Memory::MF_READ:
@@ -66,6 +67,7 @@ static int getPosixProtectionFlags(unsigned Flags) {
   // Provide a default return value as required by some compilers.
   return PROT_NONE;
 }
+#endif
 
 namespace llvm {
 namespace sys {
@@ -77,6 +79,17 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   if (NumBytes == 0)
     return MemoryBlock();
 
+#ifdef __wasi__
+  MemoryBlock Result;
+  Result.Address = malloc(NumBytes);
+  if (!Result.Address) {
+    EC = errnoAsErrorCode();
+    Result.AllocatedSize = 0;
+  } else {
+    Result.AllocatedSize = NumBytes;
+  }
+  return Result;
+#else
   // On platforms that have it, we can use MAP_ANON to get a memory-mapped
   // page without file backing, but we need a fallback of opening /dev/zero
   // for strictly POSIX platforms instead.
@@ -146,14 +159,19 @@ MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
   }
 
   return Result;
+#endif
 }
 
 std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
+#ifdef __wasi__
+  free(M.Address);
+#else
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
 
   if (0 != ::munmap(M.Address, M.AllocatedSize))
     return errnoAsErrorCode();
+#endif
 
   M.Address = nullptr;
   M.AllocatedSize = 0;
@@ -163,6 +181,7 @@ std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
 
 std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
                                             unsigned Flags) {
+#ifndef __wasi__
   static const Align PageSize = Align(Process::getPageSizeEstimate());
   if (M.Address == nullptr || M.AllocatedSize == 0)
     return std::error_code();
@@ -200,6 +219,7 @@ std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
 
   if (InvalidateCache)
     Memory::InvalidateInstructionCache(M.Address, M.AllocatedSize);
+#endif
 
   return std::error_code();
 }
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 6e679f74869f0..32d4b9e66682f 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -32,7 +32,9 @@
 #endif
 
 #include <dirent.h>
+#ifndef __wasi__
 #include <pwd.h>
+#endif
 #include <sys/file.h>
 
 #ifdef __APPLE__
@@ -191,7 +193,9 @@ static char *getprogpath(char ret[PATH_MAX], const char *bin) {
 /// GetMainExecutable - Return the path to the main executable, given the
 /// value of argv[0] from program startup.
 std::string getMainExecutable(const char *argv0, void *MainAddr) {
-#if defined(__APPLE__)
+#if defined(__wasi__)
+  return argv0;
+#elif defined(__APPLE__)
   // On OS X the executable path is saved to the stack by dyld. Reading it
   // from there is much faster than calling dladdr, especially for large
   // binaries with symbols.
@@ -505,7 +509,7 @@ static bool is_local_impl(struct STATVFS &Vfs) {
 #elif defined(__Fuchsia__)
   // Fuchsia doesn't yet support remote filesystem mounts.
   return true;
-#elif defined(__EMSCRIPTEN__)
+#elif defined(__EMSCRIPTEN__) || defined(__wasi__)
   // Emscripten doesn't currently support remote filesystem mounts.
   return true;
 #elif defined(__HAIKU__)
@@ -651,6 +655,7 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
 }
 
 static void expandTildeExpr(SmallVectorImpl<char> &Path) {
+#ifndef __wasi__
   StringRef PathStr(Path.begin(), Path.size());
   if (PathStr.empty() || !PathStr.starts_with("~"))
     return;
@@ -694,6 +699,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) {
   Path.clear();
   Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
   llvm::sys::path::append(Path, Storage);
+#endif
 }
 
 void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
@@ -770,11 +776,15 @@ std::error_code status(int FD, file_status &Result) {
 }
 
 unsigned getUmask() {
+#ifdef __wasi__
+  return 0644;
+#else
   // Chose arbitary new mask and reset the umask to the old mask.
   // umask(2) never fails so ignore the return of the second call.
   unsigned Mask = ::umask(0);
   (void)::umask(Mask);
   return Mask;
+#endif
 }
 
 std::error_code setPermissions(const Twine &Path, perms Permissions) {
@@ -829,6 +839,9 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime,
 
 std::error_code mapped_file_region::init(int FD, uint64_t Offset,
                                          mapmode Mode) {
+#ifdef __wasi__
+  return std::error_code(ENOTSUP, std::generic_category());
+#else
   assert(Size != 0);
 
   int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
@@ -860,6 +873,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
   if (Mapping == MAP_FAILED)
     return errnoAsErrorCode();
   return std::error_code();
+#endif
 }
 
 mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
@@ -872,11 +886,14 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
 }
 
 void mapped_file_region::unmapImpl() {
+#ifndef __wasi__
   if (Mapping)
     ::munmap(Mapping, Size);
+#endif
 }
 
 void mapped_file_region::dontNeedImpl() {
+#ifndef __wasi__
   assert(Mode == mapped_file_region::readonly);
   if (!Mapping)
     return;
@@ -887,6 +904,7 @@ void mapped_file_region::dontNeedImpl() {
 #else
   ::madvise(Mapping, Size, MADV_DONTNEED);
 #endif
+#endif
 }
 
 int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); }
@@ -920,7 +938,7 @@ static file_type direntType(dirent *Entry) {
   // Note that while glibc provides a macro to see if this is supported,
   // _DIRENT_HAVE_D_TYPE, it's not defined on BSD/Mac, so we test for the
   // d_type-to-mode_t conversion macro instead.
-#if defined(DTTOIF)
+#if defined(DTTOIF) && !defined(__wasi__)
   return typeForMode(DTTOIF(Entry->d_type));
 #else
   // Other platforms such as Solaris require a stat() to get the type.
@@ -1224,6 +1242,9 @@ Expected<size_t> readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf,
 }
 
 std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   auto Start = std::chrono::steady_clock::now();
   auto End = Start + Timeout;
   do {
@@ -1241,9 +1262,13 @@ std::error_code tryLockFile(int FD, std::chrono::milliseconds Timeout) {
     usleep(1000);
   } while (std::chrono::steady_clock::now() < End);
   return make_error_code(errc::no_lock_available);
+#endif
 }
 
 std::error_code lockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   memset(&Lock, 0, sizeof(Lock));
   Lock.l_type = F_WRLCK;
@@ -1253,9 +1278,13 @@ std::error_code lockFile(int FD) {
   if (::fcntl(FD, F_SETLKW, &Lock) != -1)
     return std::error_code();
   return errnoAsErrorCode();
+#endif
 }
 
 std::error_code unlockFile(int FD) {
+#if defined(__wasi__)
+  return std::error_code();
+#else
   struct flock Lock;
   Lock.l_type = F_UNLCK;
   Lock.l_whence = SEEK_SET;
@@ -1264,6 +1293,7 @@ std::error_code unlockFile(int FD) {
   if (::fcntl(FD, F_SETLK, ...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I am happy with the one line change in libcxxabi/. For libc++ to officially support this beyond just a fun proof of concept, we'd need a story for running the tests and set up a pre-commit CI for it though.

I'm approving just to get the reviewers-libcxxabi group out of your way, I didn't look at the rest of the PR. Also, if you think we should go ahead with the other PR instead and don't plan on merging this, please close this PR to help us keep our PR queue tidy. Thanks!

@veluca93
Copy link
Author

Will close this PR for now waiting for the other one to go through - if that does not happen, feel free to reopen it!

@veluca93 veluca93 closed this Jun 12, 2024
@whitequark
Copy link
Collaborator

FWIW I plan to finish the other one in a week or two; I'm busy with my job this week and on vacation the next one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category cmake Build system in general and CMake in particular libc++abi libc++abi C++ Runtime Library. Not libc++. llvm:adt llvm:support llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants