Skip to content

Commit

Permalink
Emscripten and freestanding starting points
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdruppe committed Oct 21, 2024
1 parent fa1cd01 commit 8db4bb1
Show file tree
Hide file tree
Showing 64 changed files with 731 additions and 39 deletions.
1 change: 1 addition & 0 deletions druntime/src/core/cpuid.d
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ void cpuidSparc()
}
*/

version(WebAssembly) {} else
pragma(crt_constructor) void cpuid_initialization()
{
auto cf = getCpuFeatures();
Expand Down
8 changes: 8 additions & 0 deletions druntime/src/core/internal/abort.d
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ void abort(scope string msg, scope string filename = __FILE__, size_t line = __L
}
}
}
else version(FreeStanding)
{
void writeStr(scope const(char)[][] m...) @nogc nothrow @trusted
{
// FIXME
}

}
else
static assert(0, "Unsupported OS");

Expand Down
1 change: 1 addition & 0 deletions druntime/src/core/internal/backtrace/unwind.d
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ _Unwind_Reason_Code _Unwind_ForcedUnwind(
alias _Unwind_Trace_Fn = _Unwind_Reason_Code function(_Unwind_Context*, void*);

void _Unwind_DeleteException(_Unwind_Exception* exception_object);
version (WebAssembly) void _Unwind_Resume(void*) {} /* FIXME stub */ else
version (LDC) // simplify runtime function forward declaration
void _Unwind_Resume(void*);
else
Expand Down
44 changes: 44 additions & 0 deletions druntime/src/core/internal/elf/dl.d
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@

module core.internal.elf.dl;

version (Emscripten)
{
extern(C) void* emscripten_stack_get_base() @nogc nothrow;
// these only useful of the asyncify thing is active
// void emscripten_scan_stack(em_scan_func func)
// void emscripten_scan_registers(em_scan_func func)

// uintptr_t *emscripten_get_sbrk_ptr(void);
}

// no else here...

version (linux)
{
import core.sys.linux.link;
Expand Down Expand Up @@ -65,6 +77,38 @@ struct SharedObjects
return dg(SharedObject(*info));
}

version(Emscripten) {
// dl_iterate_phdr not implemented here (tho there is an open feature request for it)
// so let's just fake it i guess
__gshared dl_phdr_info info;
if(info.dlpi_phnum == 0) {
import core.sys.elf;
__gshared ElfW!"Phdr" phdr;
phdr.p_type = PT_LOAD;

// FIXME: what I'm actually trying to do here is find where
// mutable static/global data is. This hack assumes it is
// in between the stack and the malloc range, which should
// be accurate, but very imprecise.
phdr.p_vaddr = cast(size_t) emscripten_stack_get_base();
import core.stdc.stdlib;
auto ptr = malloc(1);
phdr.p_memsz = cast(size_t) ptr - cast(size_t) phdr.p_vaddr;
free(ptr);
assert(phdr.p_memsz < cast(size_t) ptr); // janky overflow check
phdr.p_flags = PF_W;
phdr.p_align = void*.alignof;

info.dlpi_addr = 0;
info.dlpi_name = "";
info.dlpi_phdr = &phdr;
info.dlpi_phnum = 1;
}

dg(SharedObject(info));

return 0;
} else
return dl_iterate_phdr(&nativeCallback, &dg);
}
}
Expand Down
20 changes: 8 additions & 12 deletions druntime/src/core/internal/gc/bits.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
*/
module core.internal.gc.bits;

import core.internal.gc.os : os_mem_map, os_mem_unmap;
import core.internal.gc.os : os_mem_map, os_mem_unmap, HaveFork;

import core.bitop;
import core.stdc.string;
import core.stdc.stdlib;
import core.exception : onOutOfMemoryError;

import rt.sys.config;

@system:

// use version gcbitsSingleBitOperation to disable optimizations that use
// word operands on bulk operation copyRange, setRange, clrRange, etc.
// version = gcbitsSingleBitOperation;
Expand Down Expand Up @@ -177,25 +173,25 @@ struct GCBits
}

// extract loops to allow inlining the rest
void clearWords(size_t firstWord, size_t lastWord) nothrow
void clearWords(size_t firstWord, size_t lastWord) nothrow @system
{
for (size_t w = firstWord; w < lastWord; w++)
data[w] = 0;
}

void setWords(size_t firstWord, size_t lastWord) nothrow
void setWords(size_t firstWord, size_t lastWord) nothrow @system
{
for (size_t w = firstWord; w < lastWord; w++)
data[w] = ~0;
}

void copyWords(size_t firstWord, size_t lastWord, const(wordtype)* source) nothrow
void copyWords(size_t firstWord, size_t lastWord, const(wordtype)* source) nothrow @system
{
for (size_t w = firstWord; w < lastWord; w++)
data[w] = source[w - firstWord];
}

