diff options
author | Cheng Shao <astrohavoc@gmail.com> | 2022-10-24 05:47:25 +0000 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2022-11-11 00:26:55 -0500 |
commit | d7b33982df0dfbb5b66888037dc9baf59c8af339 (patch) | |
tree | 153755ff85bd758c181d88415d50e3a1c25dba6d | |
parent | a6ac67b0ac56c6d21ec05cba2ad31ec737c8f5ef (diff) | |
download | haskell-d7b33982df0dfbb5b66888037dc9baf59c8af339.tar.gz |
rts: wasm32 specific logic
This patch adds the rest of wasm32 specific logic in rts.
-rw-r--r-- | rts/rts.cabal.in | 8 | ||||
-rw-r--r-- | rts/wasm/GetTime.c | 72 | ||||
-rw-r--r-- | rts/wasm/OSMem.c | 95 | ||||
-rw-r--r-- | rts/wasm/OSThreads.c | 9 | ||||
-rw-r--r-- | rts/wasm/Ops.c | 9 | ||||
-rw-r--r-- | rts/wasm/StgRun.c | 21 | ||||
-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 |