-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[openmp][wasm] Allow compiling OpenMP to WebAssembly #71297
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
Conversation
@llvm/pr-subscribers-flang-openmp Author: Andrew Brown (abrown) ChangesThis change allows building the static OpenMP runtime,
The commit messages have more details. Please understand this PR as a start, not the completed work, for WebAssembly support in OpenMP. Getting the tests running somehow would be a good next step, e.g.; but what is contained here works, at least with recent versions of wasi-sdk and engines that support wasi-threads. I suspect the same is true for Emscripten and browsers, but I have not tested that workflow. Patch is 25.24 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/71297.diff 14 Files Affected:
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 5b24e9fe2e0c5bd..d9b88828133945d 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -4746,10 +4746,13 @@ OpenMPIRBuilder::getOrCreateInternalVariable(Type *Ty, const StringRef &Name,
// variable for possibly changing that to internal or private, or maybe
// create different versions of the function for different OMP internal
// variables.
- auto *GV = new GlobalVariable(
- M, Ty, /*IsConstant=*/false, GlobalValue::CommonLinkage,
- Constant::getNullValue(Ty), Elem.first(),
- /*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal, AddressSpace);
+ auto linkage = this->M.getTargetTriple().rfind("wasm32") == 0
+ ? GlobalValue::ExternalLinkage
+ : GlobalValue::CommonLinkage;
+ auto *GV = new GlobalVariable(M, Ty, /*IsConstant=*/false, linkage,
+ Constant::getNullValue(Ty), Elem.first(),
+ /*InsertBefore=*/nullptr,
+ GlobalValue::NotThreadLocal, AddressSpace);
const DataLayout &DL = M.getDataLayout();
const llvm::Align TypeAlign = DL.getABITypeAlign(Ty);
const llvm::Align PtrAlign = DL.getPointerABIAlignment(AddressSpace);
diff --git a/openmp/CMakeLists.txt b/openmp/CMakeLists.txt
index 13e65bf70b5bb55..307c8dbbc0c3b9a 100644
--- a/openmp/CMakeLists.txt
+++ b/openmp/CMakeLists.txt
@@ -94,7 +94,7 @@ set(ENABLE_LIBOMPTARGET ON)
# Since the device plugins are only supported on Linux anyway,
# there is no point in trying to compile libomptarget on other OSes.
# 32-bit systems are not supported either.
-if (APPLE OR WIN32 OR NOT "cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
+if (APPLE OR WIN32 OR WASM OR NOT "cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ENABLE_LIBOMPTARGET OFF)
endif()
diff --git a/openmp/runtime/CMakeLists.txt b/openmp/runtime/CMakeLists.txt
index 4441c4babdc07c0..ca92e5f444d44e0 100644
--- a/openmp/runtime/CMakeLists.txt
+++ b/openmp/runtime/CMakeLists.txt
@@ -30,7 +30,7 @@ if(${OPENMP_STANDALONE_BUILD})
# If adding a new architecture, take a look at cmake/LibompGetArchitecture.cmake
libomp_get_architecture(LIBOMP_DETECTED_ARCH)
set(LIBOMP_ARCH ${LIBOMP_DETECTED_ARCH} CACHE STRING
- "The architecture to build for (x86_64/i386/arm/ppc64/ppc64le/aarch64/mic/mips/mips64/riscv64/loongarch64/ve).")
+ "The architecture to build for (x86_64/i386/arm/ppc64/ppc64le/aarch64/mic/mips/mips64/riscv64/loongarch64/ve/wasm32).")
# Should assertions be enabled? They are on by default.
set(LIBOMP_ENABLE_ASSERTIONS TRUE CACHE BOOL
"enable assertions?")
@@ -65,6 +65,8 @@ else() # Part of LLVM build
set(LIBOMP_ARCH loongarch64)
elseif(LIBOMP_NATIVE_ARCH MATCHES "ve")
set(LIBOMP_ARCH ve)
+ elseif(LIBOMP_NATIVE_ARCH MATCHES "wasm")
+ set(LIBOMP_ARCH wasm32)
else()
# last ditch effort
libomp_get_architecture(LIBOMP_ARCH)
@@ -85,7 +87,7 @@ if(LIBOMP_ARCH STREQUAL "aarch64")
endif()
endif()
-libomp_check_variable(LIBOMP_ARCH 32e x86_64 32 i386 arm ppc64 ppc64le aarch64 aarch64_a64fx mic mips mips64 riscv64 loongarch64 ve)
+libomp_check_variable(LIBOMP_ARCH 32e x86_64 32 i386 arm ppc64 ppc64le aarch64 aarch64_a64fx mic mips mips64 riscv64 loongarch64 ve wasm32)
set(LIBOMP_LIB_TYPE normal CACHE STRING
"Performance,Profiling,Stubs library (normal/profile/stubs)")
@@ -165,6 +167,7 @@ set(MIPS FALSE)
set(RISCV64 FALSE)
set(LOONGARCH64 FALSE)
set(VE FALSE)
+set(WASM FALSE)
if("${LIBOMP_ARCH}" STREQUAL "i386" OR "${LIBOMP_ARCH}" STREQUAL "32") # IA-32 architecture
set(IA32 TRUE)
elseif("${LIBOMP_ARCH}" STREQUAL "x86_64" OR "${LIBOMP_ARCH}" STREQUAL "32e") # Intel(R) 64 architecture
@@ -193,6 +196,8 @@ elseif("${LIBOMP_ARCH}" STREQUAL "loongarch64") # LoongArch64 architecture
set(LOONGARCH64 TRUE)
elseif("${LIBOMP_ARCH}" STREQUAL "ve") # VE architecture
set(VE TRUE)
+elseif("${LIBOMP_ARCH}" STREQUAL "wasm32") # WebAssembly architecture
+ set(WASM TRUE)
endif()
# Set some flags based on build_type
@@ -301,6 +306,11 @@ endif()
set(LIBOMP_ENABLE_SHARED TRUE CACHE BOOL
"Shared library instead of static library?")
+if(WASM)
+ libomp_warning_say("The WebAssembly build currently only supports static libraries; forcing LIBOMP_ENABLE_SHARED to false")
+ set(LIBOMP_ENABLE_SHARED FALSE)
+endif()
+
if(WIN32 AND NOT LIBOMP_ENABLE_SHARED)
libomp_error_say("Static libraries requested but not available on Windows")
endif()
diff --git a/openmp/runtime/cmake/LibompGetArchitecture.cmake b/openmp/runtime/cmake/LibompGetArchitecture.cmake
index 98bfce9ae990a7b..0faee643e109975 100644
--- a/openmp/runtime/cmake/LibompGetArchitecture.cmake
+++ b/openmp/runtime/cmake/LibompGetArchitecture.cmake
@@ -51,6 +51,8 @@ function(libomp_get_architecture return_arch)
#error ARCHITECTURE=loongarch64
#elif defined(__ve__)
#error ARCHITECTURE=ve
+ #elif defined(__wasm32__)
+ #error ARCHITECTURE=wasm32
#else
#error ARCHITECTURE=UnknownArchitecture
#endif
diff --git a/openmp/runtime/cmake/config-ix.cmake b/openmp/runtime/cmake/config-ix.cmake
index 9869aeab0354635..5e829fd463d8240 100644
--- a/openmp/runtime/cmake/config-ix.cmake
+++ b/openmp/runtime/cmake/config-ix.cmake
@@ -151,14 +151,17 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Intel" OR CMAKE_C_COMPILER_ID STREQUAL "IntelLL
endif()
# Checking Threading requirements
-find_package(Threads REQUIRED)
-if(WIN32)
- if(NOT CMAKE_USE_WIN32_THREADS_INIT)
- libomp_error_say("Need Win32 thread interface on Windows.")
- endif()
-else()
- if(NOT CMAKE_USE_PTHREADS_INIT)
- libomp_error_say("Need pthread interface on Unix-like systems.")
+message(STATUS "WASM=${WASM}")
+if (NOT WASM)
+ find_package(Threads REQUIRED)
+ if(WIN32)
+ if(NOT CMAKE_USE_WIN32_THREADS_INIT)
+ libomp_error_say("Need Win32 thread interface on Windows.")
+ endif()
+ else()
+ if(NOT CMAKE_USE_PTHREADS_INIT)
+ libomp_error_say("Need pthread interface on Unix-like systems.")
+ endif()
endif()
endif()
diff --git a/openmp/runtime/src/kmp.h b/openmp/runtime/src/kmp.h
index 339e4ca4be6b350..87cf41416eb8e44 100644
--- a/openmp/runtime/src/kmp.h
+++ b/openmp/runtime/src/kmp.h
@@ -60,7 +60,15 @@
#undef KMP_CANCEL_THREADS
#endif
+// Some WASI targets (e.g., wasm32-wasi-threads) do not support thread
+// cancellation.
+#if defined(KMP_OS_WASI)
+#undef KMP_CANCEL_THREADS
+#endif
+
+#if !KMP_OS_WASI
#include <signal.h>
+#endif
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
@@ -121,7 +129,7 @@ class kmp_stats_list;
#endif
#include "kmp_i18n.h"
-#define KMP_HANDLE_SIGNALS (KMP_OS_UNIX || KMP_OS_WINDOWS)
+#define KMP_HANDLE_SIGNALS ((KMP_OS_UNIX && !KMP_OS_WASI) || KMP_OS_WINDOWS)
#include "kmp_wrapper_malloc.h"
#if KMP_OS_UNIX
@@ -598,7 +606,9 @@ typedef int PACKED_REDUCTION_METHOD_T;
#endif
#if KMP_OS_UNIX
+#if !KMP_OS_WASI
#include <dlfcn.h>
+#endif
#include <pthread.h>
#endif
@@ -1335,6 +1345,10 @@ extern kmp_uint64 __kmp_now_nsec();
/* TODO: tune for KMP_OS_OPENBSD */
#define KMP_INIT_WAIT 1024U /* initial number of spin-tests */
#define KMP_NEXT_WAIT 512U /* susequent number of spin-tests */
+#elif KMP_OS_WASI
+/* TODO: tune for KMP_OS_WASI */
+#define KMP_INIT_WAIT 1024U /* initial number of spin-tests */
+#define KMP_NEXT_WAIT 512U /* susequent number of spin-tests */
#endif
#if KMP_ARCH_X86 || KMP_ARCH_X86_64
diff --git a/openmp/runtime/src/kmp_ftn_entry.h b/openmp/runtime/src/kmp_ftn_entry.h
index ffb01a31fb93edb..6132f1b04fc7777 100644
--- a/openmp/runtime/src/kmp_ftn_entry.h
+++ b/openmp/runtime/src/kmp_ftn_entry.h
@@ -593,7 +593,7 @@ int FTN_STDCALL KMP_EXPAND_NAME(FTN_GET_THREAD_NUM)(void) {
return 0;
}
--gtid; // We keep (gtid+1) in TLS
-#elif KMP_OS_LINUX
+#elif KMP_OS_LINUX || KMP_OS_WASI
#ifdef KMP_TDATA_GTID
if (__kmp_gtid_mode >= 3) {
if ((gtid = __kmp_gtid) == KMP_GTID_DNE) {
@@ -1043,7 +1043,7 @@ void FTN_STDCALL KMP_EXPAND_NAME(FTN_SET_DEFAULT_DEVICE)(int KMP_DEREF arg) {
int FTN_STDCALL KMP_EXPAND_NAME(FTN_GET_NUM_DEVICES)(void)
KMP_WEAK_ATTRIBUTE_EXTERNAL;
int FTN_STDCALL KMP_EXPAND_NAME(FTN_GET_NUM_DEVICES)(void) {
-#if KMP_MIC || KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_MIC || KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return 0;
#else
int (*fptr)();
@@ -1558,7 +1558,7 @@ typedef void *omp_interop_t;
// libomptarget, if loaded, provides this function
int FTN_STDCALL FTN_GET_NUM_INTEROP_PROPERTIES(const omp_interop_t interop) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return 0;
#else
int (*fptr)(const omp_interop_t);
@@ -1573,7 +1573,7 @@ int FTN_STDCALL FTN_GET_NUM_INTEROP_PROPERTIES(const omp_interop_t interop) {
intptr_t FTN_STDCALL FTN_GET_INTEROP_INT(const omp_interop_t interop,
omp_interop_property_t property_id,
int *err) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return 0;
#else
intptr_t (*fptr)(const omp_interop_t, omp_interop_property_t, int *);
@@ -1587,7 +1587,7 @@ intptr_t FTN_STDCALL FTN_GET_INTEROP_INT(const omp_interop_t interop,
void *FTN_STDCALL FTN_GET_INTEROP_PTR(const omp_interop_t interop,
omp_interop_property_t property_id,
int *err) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return nullptr;
#else
void *(*fptr)(const omp_interop_t, omp_interop_property_t, int *);
@@ -1601,7 +1601,7 @@ void *FTN_STDCALL FTN_GET_INTEROP_PTR(const omp_interop_t interop,
const char *FTN_STDCALL FTN_GET_INTEROP_STR(const omp_interop_t interop,
omp_interop_property_t property_id,
int *err) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return nullptr;
#else
const char *(*fptr)(const omp_interop_t, omp_interop_property_t, int *);
@@ -1614,7 +1614,7 @@ const char *FTN_STDCALL FTN_GET_INTEROP_STR(const omp_interop_t interop,
// libomptarget, if loaded, provides this function
const char *FTN_STDCALL FTN_GET_INTEROP_NAME(
const omp_interop_t interop, omp_interop_property_t property_id) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return nullptr;
#else
const char *(*fptr)(const omp_interop_t, omp_interop_property_t);
@@ -1627,7 +1627,7 @@ const char *FTN_STDCALL FTN_GET_INTEROP_NAME(
// libomptarget, if loaded, provides this function
const char *FTN_STDCALL FTN_GET_INTEROP_TYPE_DESC(
const omp_interop_t interop, omp_interop_property_t property_id) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return nullptr;
#else
const char *(*fptr)(const omp_interop_t, omp_interop_property_t);
@@ -1640,7 +1640,7 @@ const char *FTN_STDCALL FTN_GET_INTEROP_TYPE_DESC(
// libomptarget, if loaded, provides this function
const char *FTN_STDCALL FTN_GET_INTEROP_RC_DESC(
const omp_interop_t interop, omp_interop_property_t property_id) {
-#if KMP_OS_DARWIN || defined(KMP_STUB)
+#if KMP_OS_DARWIN || KMP_OS_WASI || defined(KMP_STUB)
return nullptr;
#else
const char *(*fptr)(const omp_interop_t, omp_interop_property_t);
diff --git a/openmp/runtime/src/kmp_gsupport.cpp b/openmp/runtime/src/kmp_gsupport.cpp
index d77d4809a7e95c2..dc843f78dc69759 100644
--- a/openmp/runtime/src/kmp_gsupport.cpp
+++ b/openmp/runtime/src/kmp_gsupport.cpp
@@ -356,7 +356,7 @@ void KMP_EXPAND_NAME(KMP_API_NAME_GOMP_ORDERED_END)(void) {
// They come in two flavors: 64-bit unsigned, and either 32-bit signed
// (IA-32 architecture) or 64-bit signed (Intel(R) 64).
-#if KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS
+#if KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS || KMP_ARCH_WASM
#define KMP_DISPATCH_INIT __kmp_aux_dispatch_init_4
#define KMP_DISPATCH_FINI_CHUNK __kmp_aux_dispatch_fini_chunk_4
#define KMP_DISPATCH_NEXT __kmpc_dispatch_next_4
diff --git a/openmp/runtime/src/kmp_os.h b/openmp/runtime/src/kmp_os.h
index 2c632112a8d8e35..b4bb9f1001c628a 100644
--- a/openmp/runtime/src/kmp_os.h
+++ b/openmp/runtime/src/kmp_os.h
@@ -75,7 +75,7 @@
#error Unknown compiler
#endif
-#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD)
+#if (KMP_OS_LINUX || KMP_OS_WINDOWS || KMP_OS_FREEBSD) && !KMP_OS_WASI
#define KMP_AFFINITY_SUPPORTED 1
#if KMP_OS_WINDOWS && KMP_ARCH_X86_64
#define KMP_GROUP_AFFINITY 1
@@ -175,7 +175,7 @@ typedef unsigned long long kmp_uint64;
#define KMP_UINT64_SPEC "llu"
#endif /* KMP_OS_UNIX */
-#if KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS
+#if KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS || KMP_ARCH_WASM
#define KMP_SIZE_T_SPEC KMP_UINT32_SPEC
#elif KMP_ARCH_X86_64 || KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 || \
KMP_ARCH_MIPS64 || KMP_ARCH_RISCV64 || KMP_ARCH_LOONGARCH64 || KMP_ARCH_VE
@@ -184,7 +184,7 @@ typedef unsigned long long kmp_uint64;
#error "Can't determine size_t printf format specifier."
#endif
-#if KMP_ARCH_X86 || KMP_ARCH_ARM
+#if KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_WASM
#define KMP_SIZE_T_MAX (0xFFFFFFFF)
#else
#define KMP_SIZE_T_MAX (0xFFFFFFFFFFFFFFFF)
@@ -213,8 +213,8 @@ typedef kmp_uint32 kmp_uint;
#define KMP_INT_MIN ((kmp_int32)0x80000000)
// stdarg handling
-#if (KMP_ARCH_ARM || KMP_ARCH_X86_64 || KMP_ARCH_AARCH64) && \
- (KMP_OS_FREEBSD || KMP_OS_LINUX)
+#if (KMP_ARCH_ARM || KMP_ARCH_X86_64 || KMP_ARCH_AARCH64 || KMP_ARCH_WASM) && \
+ (KMP_OS_FREEBSD || KMP_OS_LINUX || KMP_OS_WASI)
typedef va_list *kmp_va_list;
#define kmp_va_deref(ap) (*(ap))
#define kmp_va_addr_of(ap) (&(ap))
@@ -1143,7 +1143,7 @@ extern kmp_real64 __kmp_xchg_real64(volatile kmp_real64 *p, kmp_real64 v);
KMP_COMPARE_AND_STORE_REL64((volatile kmp_int64 *)(volatile void *)&(a), \
(kmp_int64)(b), (kmp_int64)(c))
-#if KMP_ARCH_X86 || KMP_ARCH_MIPS
+#if KMP_ARCH_X86 || KMP_ARCH_MIPS || KMP_ARCH_WASM
// What about ARM?
#define TCR_PTR(a) ((void *)TCR_4(a))
#define TCW_PTR(a, b) TCW_4((a), (b))
@@ -1285,6 +1285,9 @@ bool __kmp_atomic_compare_store_rel(std::atomic<T> *p, T expected, T desired) {
extern void *__kmp_lookup_symbol(const char *name, bool next = false);
#define KMP_DLSYM(name) __kmp_lookup_symbol(name)
#define KMP_DLSYM_NEXT(name) __kmp_lookup_symbol(name, true)
+#elif KMP_OS_WASI
+#define KMP_DLSYM(name) nullptr
+#define KMP_DLSYM_NEXT(name) nullptr
#else
#define KMP_DLSYM(name) dlsym(RTLD_DEFAULT, name)
#define KMP_DLSYM_NEXT(name) dlsym(RTLD_NEXT, name)
diff --git a/openmp/runtime/src/kmp_platform.h b/openmp/runtime/src/kmp_platform.h
index 1a2197d338342ac..eb432427e53cb4e 100644
--- a/openmp/runtime/src/kmp_platform.h
+++ b/openmp/runtime/src/kmp_platform.h
@@ -23,6 +23,7 @@
#define KMP_OS_DARWIN 0
#define KMP_OS_WINDOWS 0
#define KMP_OS_HURD 0
+#define KMP_OS_WASI 0
#define KMP_OS_UNIX 0 /* disjunction of KMP_OS_LINUX, KMP_OS_DARWIN etc. */
#ifdef _WIN32
@@ -70,13 +71,19 @@
#define KMP_OS_HURD 1
#endif
+#if (defined __wasi__) || (defined __EMSCRIPTEN__)
+#undef KMP_OS_WASI
+#define KMP_OS_WASI 1
+#endif
+
#if (1 != KMP_OS_LINUX + KMP_OS_DRAGONFLY + KMP_OS_FREEBSD + KMP_OS_NETBSD + \
- KMP_OS_OPENBSD + KMP_OS_DARWIN + KMP_OS_WINDOWS + KMP_OS_HURD)
+ KMP_OS_OPENBSD + KMP_OS_DARWIN + KMP_OS_WINDOWS + KMP_OS_HURD + \
+ KMP_OS_WASI)
#error Unknown OS
#endif
#if KMP_OS_LINUX || KMP_OS_DRAGONFLY || KMP_OS_FREEBSD || KMP_OS_NETBSD || \
- KMP_OS_OPENBSD || KMP_OS_DARWIN || KMP_OS_HURD
+ KMP_OS_OPENBSD || KMP_OS_DARWIN || KMP_OS_HURD || KMP_OS_WASI
#undef KMP_OS_UNIX
#define KMP_OS_UNIX 1
#endif
@@ -185,6 +192,10 @@
#define KMP_ARCH_ARM 1
#endif
+#if defined(__wasm32__)
+#define KMP_ARCH_WASM 1
+#endif
+
#if defined(__MIC__) || defined(__MIC2__)
#define KMP_MIC 1
#if __MIC2__ || __KNC__
@@ -201,7 +212,8 @@
#endif
/* Specify 32 bit architectures here */
-#define KMP_32_BIT_ARCH (KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS)
+#define KMP_32_BIT_ARCH \
+ (KMP_ARCH_X86 || KMP_ARCH_ARM || KMP_ARCH_MIPS || KMP_ARCH_WASM)
// Platforms which support Intel(R) Many Integrated Core Architecture
#define KMP_MIC_SUPPORTED \
@@ -210,7 +222,8 @@
// TODO: Fixme - This is clever, but really fugly
#if (1 != KMP_ARCH_X86 + KMP_ARCH_X86_64 + KMP_ARCH_ARM + KMP_ARCH_PPC64 + \
KMP_ARCH_AARCH64 + KMP_ARCH_MIPS + KMP_ARCH_MIPS64 + \
- KMP_ARCH_RISCV64 + KMP_ARCH_LOONGARCH64 + KMP_ARCH_VE)
+ KMP_ARCH_RISCV64 + KMP_ARCH_LOONGARCH64 + KMP_ARCH_VE + \
+ KMP_ARCH_WASM)
#error Unknown or unsupported architecture
#endif
diff --git a/openmp/runtime/src/kmp_runtime.cpp b/openmp/runtime/src/kmp_runtime.cpp
index 9bf5105d134e82e..fcffd360d3547e9 100644
--- a/openmp/runtime/src/kmp_runtime.cpp
+++ b/openmp/runtime/src/kmp_runtime.cpp
@@ -47,8 +47,9 @@ static char *ProfileTraceFile = nullptr;
#include <process.h>
#endif
-#if KMP_OS_WINDOWS
-// windows does not need include files as it doesn't use shared memory
+#if !KMP_USE_SHM
+// Windows and WASI do not need these include files as they don't use shared
+// memory.
#else
#include <sys/mman.h>
#include <sys/stat.h>
@@ -446,26 +447,26 @@ void __kmp_abort_process() {
__kmp_dump_debug_buffer();
}
- if (KMP_OS_WINDOWS) {
- // Let other threads know of abnormal termination and prevent deadlock
- // if abort happened during library initialization or shutdown
- __kmp_global.g.g_abort = SIGABRT;
-
- /* On Windows* OS by default abort() causes pop-up error box, which stalls
- nightly testing. Unfortunately, we cannot reliably suppress pop-up error
- boxes. _set_abort_behavior() works well, but this function is not
- available in VS7 (this is not problem for DLL, but it is a problem for
- static OpenMP RTL). SetErrorMode (and so, timelimit utility) does not
- help, at least in some versions of MS C RTL.
-
- It seems following sequence is the only way to simulate abort() and
- avoid pop-up error box. */
- raise(SIGABRT);
- _exit(3); // Just in case, if signal ignored, exit anyway.
- } else {
- __kmp_unregister_library();
- abort();
- }
+#if KMP_OS_WINDOWS
+ // Let other threads know of abnormal termination and prevent deadlock
+ // if abort happened during library initialization or shutdown
+ __kmp_global.g.g_abort = SIGABRT;
+
+ /* On Windows* OS by default abort() causes pop-up error box, which stalls
+ nightly testing. Unfortunately, we cannot reliably suppress pop-up error
+ boxes. _set_abort_behavior() works well, but this function is not
+ available in VS7 (this is not problem for DLL, but it is a problem for
+ static OpenMP RTL). SetErrorMode (and so, timelimit utility) does not
+ help, at least in some versions of MS C RTL.
+
+ It seems following sequence is the only way to simulate abort() and
+ avoid pop-up error box. */
+ raise(SIGABRT);
+ _exit(3); // Just in case, if signal ignored, exit anyway.
+#else
+ __kmp_unregister_library();
+ abort();
+#endif
__kmp_infinite_loop();
__kmp_release_bootstrap_lock(&__kmp_exit_lock);
@@ -8894,10 +8895,12 @@ __kmp_determine_reduction_method(
int atomic_available = FAST_REDUCTION_ATOMIC_METHOD_GENERATED;
#if KMP_ARCH_X86_64 || KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 || \
- KMP_ARCH_MIPS64 || KMP_ARCH_RISCV64 || KMP_ARCH_LOONGARCH64 || KMP_ARCH_VE
+ KMP_ARCH_MIPS64 || KMP_ARCH_RISCV64 || KMP_ARCH_LOONGARCH64 || \
+ KMP_ARCH_VE || KMP_ARCH_WASM
#if KMP_OS_LINUX || KMP_OS_DRAGONFLY || KMP_OS_FREEBSD || KMP_OS_NETBSD || \
- KMP_OS_OPENBSD || KMP_OS_WINDOWS || KMP_OS_DARWIN || KMP_OS_HURD
+ KMP_OS_OPENBSD || KMP_OS_WINDOWS || KMP_OS_DARWIN || KMP_OS_HURD || \
+ KMP_OS_WASI
int t...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will also want (have) to disable certain features such as OMPD and OMPT for WASM.
M, Ty, /*IsConstant=*/false, GlobalValue::CommonLinkage, | ||
Constant::getNullValue(Ty), Elem.first(), | ||
/*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal, AddressSpace); | ||
auto linkage = this->M.getTargetTriple().rfind("wasm32") == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto linkage = this->M.getTargetTriple().rfind("wasm32") == 0 | |
auto Linkage = this->M.getTargetTriple().rfind("wasm32") == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make it a separate patch for this change
__kmp_unnamed_critical_addr: | ||
.4byte .gomp_critical_user_ | ||
.size __kmp_unnamed_critical_addr, 4 | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WASM doesn't require special definition of functions like __kmp_invoke_microtask
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To elaborate here a bit, when a parallel region is called, the task corresponding to the parallel region (microtask_t) that is generated by the compiler is sent through:
__kmpc_fork_call()
-> __kmp_fork_call()
-> __kmp_invoke_microtask(microtask_t, gtid, tid, argc, ...)
-> call the microtask with argc
number of "shareds"
where the varargs component is a list of pointers to shared variables (or firstprivates). There is a limited, platform-independent implementation which WASM is using inside z_linux_util.cpp
at around line 2492 which can handle up to 15 args. Anymore than that, and the runtime will error out. The test in openmp/runtime/test/misc_bugs/many-microtask-args.c
is an example of this limitation.
In z_Linux_asm.S
there are platform-dependent assembly versions of this function. For WASM, it would be best if an assembly version of the __kmp_invoke_microtask()
function is created to handle any number of "shareds" passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For WASM, it would be best if an assembly version of the __kmp_invoke_microtask() function is created to handle any number of "shareds" passed in.
I'm not sure if that is even possible here. WebAssembly does not have variable arguments, so to call the microtask I suspect we'll always need a switch
to dispatch to the right signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah OK, I understand the limitation now.
__kmp_unregister_library(); | ||
abort(); | ||
} | ||
#if KMP_OS_WINDOWS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this change separate?
✅ With the latest revision this PR passed the C/C++ code formatter. |
If you are planning to use LTO across the user code and runtime we need to disable certain parts of OpenMP-Opt or bad things might happen. |
63ac7be
to
b51e3ba
Compare
Ok, I rebased this on |
__kmp_unnamed_critical_addr: | ||
.4byte .gomp_critical_user_ | ||
.size __kmp_unnamed_critical_addr, 4 | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To elaborate here a bit, when a parallel region is called, the task corresponding to the parallel region (microtask_t) that is generated by the compiler is sent through:
__kmpc_fork_call()
-> __kmp_fork_call()
-> __kmp_invoke_microtask(microtask_t, gtid, tid, argc, ...)
-> call the microtask with argc
number of "shareds"
where the varargs component is a list of pointers to shared variables (or firstprivates). There is a limited, platform-independent implementation which WASM is using inside z_linux_util.cpp
at around line 2492 which can handle up to 15 args. Anymore than that, and the runtime will error out. The test in openmp/runtime/test/misc_bugs/many-microtask-args.c
is an example of this limitation.
In z_Linux_asm.S
there are platform-dependent assembly versions of this function. For WASM, it would be best if an assembly version of the __kmp_invoke_microtask()
function is created to handle any number of "shareds" passed in.
The `gomp_critical_user` lock is typically compiled using a common symbol, but the WebAssembly backend does not have these implemented. This change uses an external symbol instead, which avoids compiler crashes when compiling `#pragma omp critical` code to WebAssembly.
This change touches CMake files only. It sets up a new `LIBOMP_ARCH` option--`wasm32`--and provides a handy `WASM` shortcut variable for internal CMake use like other targets do. This also disables shared library support (WebAssembly expects a static one) and the `LIBOMPTARGET` functionality when targeting WebAssembly.
This change sets up definitions for the many preprocessor conditions used in OpenMP and compiles out features that will not be supported in a WebAssembly target. The `KMP_ARCH_WASM` target is tied to `wasm32` and ensures 32-bit pointers are used. The `KMP_OS_WASI` target is more tricky: it attempts to cover the subset of functionality that is compilable by both Emscripten and wasi-sdk. Since Emscripten currently has support for more of libc than wasi-sdk, the "WASI" name is used to indicate that only the wasi-sdk subset is used here. This change follows along the same lines as [D142593], though it takes things a bit further, compiles with multiple toolchains (Emscripten _and_ wasi-sdk), handles some new cases, and avoids for now the `microtask_*` changes from that PR. Many thanks to @arsnyder16 for providing some initial indication that this all was possible! [D142593]: https://reviews.llvm.org/D142593 Co-authored-by: Atanas Atanasov <[email protected]>
I updated this PR with @jpeyton52's suggestions and rebased again on the latest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the patch!
As the changes are now. I believe they are good enough as an initial support patch for WebAssembly.
When OpenMP is compiled for WebAssembly (see #71297), it invokes a microtask via a `switch` statement that dispatches to the `void *` microtask pointer with spelled-out arguments (not varargs). As #83329 points out, however, this can result in a type mismatch when the indirect call is executed by WebAssembly; WebAssembly expects the called pointer to have the precise type of the call site. This change fixes the issue by bringing back the approach in [D142593] of type-casting all the `switch` arms to the precise type. This fixes #83329. [D142593]: https://reviews.llvm.org/D142593
/*InsertBefore=*/nullptr, GlobalValue::NotThreadLocal, AddressSpace); | ||
auto Linkage = this->M.getTargetTriple().rfind("wasm32") == 0 | ||
? GlobalValue::ExternalLinkage | ||
: GlobalValue::CommonLinkage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abrown Hey Andrew, this seems to break linking under wasm.
Previously when it common linked it was required to define these variables through assembly, hence you updates to z_Linux_asm.S specially .global .gomp_critical_user_.reduction.var
are actually no longer required.
But now that they are external linkage they suffer from duplicate symbol problem.
For critical sections this can avoided if you manually add a name to the critical section that does not conflict
omp critical(UniqueValue)
but for reduction allowing them to be named is not an option, so if multiple compilation units use reduction you will introduce a duplicate symbols
wasm-ld: error: duplicate symbol: .gomp_critical_user_.reduction.var
>>> defined in lib/libTest.a(a.cpp.o)
>>> defined in lib/libTest.a(b.cpp.o)
I am wonder if something like InternalLinkage
is more appropriate
@@ -76,14 +77,20 @@ | |||
#define KMP_OS_SOLARIS 1 | |||
#endif | |||
|
|||
#if (defined __wasi__) || (defined __EMSCRIPTEN__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abrown
This is conflating wasi and the emscripten toolchain. They are two different cases where emscripten mimics linux/posix system by statically linking musl into the wasm file. This was mentioned in D142593
The changes in this PR will not allow openmp to compile under emscripten, the changes here will
#95169
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good finds, both of them. Let's discuss in #95169.
This change allows building the static OpenMP runtime,
libomp.a
, as WebAssembly. It builds on the work done in D142593 but goes further in several ways:KMP_ARCH_WASM
andKMP_OS_WASI
The commit messages have more details. Please understand this PR as a start, not the completed work, for WebAssembly support in OpenMP. Getting the tests running somehow would be a good next step, e.g.; but what is contained here works, at least with recent versions of wasi-sdk and engines that support wasi-threads. I suspect the same is true for Emscripten and browsers, but I have not tested that workflow.