emscripten-core / emscripten-core/emscripten

EM_ASM code size and performance regression from fastcomp to Wasm backend

Open
#10,113 5 comments 0 reactions 0 assignees View on GitHub
wontfix
Dominant language
C++
Stars
27.6k
Forks
3.6k
Avg merge
1d 1h
Merged PRs (30d)
105

Description

The implementation of EM_ASM used to be somewhat tight and neat in Wasm fastcomp, with

`a.c`
```c
#include
int main()
{
EM_ASM(console.log('hello ' $0 + $1 + $2), 41, 42, 43);
}
```
producing
`a_fastcomp.js`
```js
var ASM_CONSTS = [function($0, $1, $2) { console.log('hello ' $0 + $1 + $2) }];

function _emscripten_asm_const_iiii(code, a0, a1, a2) {
return ASM_CONSTS[code](a0, a1, a2);
}
```
`a_fastcomp.wast`
```wast
(func $_main (; 16 ;) (result i32)
(local $0 i32)
(local $1 i32)
(local $2 i32)
(local.set $2
(global.get $global$9)
)
(local.set $0
(call $_emscripten_asm_const_iiii
(i32.const 0)
(i32.const 41)
(i32.const 42)
(i32.const 43)
)
)
(return
(i32.const 0)
)
)
```

However Wasm backend now generates:
`a_wasm.js`
```js
var ASM_CONSTS = {
1024: function($0, $1, $2) {console.log('hello ' $0 + $1 + $2)}
};

// Avoid creating a new array
var _readAsmConstArgsArray = [];

function readAsmConstArgs(sigPtr, buf) {
var args = _readAsmConstArgsArray;
args.length = 0;
while (1) {
var ch = HEAPU8[sigPtr++];
if (!ch) return args;
if (ch === 'd'.charCodeAt(0) || ch === 'f'.charCodeAt(0)) {
buf = alignMemory(buf, 8);
args.push(HEAPF64[(buf >> 3)]);
buf += 8;
} else if (ch === 'i'.charCodeAt(0)) {
buf = alignMemory(buf, 4);
args.push(HEAP32[(buf >> 2)]);
buf += 4;
} else abort("unexpected char in asm const signature " + ch);
}
}

function alignMemory(size, factor) {
if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default
return Math.ceil(size / factor) * factor;
}

function _emscripten_asm_const_iii(code, sigPtr, argbuf) {
var args = readAsmConstArgs(sigPtr, argbuf);
return ASM_CONSTS[code].apply(null, args);
}
```
`a_wasm.wast`
```wast
(func $main (; 8 ;) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(local $7 i32)
(local $8 i32)
(local $9 i32)
(local $10 i32)
(local $11 i32)
(local $12 i32)
(local $13 i32)
(local $14 i32)
(local $15 i32)
(local $16 i32)
(local $17 i32)
(local $18 i32)
(local $19 i32)
(local.set $2
(block $label$1 (result i32)
(local.set $3
(i32.const 0)
)
(local.set $4
(i32.const 0)
)
(local.set $5
(i32.const 0)
)
(local.set $6
(i32.const 0)
)
(local.set $7
(i32.const 0)
)
(local.set $8
(i32.const 0)
)
(local.set $9
(i32.const 0)
)
(local.set $10
(i32.const 0)
)
(local.set $11
(i32.const 0)
)
(local.set $12
(i32.const 0)
)
(local.set $13
(i32.const 0)
)
(local.set $14
(i32.const 0)
)
(local.set $15
(i32.const 0)
)
(local.set $16
(i32.const 0)
)
(local.set $17
(i32.const 0)
)
(local.set $18
(i32.const 0)
)
(local.set $19
(i32.const 0)
)
(block $label$2 (result i32)
(local.set $3
(global.get $global$0)
)
(local.set $4
(i32.const 16)
)
(local.set $5
(i32.sub
(local.get $3)
(local.get $4)
)
)
(block $label$3
(if
(i32.lt_u
(local.tee $18
(local.get $5)
)
(global.get $global$2)
)
(call $__handle_stack_overflow)
)
(global.set $global$0
(local.get $18)
)
)
(local.set $6
(i32.const 12)
)
(local.set $7
(i32.add
(local.get $5)
(local.get $6)
)
)
(local.set $8
(local.get $7)
)
(local.set $9
(i32.const 0)
)
(local.set $10
(i32.const 105)
)
(i32.store8 offset=12
(local.get $5)
(local.get $10)
)
(i32.store8 offset=13
(local.get $5)
(local.get $10)
)
(i32.store8 offset=14
(local.get $5)
(local.get $10)
)
(i32.store8 offset=15
(local.get $5)
(local.get $9)
)
(local.set $11
(i32.const 43)
)
(i32.store offset=8
(local.get $5)
(local.get $11)
)
(local.set $12
(i32.const 42)
)
(i32.store offset=4
(local.get $5)
(local.get $12)
)
(local.set $13
(i32.const 41)
)
(i32.store
(local.get $5)
(local.get $13)
)
(local.set $14
(i32.const 1024)
)
(drop
(call $emscripten_asm_const_iii
(local.get $14)
(local.get $8)
(local.get $5)
)
)
(local.set $15
(i32.const 0)
)
(local.set $16
(i32.const 16)
)
(local.set $17
(i32.add
(local.get $5)
(local.get $16)
)
)
(block $label$5
(if
(i32.lt_u
(local.tee $19
(local.get $17)
)
(global.get $global$2)
)
(call $__handle_stack_overflow)
)
(global.set $global$0
(local.get $19)
)
)
(br $label$1
(local.get $15)
)
)
)
)
(return
(local.get $2)
)
)
```
Same kind of issue occurs with multithreaded proxied EM_ASM blocks.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.