-
-
Notifications
You must be signed in to change notification settings - Fork 670
Relocatable module experiment #890
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
Just to validate this as a future option (when supported by the spec) I'll pull in a reference the the design doc
|
After rereading https://github.com/WebAssembly/design/blob/master/DynamicLinking.md (for the 100th time) I feel like this may be possible with the current spec I played around with it a little, and your pr is currently generating the following for two globals (data (i32.add (global.get $__memory_base)(i32.const 8)) "\16\00\00\00\01\00\00\00\01\00\00\00\16\00\00\00a\00p\00p\001\00 \00g\00l\00o\00b\00a\00l\00")
(data (i32.add (global.get $__memory_base)(i32.const 48)) "@\00\00\00\01\00\00\00\01\00\00\00@\00\00\00t\00h\00i\00s\00 \00i\00s\00 \00a\00n\00o\00t\00h\00e\00r\00 \00g\00l\00o\00b\00a\00l\00 \00f\00r\00o\00m\00 \00a\00p\00p\001\00")
I dug through a couple of implementations and it appears the the data segments are additive/relative by default, so you only need this (data (global.get $__memory_base) "\16\00\00\00\01\00\00\00\01\00\00\00\16\00\00\00a\00p\00p\001\00 \00g\00l\00o\00b\00a\00l\00")
(data (i32.const 48) "@\00\00\00\01\00\00\00\01\00\00\00@\00\00\00t\00h\00i\00s\00 \00i\00s\00 \00a\00n\00o\00t\00h\00e\00r\00 \00g\00l\00o\00b\00a\00l\00 \00f\00r\00o\00m\00 \00a\00p\00p\001\00") I've done a quick translation of the above using wat2wasm, and it actually seemed to function correctly? I set my first import of memory_base to 8 to mimic what you did in your generated code and it built/ran fine. I'm sure I'm missing something, but it looks promising to me. Thoughts? |
Afaik these are absolute, unfortunately. If your module works, I either assume because |
Yeah you were correct they are absolute.. I do have good initial results merging the data statements(by hand) into one large statement with the imported offset.. But I haven't figured out if there is a way to have binaryen generate that. |
That's a great idea! |
Gave that a try and seems to be working. Requires importing a suitable memory as well, though, because the compiler doesn't know the correct |
Thinking one step further: It won't be possible to use the same workaround for the table since we can't "merge" table elements like memory segments. Unfortunate. |
Very cool, I'll play around with a bit and see where it goes. Thanks! |
So far looks quite good..but i have the issue of not really needing tables for our project (yet?) but from a memory side seems to work a treat. I'll integrate that with my original shared-runtime example and see what the next issue becomes |
The table becomes important as soon as a function is passed around by reference, like when providing a callback to |
Regarding #891: The compiler didn't yet relocate inlined offsets, which it does now, but leads to various issues in these cases. Accessing |
I couldn't sleep thinking about this so I put some more time into it I do apologize this isn't compatible with your current commit, as it would have been to much work to rebase my repo (at the moment). I am able to build, pass validation and perform the following const someStaticStuff: i32[] = [0];
someStaticStuff;
//verifies that the ptr is > memory_base and that the strings match
@external("env","log")
declare function log<T>(obj:T,length:i32):void
//verifies that the ptr is greater than memory_base
@external("env","checkPtr")
declare function checkPtr<T>(obj:T):void
const test1="relocatable"
export function main():void{
someStaticStuff[0]=1
checkPtr(someStaticStuff)
log(test1,test1.length)
const test2="relocatable"
log(test2,test2.length)
let test3=String.UTF16.encode(test2)
log(test3,test2.length)
} Please have a look if you dont mind I'd like your advice on if you would prefer the way you did your last commit, if so I'll spend some time to update before I make a PR |
Also still thinking how to do this. I guess a better approach to my previous is to make the relocation opaque to the module's logic so we can keep using constants and so on, with the downside that any pointer leaving the module must be relocated by the loader / RT. Last change makes it so that even if we have a |
However, that makes objects from one module non-interoperable with other modules without adding relocation on the boundary, which will become problematic once modules can call into each other without the help of a loader. At this point everything would go through a loader, though. In fact, the loader would sometimes even pass negative pointers around this way, that become properly absolute in the domain of a relocated module again, for example if the calling module is located before the called module in linear memory. Hmm hmm... One can think of this as each module having its own virtual zero-based address space, not knowing that its memory region is actually being relocated to somewhere else, with the loader taking all the necessary steps to make pointers interoperable between modules. Let's say we have module A and B in this order in linear memory, if module A calls an export of module B with a static pointer, the loader would make that |
I continued down the path I started to see how far I could get and was able to get it working with my shared-rt example. Take a look and see what you think..i also updated my relocatable fork if you would like to only see the changes to the compiler for relocatable. (keep in mind my relocatable is out of sync with yours) I also created a DESIGN.md trying to start explaining how shared-rt works I need to develop some tests and start look`ing into tables...but I feel like I'm still making progress. Oh and the README in shared-rt is out of date so dont read it (: |
I've been playing with tables and I can't get the compiler to generate more than one elem section? It always just appends to the first.. what would cause it to generate more than one line so I can test some things? |
That's how it's currently generated, yeah. Might have something to do with how Binaryen handles this. The API there is (elem (i32.const 0) $null $start:empty~anonymous|0 $start:empty~anonymous|1)
; ^ use __table_base Also, your design doc aligns well with my thoughts :) |
Last commit implements table relocation using a custom Binaryen build including WebAssembly/binaryen#2380. |
Remaining challenge is to obtain |
One way to expose this information might be to hijack the proposed dylink section (while it's not available) by adding an API to Binaryen that lets us emit arbitrary custom sections, and use it by means of WebAssembly.Module.customSections today. |
Last commit hijacks the dylink section as mentioned using latest Binaryen. Removes the |
Great work, I did some mods to the compiler test runner to be able to run multiple modules in one test (via a modules section in the test .json config) so I'll pull these in and see if I can build a few tests showing some possible use cases.
Sam
…________________________________
From: Daniel Wirtz <notifications@github.com>
Sent: Saturday, October 12, 2019 9:17:42 AM
To: AssemblyScript/assemblyscript <assemblyscript@noreply.github.com>
Cc: sam <sam@tech4ind.com>; Comment <comment@noreply.github.com>
Subject: Re: [AssemblyScript/assemblyscript] Relocatable module experiment (#890)
Last commit hijacks the dylink section as mentioned using latest Binaryen. Removes the memory_size and table_size exports in favor of evaluating the section, which we can do before instantiation now. Should theoretically be sufficient now to build a working dynamic linker.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub<#890?email_source=notifications&email_token=ACDGQ57GHRPRELMRDNMTX5LQOHTBNA5CNFSM4I6634G2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBCBUKY#issuecomment-541334059>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ACDGQ53B3JDVCONY2K36B3LQOHTBNANCNFSM4I6634GQ>.
|
So, regarding a shared runtime, it appears that with each module having its own set of runtime types along the respective compiler generated logic to visit these, that garbage collection has to remain local to the module at least partly, requiring changing a few APIs to make this possible. Affected APIs are The general problem that remains is that everything but hard coded types (ArrayBuffer, String) are incompatible between modules and that some sort of serialization is required for everything else. |
Last commit has a shared runtime concept with an overview here. It still remains to be seen if this works, though :) |
@dcodeIO thats looking real good, I still need to explore a few things and I would like to write some tests that support our use cases. As I mentioned before I reworked the compiler test runner to allow child modules to be instantiated. Is that a direction you would like to go, or are we flirting with more of an 'integration' test and should make a separate runner i.e. I at least want to do my internal testing in a way that I could eventually (without much rework) contribute them if they benefit the project |
This draft aged surprisingly well, and it might still be feasible to return to relocation eventually. Keeping it open just in case. (see) |
clean outdated draft PR |
Experimental branch investigating #887 that doesn't work because we can't have
(data ...)
and(elem ...)
offsets in the formsomeConstantGlobal + someConstantValue
. See also this comment. Pinned here for reference, even though it's not possible currently.