Is the compiler released?
aminya opened this issue · comments
I wanted to try the benchmarks, but I can't find the compiler. Isn't it released yet?
Hi @aminya, the compiler is currently 2 tools:
- JS -> C++ is very much of a prototype, I will polish it and release
- C++ -> JS is the unmodified Cheerp (https://github.com/leaningtech/cheerp-compiler, current master branch, but probably any recent one should work)
For now there is not much to be tried at this stage, did you had any specific idea on what to try this on?
Thanks! I am really excited to try this. There are many projects that could benefit from the potential speed-ups.
This can also be a new backend for AssemblyScript. AssemblyScript compiler has a JS output option, but the output usually has a lot of excess things that are useful for WASM but not JavaScript.
This is what AssemblyScript generates when the --jsfile
is used for the following simple code:
export function for_array64(arr: Float64Array): f64 {
let sum = 0.0
for (let i = 0, l = arr.length; i < l; ++i) {
sum += arr[i]
}
return sum
}
Output with the following scripts
"scripts": {
"jsbuild": "asc assembly/index.ts --jsFile build/index.js -O --noAssert --runtime=stub --noValidate --noExportMemory --disable",
"terser": "terser --config-file ./.terserrc.json --output ./build/index.tersed.js -- build/index.js",
"format.output": "prettier --write build"
},
is:
import { abort } from "env"
var bufferView
var base64ReverseLookup = new Uint8Array(123 /*'z'+1*/)
for (var i = 25; i >= 0; --i) {
base64ReverseLookup[48 + i] = 52 + i // '0-9'
base64ReverseLookup[65 + i] = i // 'A-Z'
base64ReverseLookup[97 + i] = 26 + i // 'a-z'
}
base64ReverseLookup[43] = 62 // '+'
base64ReverseLookup[47] = 63 // '/'
/** @noinline Inlining this function would mean expanding the base64 string 4x times in the source code, which Closure seems to be happy to do. */
function base64DecodeToExistingUint8Array(uint8Array, offset, b64) {
var b1,
b2,
i = 0,
j = offset,
bLength = b64.length,
end = offset + ((bLength * 3) >> 2) - (b64[bLength - 2] == "=") - (b64[bLength - 1] == "=")
for (; i < bLength; i += 4) {
b1 = base64ReverseLookup[b64.charCodeAt(i + 1)]
b2 = base64ReverseLookup[b64.charCodeAt(i + 2)]
uint8Array[j++] = (base64ReverseLookup[b64.charCodeAt(i)] << 2) | (b1 >> 4)
if (j < end) uint8Array[j++] = (b1 << 4) | (b2 >> 2)
if (j < end) uint8Array[j++] = (b2 << 6) | base64ReverseLookup[b64.charCodeAt(i + 3)]
}
}
function initActiveSegments(imports) {
base64DecodeToExistingUint8Array(bufferView, 1036, "PA==")
base64DecodeToExistingUint8Array(bufferView, 1048, "AQAAACQAAABJAG4AZABlAHgAIABvAHUAdAAgAG8AZgAgAHIAYQBuAGcAZQ==")
base64DecodeToExistingUint8Array(bufferView, 1100, "PA==")
base64DecodeToExistingUint8Array(bufferView, 1112, "AQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcw==")
}
function asmFunc(env) {
var buffer = new ArrayBuffer(65536)
var HEAP8 = new Int8Array(buffer)
var HEAP16 = new Int16Array(buffer)
var HEAP32 = new Int32Array(buffer)
var HEAPU8 = new Uint8Array(buffer)
var HEAPU16 = new Uint16Array(buffer)
var HEAPU32 = new Uint32Array(buffer)
var HEAPF32 = new Float32Array(buffer)
var HEAPF64 = new Float64Array(buffer)
var Math_imul = Math.imul
var Math_fround = Math.fround
var Math_abs = Math.abs
var Math_clz32 = Math.clz32
var Math_min = Math.min
var Math_max = Math.max
var Math_floor = Math.floor
var Math_ceil = Math.ceil
var Math_trunc = Math.trunc
var Math_sqrt = Math.sqrt
var abort = env.abort
var nan = NaN
var infinity = Infinity
var $lib_builtins_abort = env.abort
function assembly_index_for_array64($0) {
$0 = $0 | 0
var $1 = 0,
$2 = 0.0,
$3 = 0
$3 = (HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0
for_loop_0: while (1) {
if (($1 | 0) < ($3 | 0)) {
if (((HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0) >>> 0 <= $1 >>> 0) {
$lib_builtins_abort(1056 | 0, 1120 | 0, 1374 | 0, 64 | 0)
abort()
}
$2 = $2 + HEAPF64[((HEAP32[(($0 + 4) | 0) >> 2] + (($1 << 3) | 0)) | 0) >> 3]
$1 = ($1 + 1) | 0
continue for_loop_0
}
break for_loop_0
}
return +$2
}
bufferView = HEAPU8
initActiveSegments(env)
function __wasm_memory_size() {
return (buffer.byteLength / 65536) | 0
}
function __wasm_memory_grow(pagesToAdd) {
pagesToAdd = pagesToAdd | 0
var oldPages = __wasm_memory_size() | 0
var newPages = (oldPages + pagesToAdd) | 0
if (oldPages < newPages && newPages < 65536) {
var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536))
var newHEAP8 = new Int8Array(newBuffer)
newHEAP8.set(HEAP8)
HEAP8 = new Int8Array(newBuffer)
HEAP16 = new Int16Array(newBuffer)
HEAP32 = new Int32Array(newBuffer)
HEAPU8 = new Uint8Array(newBuffer)
HEAPU16 = new Uint16Array(newBuffer)
HEAPU32 = new Uint32Array(newBuffer)
HEAPF32 = new Float32Array(newBuffer)
HEAPF64 = new Float64Array(newBuffer)
buffer = newBuffer
bufferView = HEAPU8
}
return oldPages
}
return {
for_array64: assembly_index_for_array64,
}
}
var retasmFunc = asmFunc({
abort: function () {
throw new Error("abort")
},
abort,
})
export var for_array64 = retasmFunc.for_array64
If I run terser on the output with the following config:
{
"module": true,
"compress": {
"ecma": "2018",
"toplevel": false,
"hoist_vars": false,
"hoist_funs": true,
"pure_getters": true,
"unsafe": true,
"unsafe_arrows": true,
"unsafe_comps": true,
"unsafe_Function": true,
"unsafe_math": true,
"unsafe_symbols": true,
"unsafe_methods": true,
"unsafe_proto": true,
"unsafe_regexp": true,
"unsafe_undefined": true,
"passes": 2
},
"parse": {
"ecma": 2020
},
"mangle": false
}
The output becomes:
function base64DecodeToExistingUint8Array(uint8Array, offset, b64) {
for (
var b1,
b2,
i = 0,
j = offset,
bLength = b64.length,
end = offset + ((3 * bLength) >> 2) - ("=" == b64[bLength - 2]) - ("=" == b64[bLength - 1]);
bLength > i;
i += 4
)
(b1 = base64ReverseLookup[b64.charCodeAt(i + 1)]),
(b2 = base64ReverseLookup[b64.charCodeAt(i + 2)]),
(uint8Array[j++] = (base64ReverseLookup[b64.charCodeAt(i)] << 2) | (b1 >> 4)),
end > j && (uint8Array[j++] = (b1 << 4) | (b2 >> 2)),
end > j && (uint8Array[j++] = (b2 << 6) | base64ReverseLookup[b64.charCodeAt(i + 3)])
}
import { abort } from "env"
for (var bufferView, base64ReverseLookup = new Uint8Array(123), i = 25; i >= 0; --i)
(base64ReverseLookup[48 + i] = 52 + i), (base64ReverseLookup[65 + i] = i), (base64ReverseLookup[97 + i] = 26 + i)
;(base64ReverseLookup[43] = 62), (base64ReverseLookup[47] = 63)
var retasmFunc = ((env) => {
var buffer = new ArrayBuffer(65536),
HEAP32 = (new Int8Array(buffer), new Int16Array(buffer), new Int32Array(buffer)),
HEAPU8 = new Uint8Array(buffer),
HEAPF64 = (new Uint16Array(buffer), new Uint32Array(buffer), new Float32Array(buffer), new Float64Array(buffer)),
abort = env.abort,
$lib_builtins_abort = env.abort
return (
base64DecodeToExistingUint8Array((bufferView = HEAPU8), 1036, "PA=="),
base64DecodeToExistingUint8Array(bufferView, 1048, "AQAAACQAAABJAG4AZABlAHgAIABvAHUAdAAgAG8AZgAgAHIAYQBuAGcAZQ=="),
base64DecodeToExistingUint8Array(bufferView, 1100, "PA=="),
base64DecodeToExistingUint8Array(bufferView, 1112, "AQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcw=="),
{
for_array64($0) {
var $3,
$1 = 0,
$2 = 0
for ($3 = (HEAP32[((8 + ($0 |= 0)) | 0) >> 2] >>> 3) | 0; (0 | $3) > (0 | $1); )
((HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0) >>> 0 > $1 >>> 0 ||
($lib_builtins_abort(1056, 1120, 1374, 64), abort()),
($2 += HEAPF64[((HEAP32[(($0 + 4) | 0) >> 2] + (($1 << 3) | 0)) | 0) >> 3]),
($1 = ($1 + 1) | 0)
return +$2
},
}
)
})({
abort() {
throw Error("abort")
},
abort: abort,
})
export var for_array64 = retasmFunc.for_array64
Backend for AssemblyScript would be very cool, and would allow to exploit the additional type information being present.
This example you posted compiles to either to this:
function __Z11for_array64PN6client12Float64ArrayE(Larr){
var Lcall=-0.,Li$p06=-0.,Lsum$p05=-0.;
Lcall=+Larr.length;
if(!(Lcall>0))return 0;
Lsum$p05=0;
Li$p06=0;
while(1){
Lsum$p05+= +Larr[0+~~Li$p06|0];
Li$p06+=1;
if(Li$p06<Lcall)continue;
break;
}
return Lsum$p05;
}
var for_array64=__Z11for_array64PN6client12Float64ArrayE;
or, without pretty-code to
function f(g ){var c=-0.,a=-0.,b=-0.;c=+g.length;if(!(c>0))return 0;b=0;a=0;while(1){b+= +g[0+~~a|0];a+=1;if(a<c)continue;break;}return b;}var for_array64=f;
Currently every number is mapped to a double in the C++. Once that's fixed it would be even more streamlined.
The output looks very promising! 🚀
The example code here was very simple, but I can imagine how the output would look for more realistic examples.