digitalmars.D - Static ctors in wasm
- H. S. Teoh (11/11) Feb 08 How are static ctors actually implemented in D? I was under the
- kinke (21/31) Feb 08 It primarily depends on the binary format, and is a cooperation
- kinke (4/5) Feb 08 Oh well, looks like wasm-ld (i.e., lld) supports exactly the same
- kinke (4/7) Feb 08 Oh man, I've even added an LDC test for this, but almost 4 years
- Sebastiaan Koppe (2/5) Feb 08 Yep, I remember asking you about for my druntime wasm port.
- H. S. Teoh (35/44) Feb 15 Hmm, I'm running into a problem here. Following the above link I
- David Gileadi (5/58) Feb 15 While I have zero knowledge of how LDC handles WASM, I did notice one
- H. S. Teoh (9/21) Feb 15 [...]
- Richard (Rikki) Andrew Cattermole (3/3) Feb 15 Start back at the basics, look at the object file dump, is ModuleInfo
- H. S. Teoh (6/9) Feb 15 Does it need to be defined in object.d?
- Richard (Rikki) Andrew Cattermole (3/16) Feb 15 Yes.
- H. S. Teoh (11/25) Feb 15 Figures! I was under the wrong impression that I only need to define it
- Richard (Rikki) Andrew Cattermole (4/31) Feb 15 Great!
- H. S. Teoh (9/20) Feb 15 [...]
- Richard (Rikki) Andrew Cattermole (30/35) Feb 15 I did some searching.
- Richard (Rikki) Andrew Cattermole (6/6) Feb 16 https://github.com/llvm/llvm-project/issues/23280
- H. S. Teoh (12/18) Feb 15 [...]
- Sebastiaan Koppe (14/17) Feb 08 Might I direct you to my past efforts of a druntime port? This is
- H. S. Teoh (24/41) Feb 09 Oh cool!
How are static ctors actually implemented in D? I was under the impression that there's some kind of table stored in the executable that the druntime startup code traverses. But apparently this is OS-dependent?? I'm trying to get static ctors to work in wasm, but can't find any way of making it work. The druntime code that I looked into all hook into stuff dependent on the executable format, and obviously there isn't anything for wasm (yet). Is this even possible in wasm? Or am I missing something obvious? T -- A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. -- Michael B. Allen
Feb 08
On Friday, 9 February 2024 at 00:02:12 UTC, H. S. Teoh wrote:How are static ctors actually implemented in D? I was under the impression that there's some kind of table stored in the executable that the druntime startup code traverses. But apparently this is OS-dependent?? I'm trying to get static ctors to work in wasm, but can't find any way of making it work. The druntime code that I looked into all hook into stuff dependent on the executable format, and obviously there isn't anything for wasm (yet).It primarily depends on the binary format, and is a cooperation between compiler and druntime. Usually, the compiler emits non-linker-strippable pointers to compiler-generated `ModuleInfo` structs into a special section (`__minfo`, `.minfo` or so), one for each .d module getting compiled into a specific object file. After linking to an executable/shared library, the ModuleInfo pointers of *all* .d modules of *each linked* object file are then stored consecutively in that named section of the data segment. This way, druntime can then reflect on all D modules/ModuleInfos linked into a binary, by getting that data range of ModuleInfo pointers (e.g., via linker-generated `__{start,stop}___minfo` bracketing symbols for ELF). And from there, module ctors, unittests etc. can be inferred.Is this even possible in wasm? Or am I missing something obvious?LDC has a fallback for exotic platforms, where it uses a linked list, and compiler-generated CRT-constructor functions which insert the ModuleInfo pointers at program initialization (in undefined order), invoked by the C runtime before C main(). So if wasm doesn't support named sections/data ranges, but would 'implicitly' support initializer functions (CRT constructors), that could be a potential route to take.
Feb 08
On Friday, 9 February 2024 at 00:48:47 UTC, kinke wrote:So if wasm doesn't support named sections/data rangesOh well, looks like wasm-ld (i.e., lld) supports exactly the same magic `__{start,stop}_*` symbols as for ELF: https://github.com/llvm/llvm-project/issues/55839
Feb 08
On Friday, 9 February 2024 at 01:17:26 UTC, kinke wrote:Oh well, looks like wasm-ld (i.e., lld) supports exactly the same magic `__{start,stop}_*` symbols as for ELF: https://github.com/llvm/llvm-project/issues/55839Oh man, I've even added an LDC test for this, but almost 4 years ago and so forgot. :D https://github.com/ldc-developers/ldc/blob/3c21924705aae83f0c16bfe54e673953671afe58/tests/codegen/wasi.d#L22-L39
Feb 08
On Friday, 9 February 2024 at 01:22:28 UTC, kinke wrote:Oh man, I've even added an LDC test for this, but almost 4 years ago and so forgot. :D https://github.com/ldc-developers/ldc/blob/3c21924705aae83f0c16bfe54e673953671afe58/tests/codegen/wasi.d#L22-L39Yep, I remember asking you about for my druntime wasm port.
Feb 08
On Fri, Feb 09, 2024 at 01:22:28AM +0000, kinke via Digitalmars-d wrote:On Friday, 9 February 2024 at 01:17:26 UTC, kinke wrote:Hmm, I'm running into a problem here. Following the above link I declared the symbols like this: ```d extern(C) extern __gshared { void* __start__minfo; void* __stop__minfo; } ``` However, when I check their values at runtime: ```d export void _wasm_start() // this is called from my JS bridge { writefln("__start__minfo=0x%x", &__start__minfo); writefln("__end__minfo=0x%x", &__stop__minfo); } ``` (I have a bare-bones version of writefln running for debugging purposes.) The output says: ```` __start__minfo=0x0 __end__minfo=0x%0 ```` What gives? For reference, I'm using LDC 1.36.0 on x86_64--linux-gnu, with these compile flags: -mtriple=wasm32-unknown-unknown-wasm -Iplatform/wasm -O2 -L-allow-undefined -allinst -linkonce-templates --fvisibility=hidden I had --fno-rtti before and it didn't work; I removed it but it still made no difference. I guess it's unrelated to RTTI. Also, I tried removing --fvisibility-hidden but it also made no difference. What am I doing wrong? T -- Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.Oh well, looks like wasm-ld (i.e., lld) supports exactly the same magic `__{start,stop}_*` symbols as for ELF: https://github.com/llvm/llvm-project/issues/55839Oh man, I've even added an LDC test for this, but almost 4 years ago and so forgot. :D https://github.com/ldc-developers/ldc/blob/3c21924705aae83f0c16bfe54e673953671afe58/tests/codegen/wasi.d#L22-L39
Feb 15
On 2/15/24 2:45 PM, H. S. Teoh wrote:On Fri, Feb 09, 2024 at 01:22:28AM +0000, kinke via Digitalmars-d wrote:While I have zero knowledge of how LDC handles WASM, I did notice one difference between your code and the example in the link: the definition at the link has three underscores before the `minfo` whereas your code has only two.On Friday, 9 February 2024 at 01:17:26 UTC, kinke wrote:Hmm, I'm running into a problem here. Following the above link I declared the symbols like this: ```d extern(C) extern __gshared { void* __start__minfo; void* __stop__minfo; } ``` However, when I check their values at runtime: ```d export void _wasm_start() // this is called from my JS bridge { writefln("__start__minfo=0x%x", &__start__minfo); writefln("__end__minfo=0x%x", &__stop__minfo); } ``` (I have a bare-bones version of writefln running for debugging purposes.) The output says: ```` __start__minfo=0x0 __end__minfo=0x%0 ```` What gives? For reference, I'm using LDC 1.36.0 on x86_64--linux-gnu, with these compile flags: -mtriple=wasm32-unknown-unknown-wasm -Iplatform/wasm -O2 -L-allow-undefined -allinst -linkonce-templates --fvisibility=hidden I had --fno-rtti before and it didn't work; I removed it but it still made no difference. I guess it's unrelated to RTTI. Also, I tried removing --fvisibility-hidden but it also made no difference. What am I doing wrong?Oh well, looks like wasm-ld (i.e., lld) supports exactly the same magic `__{start,stop}_*` symbols as for ELF: https://github.com/llvm/llvm-project/issues/55839Oh man, I've even added an LDC test for this, but almost 4 years ago and so forgot. :D https://github.com/ldc-developers/ldc/blob/3c21924705aae83f0c16bfe54e673953671afe58/tests/codegen/wasi.d#L22-L39
Feb 15
On Thu, Feb 15, 2024 at 03:04:28PM -0700, David Gileadi via Digitalmars-d wrote:On 2/15/24 2:45 PM, H. S. Teoh wrote:[...][...]```d extern(C) extern __gshared { void* __start__minfo; void* __stop__minfo; } ```While I have zero knowledge of how LDC handles WASM, I did notice one difference between your code and the example in the link: the definition at the link has three underscores before the `minfo` whereas your code has only two.Whoa... I totally missed that! :-O However, correcting it to 3 underscores still produced 0. :-( What's going on?? T -- I am not young enough to know everything. -- Oscar Wilde
Feb 15
Start back at the basics, look at the object file dump, is ModuleInfo being generated? If not, have you copied ModuleInfo over?
Feb 15
On Fri, Feb 16, 2024 at 02:28:25PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:Start back at the basics, look at the object file dump, is ModuleInfo being generated?It's missing from the object file.If not, have you copied ModuleInfo over?Does it need to be defined in object.d? T -- Doctor: "Sir, I’m afraid your DNA is backwards." / Patient: "And?"
Feb 15
On 16/02/2024 2:57 PM, H. S. Teoh wrote:On Fri, Feb 16, 2024 at 02:28:25PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:Yes. https://github.com/dlang/dmd/blob/a952831fdb2877199c8eda07292757a0c5c29a1a/compiler/src/dmd/dmsc.d#L82Start back at the basics, look at the object file dump, is ModuleInfo being generated?It's missing from the object file.If not, have you copied ModuleInfo over?Does it need to be defined in object.d? T
Feb 15
On Fri, Feb 16, 2024 at 03:08:06PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:On 16/02/2024 2:57 PM, H. S. Teoh wrote:[...]On Fri, Feb 16, 2024 at 02:28:25PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:Start back at the basics, look at the object file dump, is ModuleInfo being generated?It's missing from the object file.If not, have you copied ModuleInfo over?Does it need to be defined in object.d?Yes. https://github.com/dlang/dmd/blob/a952831fdb2877199c8eda07292757a0c5c29a1a/compiler/src/dmd/dmsc.d#L82Figures! I was under the wrong impression that I only need to define it for code that actually needs to traverse it. Thanks for the tip!! I copy-n-pasted ModuleInfo from the real (non-wasm) object.d, and now I'm finally getting an address for __start___minfo. However, __end___minfo for some reason still shows up as 0x0? I must be missing something else? T -- Famous last words: I *think* this will work...
Feb 15
On 16/02/2024 5:45 PM, H. S. Teoh wrote:On Fri, Feb 16, 2024 at 03:08:06PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:Great! Unfortunately I don't know how the lists created by the linker work. But I do know that they are linker specific.On 16/02/2024 2:57 PM, H. S. Teoh wrote:[...]On Fri, Feb 16, 2024 at 02:28:25PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:Start back at the basics, look at the object file dump, is ModuleInfo being generated?It's missing from the object file.If not, have you copied ModuleInfo over?Does it need to be defined in object.d?Yes. https://github.com/dlang/dmd/blob/a952831fdb2877199c8eda07292757a0c5c29a1a/compiler/src/dmd/dmsc.d#L82Figures! I was under the wrong impression that I only need to define it for code that actually needs to traverse it. Thanks for the tip!! I copy-n-pasted ModuleInfo from the real (non-wasm) object.d, and now I'm finally getting an address for __start___minfo. However, __end___minfo for some reason still shows up as 0x0? I must be missing something else? T
Feb 15
On Fri, Feb 16, 2024 at 06:02:46PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:On 16/02/2024 5:45 PM, H. S. Teoh wrote:[...][...]I copy-n-pasted ModuleInfo from the real (non-wasm) object.d, and now I'm finally getting an address for __start___minfo. However, __end___minfo for some reason still shows up as 0x0? I must be missing something else?Great! Unfortunately I don't know how the lists created by the linker work. But I do know that they are linker specific.Hmm, the module traversal code seems to depend on SectionGroup. Does this need to be imported by object.d too? Currently I only have it in a submodule in the runtime. T -- If you look at a thing nine hundred and ninety-nine times, you are perfectly safe; if you look at it the thousandth time, you are in frightful danger of seeing it for the first time. -- G. K. Chesterton
Feb 15
On 16/02/2024 6:02 PM, Richard (Rikki) Andrew Cattermole wrote:Great! Unfortunately I don't know how the lists created by the linker work. But I do know that they are linker specific.I did some searching. ``__start_section`` and ``__stop_section`` are generated by the linker automatically for a given section. https://stackoverflow.com/a/48550485 I have been able to replicate what it should be doing using run.dlang.io. ```d import std; import core.attribute; void main() { writeln([var1, var2, var3], __start_sect_test, __stop_sect_test); } __gshared extern(C) extern { immutable int* __start_sect_test; immutable int* __stop_sect_test; } __gshared { (ldc.attributes.section("sect_test")) int var1 = 72; (ldc.attributes.section("sect_test")) int var2 = 43; (ldc.attributes.section("sect_test")) int var3 = 59; } ``` Now you'll have something to compare the ``__minfo`` symbols to. One thing to check is that your triple is for the binary wasm otherwise it might not be using sections. https://github.com/ldc-developers/ldc/blob/6ede9a4fdfd04724fc28a60e6460993d8344136f/gen/modules.cpp#L105
Feb 15
https://github.com/llvm/llvm-project/issues/23280 https://reviews.llvm.org/D64148 This is consistently appearing to be related to GC'ing of sections. I.e. try ``--no-gc-sections``. Alternatively it may be better to swap over to the linked list approach, but you'll need to compile ldc to switch it over.
Feb 16
On Thu, Feb 15, 2024 at 01:45:50PM -0800, H. S. Teoh via Digitalmars-d wrote: [...]The output says: ```` __start__minfo=0x0 __end__minfo=0x%0 ````[...] Ack, stray character got stuck in there somehow. Here's a pristine copy-n-paste from the output log: ```` __start__minfo=0x0 __end__minfo=0x0 ```` T -- We are in class, we are supposed to be learning, we have a teacher... Is it too much that I expect him to teach me??? -- RL
Feb 15
On Friday, 9 February 2024 at 00:02:12 UTC, H. S. Teoh wrote:Is this even possible in wasm? Or am I missing something obvious? TMight I direct you to my past efforts of a druntime port? This is a good entry point https://github.com/skoppe/ldc/tree/wasm as it contains the changes for LDC and submodules for Phobos and druntime. I got everything working, except for things that aren't supported like exceptions and fibers. Its just 3 year behind on master. The other issue is the GC not seeing all pointers hence freeing too much. As mentioned in another thread this can be solved by spilling the pointers to the shadow stack, which involves either changes to LDC/llvm or doing a post build step. Nowadays there is binaryen which reimplemented the required pass. I haven't been able to test it but others have used it with a similar Böhm GC.
Feb 08
On Fri, Feb 09, 2024 at 01:17:26AM +0000, kinke via Digitalmars-d wrote:On Friday, 9 February 2024 at 00:48:47 UTC, kinke wrote:Oh cool! But is ModuleInfo emitted when compiling with --fno-rtti? I adapted Sebastiaan Koppe's code to traverse module info at runtime, but I'm getting an empty list... or maybe I missed something, as usual. On Fri, Feb 09, 2024 at 07:21:04AM +0000, Sebastiaan Koppe via Digitalmars-d wrote: [...]So if wasm doesn't support named sections/data rangesOh well, looks like wasm-ld (i.e., lld) supports exactly the same magic `__{start,stop}_*` symbols as for ELF: https://github.com/llvm/llvm-project/issues/55839Might I direct you to my past efforts of a druntime port? This is a good entry point https://github.com/skoppe/ldc/tree/wasm as it contains the changes for LDC and submodules for Phobos and druntime.Thanks, that was very helpful!I got everything working, except for things that aren't supported like exceptions and fibers.Hmm. I wonder if it's possible to implement exceptions without stack unwinding. Like using sumtypes or a dedicated error flag/register under the hood, with the compiler transparently inserting the necessary branches after calling a non-nothrow function. Would require a major ABI change though, may not be feasible. But this could be a good opportunity to experiment with alternative exception implementations. [...]The other issue is the GC not seeing all pointers hence freeing too much. As mentioned in another thread this can be solved by spilling the pointers to the shadow stack, which involves either changes to LDC/llvm or doing a post build step. Nowadays there is binaryen which reimplemented the required pass. I haven't been able to test it but others have used it with a similar Böhm GC.I'll look into it when I get to that point. :-D Currently, for my purposes, it's Good Enough(tm) to preallocate everything in a setup function and then just use a bump-the-pointer allocator per callback, resetting to last pointer location afterwards. Sorta like a poor man's region allocator. As long as no pointers persist beyond the callback I should be OK. T -- Ph.D. = Permanent head Damage
Feb 09