void copyWordsShifted(size_t firstWord, size_t cntWords, size_t firstOff, const(wordtype)* source) nothrow
void copyWordsShifted(size_t firstWord, size_t cntWords, size_t firstOff, const(wordtype)* source) nothrow @system
{
wordtype mask = ~BITS_0 << firstOff;
data[firstWord] = (data[firstWord] & ~mask) | (source[0] << firstOff);
Expand All @@ -222,7 +218,7 @@ struct GCBits
}
}

void copyRangeZ(size_t target, size_t len, const(wordtype)* source) nothrow
void copyRangeZ(size_t target, size_t len, const(wordtype)* source) nothrow @system
{
mixin RangeVars!();

Expand Down Expand Up @@ -310,7 +306,7 @@ struct GCBits
}
}

void setRangeZ(size_t target, size_t len) nothrow
void setRangeZ(size_t target, size_t len) nothrow @system
{
mixin RangeVars!();

Expand Down Expand Up @@ -342,7 +338,7 @@ struct GCBits
}
}

void clrRangeZ(size_t target, size_t len) nothrow
void clrRangeZ(size_t target, size_t len) nothrow @system
{
mixin RangeVars!();
if (firstWord == lastWord)
Expand Down
56 changes: 51 additions & 5 deletions druntime/src/core/internal/gc/impl/conservative/gc.d
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ module core.internal.gc.impl.conservative.gc;
//debug = GC_RECURSIVE_LOCK; // check for recursive locking on the same thread
//debug = VALGRIND; // Valgrind memcheck integration


/***************************************************/
version = COLLECT_PARALLEL; // parallel scanning
version (Posix)
version = COLLECT_FORK;
version(WebAssembly) {
// parallel/fork not actually supported on emscripten
} else {
version = COLLECT_PARALLEL; // parallel scanning
version (Posix)
version = COLLECT_FORK;
}

import core.internal.gc.bits;
import core.internal.gc.os;
Expand Down Expand Up @@ -1266,7 +1271,7 @@ class ConservativeGC : GC
*/
size_t fullCollect() nothrow
{
debug(PRINTF) printf("GC.fullCollect()\n");
debug(PRINTF) printf("GC.fullCollect()\n");

// Since a finalizer could launch a new thread, we always need to lock
// when collecting.
Expand Down Expand Up @@ -1911,6 +1916,9 @@ struct Gcx

private @property bool lowMem() const nothrow
{
// size_t emscripten_get_heap_size(void);
// size_t emscripten_get_heap_max(void);

return isLowOnMem(cast(size_t)mappedPages * PAGESIZE);
}

Expand Down Expand Up @@ -2283,7 +2291,7 @@ struct Gcx
alias toscan = scanStack!precise;

debug(MARK_PRINTF)
printf("marking range: [%p..%p] (%#llx)\n", pbot, ptop, cast(long)(ptop - pbot));
printf("marking range: [%p..%p] (%#llx)\n", rng.pbot, rng.ptop, cast(long)(rng.ptop - rng.pbot));

// limit the amount of ranges added to the toscan stack
enum FANOUT_LIMIT = 32;
Expand Down Expand Up @@ -3018,6 +3026,20 @@ struct Gcx
*/
size_t fullcollect(bool nostack = false, bool block = false, bool isFinal = false) nothrow @system
{
version(Emscripten) {
if(!webassemblyStackIsEmpty) {
// queue the collect upon return to the browser's main thread
// since we can't see the webassembly stack and don't want to
// free things prematurely
if(!gcCollectQueued) {
emscripten_async_call(&__d_gcFromEmscripten, null, 0);
gcCollectQueued = true;
printf("Queuing GC\n");
}
return 0;
}
}

// It is possible that `fullcollect` will be called from a thread which
// is not yet registered in runtime (because allocating `new Thread` is
// part of `thread_attachThis` implementation). In that case it is
Expand All @@ -3026,6 +3048,8 @@ struct Gcx
if (Thread.getThis() is null)
return 0;

printf("Running GC\n");

MonoTime start, stop, begin;
begin = start = currTime;

Expand Down Expand Up @@ -5134,3 +5158,25 @@ void undefinedWrite(T)(ref T var, T value) nothrow
else
var = value;
}

version(Emscripten) {
__gshared bool webassemblyStackIsEmpty;
__gshared bool gcCollectQueued;

extern(C)
void __d_gcFromEmscripten(void*) nothrow {
webassemblyStackIsEmpty = true;
scope(exit)
webassemblyStackIsEmpty = false;
import core.memory;
GC.collect();
gcCollectQueued = false;
webassemblyStackIsEmpty = false;

import core.stdc.stdio;
printf("GC collection complete.\n");
}

extern(C)
int emscripten_async_call(void function(void*) cb, void* user, int ms) nothrow @nogc;
}
Loading

0 comments on commit 8db4bb1

Please sign in to comment.