Skip to content
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

Sharing Linear Memory/Runtime Between Modules #887

Closed
sampaioletti opened this issue Oct 5, 2019 · 5 comments
Closed

Sharing Linear Memory/Runtime Between Modules #887

sampaioletti opened this issue Oct 5, 2019 · 5 comments

Comments

@sampaioletti
Copy link

While evaluating AS as the basis for our new plugin system for indis.io we had been playing around with passing AS types (i.e. string/array/classes/etc) between multiple instances, similar to the question in #606.

To be clear they goal for us differs from some of the other PRs like #278 as we aren't targeting multi-threaded at the moment, we are just trying to make it easier for plugin authors to not have to deal with the idiosyncrasies of wasm.

i have created a fork/branch with one of the implementations we have been looking at. https://github.com/sampaioletti/assemblyscript/tree/shared-rt/examples/shared-rt

In this sample we are generating a blank module with the run time, and exporting it and the rt methods to two other 'plugins' so they can share the allocater/linear mem. Leaving the plugins very lean since the rely on the imported runtime.

It works as expected with the exception of what happens to wasm data sections (memory initializes) as they will overwrite each other as they are imported.

I'm sure some other issues will show up if this is taken further, but the I'm posting it here as its no where near PR ready and was hoping for some feedback before I dig deeper.

  • Is this a feasible/useful pattern for the main AS project
  • What would be the best way to handle the memory initializers (a compiler flag that turns them into runtime allocations/ separation using the --memoryBase flag..etc)

Past this we have looked at some other options but this was our favorite for a first try

Thanks, great project!

@dcodeIO
Copy link
Member

dcodeIO commented Oct 6, 2019

That's an interesting approach. As you noted, there'll most likely be more to it, but I like the idea of making the RT a shared module. Goes into the direction of dynamic linking, though, which there isn't a good story for in Wasm yet without multiple memories.

Wondering whether something like a --noStatic flag to disable the generation of static data would help to get things started. Some of stdlib internals might expect static data when inlining for example. Is also likely to have a runtime cost because everything must be dynamically allocated, refcounted and such, but apart from that would be more convenient than alternatives.

Laying out memory regions for all modules in advance would also work, but I assume that'd be a somewhat painful process. Essentially, the first module would do things normally, with the second using the final value of __heap_base of the first as --memoryBase and so on, very similar to static linking. The shared RT module would then use the final __heap_base of the last module as its own __heap_base so it recognizes all the static data.

A third approach might be to extend the compiler with a mechanism to emit fully relocatable modules, with each load and store being offset by an imported __memory_base or such, and an alternative mechanism to initialize static data, like from u64 values upon initialization. Something like this could be automated by an external dynamic linker I assume, that subsequently instantiates all modules by providing their respective memory base, and finally sets up the RT.

A more general issue I see is that the modules wouldn't share stdlib, so if something of stdlib utilizes a global, that won't be synced between all the modules. Might or might not be a problem, but not sharing stdlib definitely leads to redundant code, like each module having its own implementation of memory.copy, String#substring and such, depending on what is used.

@sampaioletti
Copy link
Author

sampaioletti commented Oct 7, 2019

Thanks for your insight @dcodeIO

I'll work through your ideas and see what I can come up with.

The other patterns we have narrowed down

  • involve the compiler recognizing the context change (or a new AS Type like a 'Extern') and adding in a call to a host function to copy the value over. I'm still working that one over in my mind. Lots of copy, and I haven't figured out a way to do copy on write optimizations to reduce the overhead.
  • Some Pub/Sub or other messaging pattern possibly with a broker (host or another module)

But i kind of prefer this shared runtime pattern if we can work through the details, it fits our plugin system rather nicely.

I'll close this out as you answered my questions, and you can reopen if you would like to keep it active.

@dcodeIO
Copy link
Member

dcodeIO commented Oct 9, 2019

So I've toyed around with relocation support a bit, and figured that a relatively straight-forward solution might be to

  • import __memory_base and __table_base constant globals from the host
  • make all loads, stores and call_indirects relative to those
  • make (data ...) and (elem ...) offsets relative to those (spoiler: this is the culprit)
  • export __memory_size and __table_size constant globals to the host
  • use some JS glue to read the sizes and set up the respective imports prior to instantiation
  • a static linker could utilize and later eliminate these as well by means of precomputing

While such a module can be emitted (offsets are just expressions), Binaryen and Wasm engines do not permit computed offsets on (data ...) and (elem ...), even if those are just someConstantGlobal + someConstantValue. Not sure if that will be supported sooner or later, but without that a working solution will always be a hack. Seems we'll either have to wait for relocation sections to become a thing or make a case for someConstantGlobal + someConstantValue offset support.

@dumblob
Copy link

dumblob commented Apr 24, 2021

While such a module can be emitted (offsets are just expressions), Binaryen and Wasm engines do not permit computed offsets on (data ...) and (elem ...), even if those are just someConstantGlobal + someConstantValue. Not sure if that will be supported sooner or later, but without that a working solution will always be a hack. Seems we'll either have to wait for relocation sections to become a thing or make a case for someConstantGlobal + someConstantValue offset support.

Does anyone happen to know whether relocation sections are a thing already?

@zicklag
Copy link

zicklag commented Nov 27, 2022

It seems that it depends on the tool you are using. From the readme:

These conventions are not part of the WebAssembly standard, and are not required of WebAssembly-consuming implementations to execute WebAssembly code. Tools producing and working with WebAssembly in other ways also need not follow any of these conventions. They exist only to support tools that wish to interoperate with other tools at a higher abstraction level than just WebAssembly itself.

I think some form of WASM linking is supported in lld from the Clang tool suit, but I'm not sure what compilers generate relocatable WASM files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants