A few months ago we announced the start of work on the introduction of Asm.js. Asm.js support was one of the 10 most requested requests for UserVoice for Microsoft Edge , starting from the very launch in December 2014. Since then, we have made good progress: in Windows 10 Insider Preview, starting with build 10074, you can try Asm .js in Chakra and Microsoft Edge.
What is Asm.js?
Asm.js is a strict subset of JavaScript that can be used as a low-level and effective language for the compiler. As a subset, asm.js describes a restricted virtual machine for languages ​​with unsafe memory access like C and C ++. The combination of static and dynamic checks allows JavaScript engines to use techniques like specialized compilation without insurance or AOT compilation (Ahead-of-Time) for correct asm.js code.
')
Such techniques help javascript run with “predictable” and “close to native” performance, both of which are nontrivial to achieve within the framework of conventional compiler optimizations for dynamic languages ​​like javascript.
Given the difficulty of writing asm.js code manually, today asm.js is mainly done through the transcompilation of C / C ++ code using tools such as Emscripten . The result is used in the framework of the web platform together with technologies such as WebGL and Web Audio. Game engines, for example, Unity and Unreal , are beginning to implement early or experimental support for games on the web without the use of plug-ins, using a combination of asm.js and other related technologies.
How can I try with Asm.js in Microsoft Edge?
To enable Asm.js support in Microsoft Edge, go to the about: flags page in Microsoft Edge and enable the “Enable asm.js” flag, as shown below:
Embedding Asm.js in the Chakra code execution flow
The ASM.js validator , which allows Chakra to detect the asm.js code and verify that it complies with the asm.js specification .
Generate optimized type-specific code . Given that asm.js supports only native types (int, double, float, or SIMD values), Chakra uses type information to generate type-specific code based on asm.js code.
Accelerated execution of type-specific code by the Chakra interpreter due to the use of information about types in order to exclude packaging and unpacking of numerical values ​​and by eliminating the need to generate a data profile for interpretation. For non-asm.js code in JavaScript, the Chakra interpreter creates and maintains data profiles for the JIT compiler to enable the generation of highly optimized JIT code. Tipo-specialized bytecode, created on the basis of the correct asm.js code, already contains all the information necessary for the JIT compiler to create optimized machine code. Tipo-specialized interpreter works 4-5 times faster than the interpreter with data profiles.
Accelerated compilation with the Chakra JIT compiler . Asm.js code is usually generated using the LLVM compiler and Emscripten tools . The Chakra JIT compiler takes into account the optimizations already made that are present in the asm.js code generated by the LLVM compiler. For example, our JIT compiler does not pass code embedding, focusing on embedding already done by the LLMV compiler. Eliminating code analysis for such JIT optimizations not only helps save CPU cycles and battery use when generating code, but also significantly increases the speed of the JIT compiler.
Predictable performance due to the exclusion of insurance in the compiled asm.js code. Given the dynamic nature of JavaScript, all modern JavaScript engines support some form of transition to the execution of non-optimized code, called insurance (“bailout” in this case translated as insurance). They work in those moments when the engines understand that the assumptions made by the JIT compiler are no longer valid for the compiled code. Insurance not only affects the overall performance, but also makes the execution time unpredictable. Using the advantages of safe type restrictions for correct asm.js code, the code generated by the Chakra JIT compiler does not require insurance.
JIT compiler optimizations for Asm.js
In addition to the changes to the code execution process described above, the Chakra JIT compiler also takes advantage of the limitations imposed by the asm.js specification. These improvements help the Chakra JIT compiler to generate code that will be executed with near-native performance — a cherished goal for all dynamic language JIT compilers. Let's take a closer look at two such optimizations.
Elimination of helpers and insurance calls
The code of assistants and insurance acts as a powerful lifeline for compiled code of dynamic languages. If any of the assumptions made by the JIT compiler during compilation of the code becomes incorrect, there must be a safe mechanism for correctly continuing the execution of the code.
Helpers can be used to handle unexpected situations during an operation, after which the control can be returned to the JIT code. The insurances handle situations in which the JIT code cannot recover after the occurrence of unpredicted conditions and control needs to be transferred back to the interpreter to perform the rest of the current function.
For example, if a variable appears to be an integer, and the compiler has generated code with this assumption, the appearance of a real number should not affect the correctness. In such cases, the helper or insurance code is used to continue execution even with reduced performance. For asm.js code, Chakra does not generate any additional helpers or insurance code.
To understand why this is not necessary, let's look at an example of a squaring function inside an asm.js module, which has been simplified to explain the concept:
functionPhysicsEngine(global, foreign, heap) { "use asm"; … // Function Declaration function square(x) { x = +x; return +(x*x); } … }
The square function in the example code above translates the two x64-machine instructions into asm.js code, excluding the prologue and epilogue generated for this function.
For comparison, the compiled code generated for the square function, with asm.js disabled, includes about 10 machine instructions. Including:
Check that the parameter is marked as double, if not, then go to the helper that converts from any type to double
Extract real value from double marked
Multiplication of values
Converting the result back to double marked
The Chakra JIT compiler is capable of generating efficient code for asm.js, taking advantage of type information for variables that does not change over the lifetime of the program. The validator and linker Asm.js also take this into account. Since all internal variables in asm.js are of native type (int, double, float, or SIMD values), internal functions calmly use native types without packaging them into JavaScript variables for transfer between functions. In the terminology of the compiler, this is often called direct calls and the exclusion of packaging or transformations when working with data in code in asm.js. In this case, for external calls to or from JavaScript, all incoming variables are converted to native types, and outgoing native types are converted to variables during packaging.
Exclusion of border checking when accessing typed arrays
In the previous post, we talked about how the Chakra JIT compiler implements the optimization of automatically typed arrays , while simultaneously putting the border checks out of the arrays, thus improving the performance of array operations within the loop by up to 40%. Given the type constraints in asm.js, the Chakra JIT compiler completely eliminates checking the boundaries for accessing typed arrays for all compiled asm.js code, regardless of where it is in the code (inside or outside the loop or function) or the type of the typed array. It also excludes bounds checking for persistent and non-persistent indexed queries on typed arrays. Together, these optimizations make it possible to achieve near-native performance when working with typed arrays in asm.js code.
Below is a simplified example of saving in a typed array with a constant shift and one x64-machine instruction generated by the Chakra compiler for the corresponding code in asm.js:
When compiling asm.js code, the Chakra JIT compiler can precompute the final address corresponding to int8ArrayView [4] directly during linking of the asm.js code (the first call to the asm.js module) and therefore generate one instruction corresponding to saving in a typed array. For comparison, compiled code generated for the same operation from non-asm.js JavaScript code results in approximately 10-15 machine instructions, which is quite significant. The operation includes the following steps:
Loading a typed array from a variable in a closure (in asm.js, typed arrays are already captured by a closure)
Check that the variable from the closure is actually a typed array, if not, then the safety code is called
Checking that the index is within the length of the typed array; if the conditions are not met, the transition to the helper code or insurance occurs
Storing value in an array buffer
The checks above are removed from the code in asm.js due to strict restrictions on what can be changed inside the asm.js module. For example, a variable indicating a typed array in the asm.js module does not change. If it changes, then this is no longer a valid code on asm.js, which is caught during the code validation stage. In such cases, the code is processed in the same way as any other JavaScript function.
Improved scripts and performance improvements from Asm.js
Already from the first results that you can try in the fresh build of the preview of Windows 10, we have seen some cool scripts that benefit from the asm.js support in Chakra and Microsoft Edge. If you want to play yourself, run games like Angry Bots , Survival Shooter , Tappy Chicken or try some fun demos asm.js shown here .
If we talk about improvements in performance, there are several asm.js speed meters that are constantly evolving. Already with preliminary support for asm.js, Chakra and Microsoft Edge work more than 300% faster in Unity Benchmark and about 200% faster in individual tests like zlib used in Google Octane and Apple Jet Stream test suites.
Unity Benchmark scores for 64-bit browsers (Click to enlarge)
(System info: Intel Core (TM) i7 CPU 860 @ 2.80GHz (4 cores), 12GB RAM running 64 bit Windows 10 Preview)
(Scores have been scaled so that IE11 = 1.0, to fit a single chart)
What next?
This is the first step towards integrating asm.js into the web platform behind Microsoft Edge. We are pleased with the results, but not all have done. We are working on fine-tuning the chain of code execution in Chakra to support Asm.js — collect data to make sure that the current approach to the architecture works well on real-life usage scenarios for asm.js, analyze and try to improve performance, functionality, support for tools, etc. P. before enabling this option by default.
As soon as it is enabled, asm.js will work not only in Microsoft Edge, but will also be available in Windows applications written in HTML / CSS / JS, as well as in WebView, as it uses the EdgeHTML rendering engine supplied with MicrosoftEdge in Windows 10.
We also want to thank the colleagues from Mozilla, with whom we have worked closely since the beginning of the implementation of asm.js support, and Unity for their support and cooperation in implementing asm.js in Chakra and Microsoft Edge. And a special thank you to all the developers and users of Windows 10 and Microsoft Edge, who left us valuable feedback. Continue in the same spirit! You can email us on Twitter at @MSEdgeDev or via Connect .