WASM commandline compiler

The Webassembly (wasm) command-line compiler is ready for an alpha flight and we will be testing it over the weekend. Once we see that it passes our test harness and safety scripts -it will be available to customers to play with.

I was surprised at how much survived from the system namespace. The most obvious loss is TQTXJSONObject which affects TQTXComponent and widget serialization at runtime.

The commandline compiler will be available to our customers shortly

Since our RTL does not use this feature extensively and it only affect Ragnarok messages, I doubt you will notice that it’s not there. All TQTXComponent based classes (which includes TQTXWidget’s) can serialize to/from JSON, but we have not implemented it in each widget yet to avoid the overhead. Eventually we will add it, which opens for loading and saving of DFM’s at runtime – but it’s hardly a pressing matter.

What to expect

Webassembly is closer to native code (machine code) in nature than it is Javascript. So where ordinary QTX has soft edges and a forgiving natire -webassembly is more like Delphi or C/C++ with little middle ground.

Under Webassembly the datatypes are real. Under ordinary QTX the datatypes such as Int32, Int64 etc all resolve to a standard Javascript variable. The datatypes are there to help the compiler resolve ambiguity and to initialize the variable with the correct default value. They are also there to help you work with familiar type names.

Webassembly does not allow for such softness, so if you define an Int32, you get an int32.

No variants

Webassembly lacks the variant datatype. This is used heavily under QTX’s DOM and Node namespaces to carry object references. So there is no variant datatype available at all since that would mean reverse engineering the entire multi-type infrastructure.

A large number of units from the system namespace have been ported

While it’s definitively doable it will just result in bloated code. Every method needed to work with the datatype would have to be linked in, and the compiler would add extra calls to these whenever you touch a field or variable of that type.

We might add variant support later, but right now it sort of defeats the purpose of Webassembly modules (imho).

No generics

QTX does not support generics in the conventional sense, except for promise objects. Eventually we will add full generics support for both JS and WASM, but its not a high priority since array objects does a great job of being collections. There is much more to generics than just typed collections of course, but the immediate benefits are covered by arrays (all arrays are objects under QTX, which has the same methods as a TObjectList<T> under Delphi or Lazarus).

The one place we do have generics, namely for JPromise<T>, did not make the transition. This binds directly to the JS codegen and would need heavy refactoring.

Promise objects makes little sense under Webassembly anyways, as it is an async control mechanism. Webassembly is blocking, just like native languages. It could be useful when calling out from Webassembly into the DOM or Node, but that will have to wait.

How to approach it

In our view Webassembly is best approached as libraries; like writing a DLL and isolating methods that would otherwise take a long time or be very slow in ordinary JS.

On top of my head the following are perfect candidates for webassembly:

  • Hashing large chunks of data
  • Compression and decompression
  • Encryption and decryption
  • Graphics processing such as filters and effects
  • Audio processing
  • Pre calculation

We could have gone completely overboard with this and allowed Webassembly classes to be exposed to the host, like Microsoft has done. But the cost would be absurd. It would mean that the code generator would have to emit a fake class, a proxy, that calls into wasm space in every method – again leading to massive bloat and binaries.

If we ever make such a mapping we would try to make exported classes purely external, just like we do for the DOM and NodeJS today. But that will have to wait.

Why Webassembly exists

Javascript was originally purely interpreted and very slow. Eventually it became JIT compiled (just in time compilation) which translate as much of a script as possible to real machine code. However, due to the dynamic nature of Javascript it is impossible to pre-compile an entire script.

So most Javascript runs in chunks, where some parts are pre-compiled and others are interpreted. This is why writing fast Javascript can be very difficult. Everything hinges on what datatypes you use, how you work with values. and what references you pull in and when.

The only way to solve this was to introduce a runtime with the same rules as native languages: strongly typed, linear memory, a proper stack -and a fixed and standard instruction set.

Since raw Webassembly is very low-level stuff it’s not for everyone (you wont have to, we have done it for you!). Writing raw Webassembly is like writing x64 or ARM assembly code (more closer to ARM than x64 to be honest).

Your code have to allocate and manage the stack from the same memory segment as the code, if you want classes and objects you have to implement your own stack-page logic just like a native compiler, and you likewise have to manage memory use and keep track of available segments. So it’s pretty dense stuff.

The result though, is that the entire wasm file can be converted to real machine code in a single swoop by the host! There is no mixed bag of native and interpreted code chunks, and that means the code runs very close to native speeds (performance is roughly 90% of stock C/C++).

The only speed penalty is when you call out; out of the wasm space and into the DOM or NodeJS. This is why I am trying to keep such calls at a bare minimum.

As of writing we only have two such cases:

  • Writeln() maps to console.log()
  • SetTimeOut() binds to the same function in the DOM or NodeJS. This function is used extensively in TQTXDispatch for scheduling future execution and is extremely useful

Everything else has been painstakingly re-implemented under Webassembly. Functions like Now() which returns a TDateTime compatible with Delphi and Freepascal is a pure implementation, it does not rely on the JS RTL or the host at all.

Again, we could have gone all in here and mapped up every class and RTL method under the sun, but the price would be bloat.

Published by Jon Lennart Aasenden

Lead developer for Quartex Pascal

Leave a Reply