WebAssembly Micro Runtime
github.com
github.com
~/git/languages/wasm-micro-runtime$ find . -name '*.[ch]' |xargs wc -l |sort -n
...
472 ./core/iwasm/runtime/vmcore_wasm/wasm-opcode.h
478 ./core/shared-lib/utils/runtime_timer.c
537 ./core/shared-lib/platform/zephyr/bh_thread.c
581 ./core/iwasm/runtime/platform/zephyr/wasm_math.c
590 ./core/shared-lib/mem-alloc/ems/ems_alloc.c
841 ./core/iwasm/lib/native-interface/attr-container.c
893 ./core/iwasm/lib/native/libc/libc_wrapper.c
1220 ./core/iwasm/runtime/vmcore_wasm/wasm-runtime.c
2160 ./core/iwasm/runtime/vmcore_wasm/wasm-interp.c
2874 ./core/iwasm/runtime/vmcore_wasm/wasm-loader.c
24534 total
There is also a C++ VM here that’s ~7700 lines. I haven’t tried either one but it would be interesting to see how they compare!https://github.com/WebAssembly/wabt/tree/master/src/interp
~/git/languages/wabt/src/interp$ wc -l *.cc *.h
1863 binary-reader-interp.cc
3585 interp.cc
642 interp-disassemble.cc
740 interp-trace.cc
43 binary-reader-interp.h
735 interp.h
94 interp-internal.h
7702 total[1] - https://wasi.dev/
However, they could have decided to use WASI as the interface for their wasm runtime, but it looks like they didn't
If you ask me to write a similar README in Japanese/Chinese/Thai, I wouldn't even know where to start.
I'm not sure what the word for that is, it's neither vocabulary nor grammar, and yet more important than either of them -- actually even more important than both vocabulary and grammar put together (lists of words and lists of rules).
For grammar there are barely any useful tools. Even tools like Gramarly are fairly primitive.
There's also the beginnings of a sensor manager and API so clearly small embedded use cases are intended.
It's still a very small project (looks like one contributor so far), but that's no bad thing...
https://github.com/tinygo-org/tinygo/
No garbage collector yet, but it's on the ToDo list and probably not too far off. :)
WASM was made with running on regular desktop CPUs in mind. Wasm is not a truly a state machine, nor stack machine, but a horrible hybrid in between them thanks to design by committee.
Changeable locals, accessible undefineds, use of stack pointer protection technique that kills both pipelining and speculative execution. Yes, that's combined downsides of both stack and register VMs.
There were MCU that were naively running Java long time ago, but all failed on the market. The idea proved non feasible
To be clear, this is also interpreted, so it doesn't have an advantage there.
Not sure they failed for technical reasons, embedded CPUs just got fast enough and typical memory big enough to run a JIT.
Really no value there. The positive connotation of "web native" label is not so positive in the world of dour and severe people which "hardcore embedded developers" are.
Such code would not be able to do I/O directly, but must receive it from outside the sandbox. It could be a pure function implementing a state machine on form `state, outputs = next(state, inputs)`, where state,inputs and outputs are plain data. This structure is amendable to generating testcases, be it via property testing or fuzzing. Or regression tests by capturing a stream of state transitions in serialized form.
There are languages which makes this possible, like Haskell, or maybe even Rust or Ada. However in the embedded world, C is what everyone knows and uses, so there would be a benefit to staying within that ecosystem.
I work in embedded development and the amount of pure logic is really small in our software but I guess that varies with the niche you're in.
The traditional way to simplify QA is to use a Hardware Abstraction Layer in the code, which hardware side-effects happen through. So you have a classic layer cake like:
| App Logic (imperative, stateful) |
| Hardware Abstraction Layer (interface) |
| Input/Output (implementations) |
Then during testing of the software application logic, a mocked implementation of the HAL can be used. When done with due care, this works OK.
In this basic model it is OK to read input and cause outputs "anywhere" in the application logic. That is easy and convenient, but (I argue) that this causes pure logic to be rare. Which is unfortunate, since it would be much easier to test. State also tends to be spread across the code-base, which can hide stateful behavior that is critical to test.In the proposed model, the HAL functionality is split into two distinct parts: input and output drivers. And the application logic does not call the HAL, but gets called with Input and produces a description of Output. So the layer cake kinda tips sideways:
HW input driver > Input (data) > App Logic (pure) > Output (data) > HW output driver
| |
< State <
During test of application logic we have: input generator -> App Logic -> output capture
and can then perform validations on who sequences of Input/Output pairs. And debugging can trivially access whole sequences of State changes.Those familiar with dataflow programming might find this very familiar. Frontend people might see parallels to unidirectional flow of data in reactive UI frameworks like React. Simulation minded people might see that this structure is very amendable to Discrete-Event Simulation.
I have used this model to good effect across many (relatively simple) embedded/IoT systems over the last few years. One thing I really like, is that it makes temporal logic very easy. Because in this model the current time (be it ticks or wall-time) is just a type of input. So it is easy to stimulate, visualize and make assertions across whole timelines of behavior, and one can see many such timelines at the same time. Similar to what Brett Victor showed with Mario in Inventing on Principle.
Have been meaning to write more about this for a long time, so apologies for the mini blogpost :) Some very related writing in http://www.jonnor.com/2017/03/host-based-simulation-for-embe...
PS: the comment by pault is to the point, and right on the money.
I would like to read a blog post about what you're doing. It would also be great to connect it to the other software communities doing similar things with different names.
I think this encountered these ideas in the Java world around 2005, with their strong focus on testing. It's basically "dependency injection" or dependency inversion.
I just did some Googling and found this good overview:
https://gist.github.com/kbilsted/abdc017858cad68c3e7926b0364...
It's very much an idea in the "enterprise" software world. I've never been in that world but it does seem like they are grappling with complex problems and systems, and this architecture has proven itsef in that domain. It's not surprising to me that it's also useful in the embedded domain.
I would say it's just "modularity". If your dependencies are hard-coded, then you have no modularity in your software at all. The whole thing is one big ball of mud which you can either take or leave. "Functions" aren't really modular if they have nontrivial hard-coded dependencies! (i.e. particularly ones that do I/O or depend on state).
Other names:
- capability based security / Object capabilities (WASM is influenced by these ideas, which originated from Eros OS as far as I know. The E language was an influential object capability language.) The idea of "ambient authority" is useful.
- https://news.ycombinator.com/item?id=14523728 -- a thread where Go programmers are rediscovering dependency injection. You can "invert" state or I/O. Those are independent decisions, but the same concept. In my larger programs, I tend to abstract both of them.
- In Haskell, you use State and IO monads. They are parameters, not things you go "looking for".
- Good overview of enterprise world: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-a... (Clean architecture, ports and adapters, onion architecture, hexagonal architecture, functional core / imperative shell, etc.)
I think Java eventually went overboard with "DI frameworks", which I never used. I just wire up the dependencies manually in main() and it works fine.
-----
Also, I bought a somewhat obscure many years about dataflow programming and hardware, which I think it somewhat related:
https://www.amazon.com/Programming-SIMPL-Way-John-Collins/dp...
At its most fundamental, SIMPL is a set of library functions which allow the passing of encapsulated messages between cooperating processes. These processes may be local to one host computer or spread over a network.
Unless this problem is resolved I don't think WASM can be adopt widely in the world of microcontrollers.
Having minimum 64KB of WASM memory is still a huge cost for mainstream microcontrollers. Most of dynamic memory stuff in embedded system requires at most ~10KB (and often less than 1KB) of memory. We are just throwing away the rest of the precious memory.
Could you tell me a bit more on it? Does the particular number 64kB have something to do with?
> WASM is already the LLVM official backend target
This implies that the only thing LLVM compiles to is WASM.
Better would be something like "WASM is already an official LLVM backend target" or "LLVM already compiles to WASM".
I really prefer to have documentation written by people who understand how things actually work on the inside.
Do they actually need native English speakers? I'd assume they'd just need people proficient in English :)
“Features
WASM interpreter (AOT is planned)”
You’ll compile a program to have it interpreted at the end.
Does anybody know some specific case where this direction is advantageous?
It's usually faster than executing directly over some sort of AST, and also fast when you re-execute the same script over again (that's why Python creates .pyc files for modules).
WASM already is the intermediate format and VM that you describe.
Very slow memory. Decades ago, a lot of interpreters did the same trick internally:
1. tokenise code, 2. convert to some compact binary serialisation format used internally by the rest of interpreter.
The trick was to make interpreter code to remain in CPU cache as long as possible while the code being interpreted did not require fetching from RAM
https://www.destroyallsoftware.com/talks/the-birth-and-death...
Bernhardt, Gary – The Birth & Death of JavaScript (PyCon 2014)