asm.js: translate C into JavaScript

0
545

In recent years we have witnessed a progressive convergence of technologies used in the web and those traditionally linked to the development of applications at a lower level.

If on the one hand there are frameworks that allow you to develop desktop applications using only web technologies (eg Electron ), there are also projects in the opposite direction: use technologies and languages traditionally adopted in the desktop for the development of web applications.

A turning point in this direction came with the advent of asm.js, a project developed by the Mozilla Foundation, created with the aim of defining a language compatible with JavaScript and usable as a compilation target. asm.js is not, in fact, a language in itself, but more than anything else a subset of JavaScript that can guarantee full compatibility with strongly typed languages. Therefore, the asm.js code can be executed by any JavaScript interpreter, whether it is an integrated part of a web browser, or in itself (for example Node.js ).

Thanks to the specification of asm.js and the existence of appropriate compilers, it is possible to compile source code written in a “traditional” programming language (for example, C, C ++, Ruby, Python, etc.) “translating it” into a form executable by a JavaScript interpreter.

Fill in asm.js

Emscripten is an LLVM compiler capable of translating C / C ++ code into asm.js. In particular, Emscripten is a backend for LLVM that can translate code in asm.js. This allows you to keep the front end unchanged (in the case of C / C ++ the famous clang ), thus benefiting from all the optimizations already made by the same on the code.

Recall, in fact, that the compilation process is essentially divided into two parts: in the first, the front end translates and optimizes the source code in a language independent of the source and the target platform called ” intermediate representation ” (IR, Intermediate Representation) . In the second, the back end translates the IR code into a target code, for example, machine code x86, or, as in the case of Emscripten, code asm.js.

The particular architecture of LLVM, therefore, makes possible great flexibility, being able to “combine” any front end with any available back end. Among the languages currently compiled in asm.js we mention (in addition to C and C ++) also Rust , Lua , Perl , Python and Ruby.

Features

As mentioned earlier, asm.js guarantees full compatibility with strongly typed languages. This means that, unlike JavaScript, it is necessary to ensure that each expression is evaluated using the types defined at compile time. asm.js uses some additional operations to “force” the type inference process performed by the JavaScript interpreter.

For example, the code in C language:

int f(int i) {
return i + 1;
}

is translated to asm.js in:

function f(i) {
i = i|0;
return (i + 1)|0;
}

As you can easily see, the expressions that work on whole types are translated by adding the ” | 0″ (OR bitwise with zero) instruction in the queue. It must be remembered that in JavaScript the numerical variables are by default floating point. The addition of the statement | 0 ensures that the JavaScript interpreter calculates the expression using whole variables since in JavaScript the bitwise operators convert their operands to integers automatically.

Although it may seem like a forcing, it is actually an elegant expedient, which allows you to overcome the “limitations” of JavaScript while keeping the code perfectly compatible with existing interpreters.

Applications

The potential offered by such technology is innumerable: first, it has made it possible to port existing libraries and applications simply by recompiling them. Recompilation is an automatic, repeatable process, and definitely less expensive than a rewrite. This led to the creation of some interesting projects with the aim of bringing desktop applications to the browser.

Among the famous examples, we mention the Unity3D and Unreal Engine game engines, the DOSbox emulator, the AutoCAD software in its web version, the Qt framework and much more.

The advantages offered by this process are many. First, think about portability: an application compiled in asm.js can run on any browser that supports JavaScript, regardless of whether it runs on Windows, Linux, MacOS, iOS, Android, etc. without it having to be modified or simply recompiled for the various platforms. In addition, you benefit from increased security, as applications run in the context of a web browser’s sandbox.

In other words, the level of abstraction offered by the browser is very high and there are many scenarios in which this is an undoubted advantage.

Performance

Unfortunately, all that glitters is not all gold. As is understandable, there is some overhead introduced by the JavaScript interpreter. Although there are very efficient JavaScript interpreters, the overhead introduced by asm.js is about 1.5 to 2 times. This means that an application compiled in asm.js will be from one and a half times to two times slower than the same compiled natively.

Beyond asm.js: WebAssembly

To overcome the limitations of asm.js was born a new standard, WebAssembly (in short, Wasm), which unlike asm.js, is not based on JavaScript but is in effect a bytecode format. Therefore, it requires browser-specific support in order to be executed. To date, most modern browsers support Wasm natively, both on a desktop and on mobile, and the Emscripten compiler has been updated to support Wasm as an alternative to asm.js.

The advantages of Wasm compared to asm.js are easily understood. Firstly, it is significantly faster, because the language specification is not constrained by the characteristics of JavaScript. In particular, Wasm supports 64-bit integers, load and store instructions, SIMD and much more.

One might ask why a new specification has been created instead of using an existing one, such as the aforementioned IR of LLVM. The reason for this choice must be sought in the needs that led to the birth of Wasm:

  • portability, ie independence from the underlying hardware and software platform;
  • stability: the specification should not change rapidly over time, as this would create an incompatibility with previous versions of browsers by creating fragmentation;
  • encoding size: the Wasm code should be transmitted over the Internet and therefore must be as compact as possible;
  • decoding and compiling speed: the compilation of Wasm in native code is charged to the browser. To limit application start-up times, this phase must be fast;

For example, in the development of a compiler, and therefore of its IR, we focus more on the quality of the product code rather than on the time of compilation since this operation is carried out only by the developers. In the case of Wasm instead, the compilation is an integral part of the “loading” phase of the application. That’s why the existing IRs do not lend themselves to the uses for which Wasm was conceived.

WebAssembly, therefore, aspires to become a standard and multiplatform technology for the development of high-performance client-side applications, while ensuring high levels of security (for example, thanks to the use of sandboxing). As indicated by the same authors of the specification, WebAssembly will not replace JavaScript but will support it in all those circumstances where you need more performance or integration with libraries and technologies developed with other languages.

LEAVE A REPLY

Please enter your comment!
Please enter your name here