summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheng Shao <astrohavoc@gmail.com>2022-10-24 05:47:25 +0000
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-11-11 00:26:55 -0500
commitd7b33982df0dfbb5b66888037dc9baf59c8af339 (patch)
tree153755ff85bd758c181d88415d50e3a1c25dba6d
parenta6ac67b0ac56c6d21ec05cba2ad31ec737c8f5ef (diff)
downloadhaskell-d7b33982df0dfbb5b66888037dc9baf59c8af339.tar.gz
rts: wasm32 specific logic
This patch adds the rest of wasm32 specific logic in rts.
-rw-r--r--rts/rts.cabal.in8
-rw-r--r--rts/wasm/GetTime.c72
-rw-r--r--rts/wasm/OSMem.c95
-rw-r--r--rts/wasm/OSThreads.c9
-rw-r--r--rts/wasm/Ops.c9
-rw-r--r--rts/wasm/StgRun.c21
-rw-r--r--rts/wasm/Wasm.S (renamed from rts/wasm32/Wasm32.S)0
7 files changed, 214 insertions, 0 deletions
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index 2a113e8867..34da06a365 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -611,6 +611,14 @@ library
win32/WorkQueue.c
win32/veh_excn.c
-- win32/**/*.c
+ elif arch(wasm32)
+ asm-sources: wasm/Wasm.S
+ c-sources: wasm/StgRun.c
+ wasm/GetTime.c
+ wasm/Ops.c
+ wasm/OSMem.c
+ wasm/OSThreads.c
+ posix/Select.c
else
c-sources: posix/GetEnv.c
posix/GetTime.c
diff --git a/rts/wasm/GetTime.c b/rts/wasm/GetTime.c
new file mode 100644
index 0000000000..3e9e304f39
--- /dev/null
+++ b/rts/wasm/GetTime.c
@@ -0,0 +1,72 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2005
+ *
+ * Machine-dependent time measurement functions
+ *
+ * ---------------------------------------------------------------------------*/
+
+// Not POSIX, due to use of ru_majflt in getPageFaults()
+// #include "rts/PosixSource.h"
+
+#include "Rts.h"
+#include "GetTime.h"
+
+#include <time.h>
+#include <sys/time.h>
+
+void initializeTimer()
+{
+}
+
+static Time getClockTime(clockid_t clock)
+{
+ struct timespec ts;
+ int res = clock_gettime(clock, &ts);
+ if (res == 0) {
+ return SecondsToTime(ts.tv_sec) + NSToTime(ts.tv_nsec);
+ } else {
+ sysErrorBelch("clock_gettime");
+ stg_exit(EXIT_FAILURE);
+ }
+}
+
+Time getCurrentThreadCPUTime(void)
+{
+ return getClockTime(CLOCK_MONOTONIC);
+}
+
+Time getProcessCPUTime(void)
+{
+ return getClockTime(CLOCK_MONOTONIC);
+}
+
+StgWord64 getMonotonicNSec(void)
+{
+ return getClockTime(CLOCK_MONOTONIC);
+}
+
+Time getProcessElapsedTime(void)
+{
+ return NSToTime(getMonotonicNSec());
+}
+
+void getProcessTimes(Time *user, Time *elapsed)
+{
+ *user = getProcessCPUTime();
+ *elapsed = getProcessElapsedTime();
+}
+
+void getUnixEpochTime(StgWord64 *sec, StgWord32 *nsec)
+{
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone *) NULL);
+ *sec = tv.tv_sec;
+ *nsec = tv.tv_usec * 1000;
+}
+
+W_
+getPageFaults(void)
+{
+ return 0;
+}
diff --git a/rts/wasm/OSMem.c b/rts/wasm/OSMem.c
new file mode 100644
index 0000000000..88cbc9c248
--- /dev/null
+++ b/rts/wasm/OSMem.c
@@ -0,0 +1,95 @@
+// Note [Megablock allocator on wasm]
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Wasm modules operate on a continuous linear memory, and one can use
+// the memory.size/memory.grow opcodes to query the current memory
+// size and extend it, both of which operates in the units of 64KiB
+// pages. Compared to typical unix processes, the Wasm memory address
+// space doesn't have page-level read/write access restriction,
+// doesn't have holes that segfaults when accessed, and there's no
+// native mmap().
+
+// It's possible to have userland mmap() emulation in Wasm that only
+// allows mapping anonymous pages by calling malloc(). But the way our
+// unix megablock allocator uses mmap() doesn't work well with
+// existing emulations: we over-allocate then trim to guarantee
+// alignment, and we do a lot of partial munmap()s. So we might as
+// well implement our own OSMem.c logic for wasm.
+//
+// To allocate megablocks, it's possible to call aligned_alloc()
+// directly. But there's still significant space waste: when
+// aligned_alloc() is called consecutively to allocate multiple
+// megablocks, there are gaps no smaller than 1 megablock between
+// returned spaces. This is natural, since malloc() implementations
+// tend to use extra header/footer around allocated space to maintain
+// internal metadata (tested with dlmalloc/emmalloc/mimalloc). It's
+// possible to mitigate the space waste issue by adding custom pooling
+// logic, but there's a better solution here.
+//
+// wasi-libc uses dlmalloc, which calls sbrk() to grab memory from the
+// system, which calls memory.grow under the hood. In unix programs,
+// it's typically a bad practice to call both malloc() and sbrk()
+// within the same address space, since sbrk() calls may violate the
+// libc allocator's certain invariants. But dlmalloc permits this
+// behavior!
+//
+// Therefore, we bypass dlmalloc, and directly call memory.grow to
+// allocate megablocks. We even patch dlmalloc in the libc sysroot
+// shipped in our wasi-sdk release, so that whenever dlmalloc calls
+// sbrk(), it extends the linear memory to align to the megablock
+// size, so to avoid space waste as much as possible. Our wasi-libc
+// patch doesn't impact ABI interoperability, and when stock clang
+// compiles code that calls malloc() to wasm objects, those objects
+// would just link fine with our build products.
+//
+// One remaining question is how to free a megablock. Wasm spec
+// doesn't allow shrinking the linear memory, so the logic of
+// returning memory to the OS doesn't make sense here. Given the
+// megablock allocator already has its internal free-list, we avoid
+// writing our own pooling logic, and simply avoid returning any
+// megablock on Wasm.
+
+#include "Rts.h"
+
+#include "RtsUtils.h"
+#include "sm/OSMem.h"
+#include "sm/HeapAlloc.h"
+
+#include <__macro_PAGESIZE.h>
+
+void osMemInit(void)
+{
+}
+
+void *
+osGetMBlocks(uint32_t n)
+{
+ size_t base = __builtin_wasm_memory_size(0) * PAGESIZE;
+ size_t start = MBLOCK_ROUND_UP(base);
+ size_t end = start + (n << MBLOCK_SHIFT);
+ ptrdiff_t delta = (end - base) / PAGESIZE;
+ if (__builtin_wasm_memory_grow(0, delta) == SIZE_MAX)
+ barf("osGetMBlocks failed");
+ return start;
+}
+
+void osBindMBlocksToNode(
+ void *addr STG_UNUSED,
+ StgWord size STG_UNUSED,
+ uint32_t node STG_UNUSED)
+{
+}
+
+StgWord64 getPhysicalMemorySize (void)
+{
+ return 1ULL << 32;
+}
+
+uint32_t osNumaNodes(void)
+{
+ return 1;
+}
+
+uint64_t osNumaMask(void)
+{
+ return 1;
+}
diff --git a/rts/wasm/OSThreads.c b/rts/wasm/OSThreads.c
new file mode 100644
index 0000000000..10e4de9731
--- /dev/null
+++ b/rts/wasm/OSThreads.c
@@ -0,0 +1,9 @@
+#include "rts/PosixSource.h"
+#include "ghcconfig.h"
+#include "Rts.h"
+
+uint32_t
+getNumberOfProcessors (void)
+{
+ return 1;
+}
diff --git a/rts/wasm/Ops.c b/rts/wasm/Ops.c
new file mode 100644
index 0000000000..198c8e5ded
--- /dev/null
+++ b/rts/wasm/Ops.c
@@ -0,0 +1,9 @@
+#include "Stg.h"
+
+StgWord8 hs_mulIntMayOflo8(StgWord8 _0, StgWord8 _1) { return mulIntMayOflo(_0, _1); }
+
+StgWord16 hs_mulIntMayOflo16(StgWord16 _0, StgWord16 _1) { return mulIntMayOflo(_0, _1); }
+
+StgWord32 hs_mulIntMayOflo32(StgWord32 _0, StgWord32 _1) { return mulIntMayOflo(_0, _1); }
+
+StgWord64 hs_mulIntMayOflo64(StgWord64 _0, StgWord64 _1) { return mulIntMayOflo(_0, _1); }
diff --git a/rts/wasm/StgRun.c b/rts/wasm/StgRun.c
new file mode 100644
index 0000000000..b3442a9242
--- /dev/null
+++ b/rts/wasm/StgRun.c
@@ -0,0 +1,21 @@
+#include "Rts.h"
+
+// We directly return the same BaseReg as passed to StgRun. This is
+// fine on wasm which doesn't have SMP.
+//
+// Reading BaseReg from R1 is quite tricky; we map R1 to a wasm
+// global, and reading from wasm globals from C requires calling a
+// getter function implemented in assembly, which is totally not worth
+// the effort.
+StgRegTable * StgRun(StgFunPtr f, StgRegTable *basereg)
+{
+ while (f) {
+ f = (StgFunPtr) (f)();
+ }
+ return basereg;
+}
+
+StgFunPtr StgReturn(void)
+{
+ return 0;
+}
diff --git a/rts/wasm32/Wasm32.S b/rts/wasm/Wasm.S
index a2321c0b63..a2321c0b63 100644
--- a/rts/wasm32/Wasm32.S
+++ b/rts/wasm/Wasm.S