skip to content
==> src/fuse.c <==
1/* One pass: mark branch targets, fuse pairs, compact, remap. */
2#include "wasm/fuse.h"
3
4#include "wasm/opcodes.h"
5
6#include <stdlib.h>
7#include <string.h>
8
9bool wasm_fusible(uint16_t op) {
10    if (op >= WASM_INS_BR) return false;
11    if (op == WASM_OP_I32_CONST || op == WASM_OP_I64_CONST || op == WASM_OP_F32_CONST || op == WASM_OP_F64_CONST) return false;
12    if (op == WASM_OP_MEMORY_SIZE || op == WASM_OP_MEMORY_GROW) return false;
13    uint32_t id = op >= 0x100 ? WASM_OP_FC(op - 0x100u) : op;
14    wasm_opinfo info;
15    if (!wasm_op_info(id, &info) || !info.sig) return false;
16    /* Something must be popped for an operand to be fused in. */
17    return info.sig[0] != '>';
18}
19
20static bool is_mem(uint16_t op) { return op >= 0x28 && op <= 0x3e; }
21static bool is_const(uint16_t op) {
22    return op == WASM_OP_I32_CONST || op == WASM_OP_I64_CONST || op == WASM_OP_F32_CONST || op == WASM_OP_F64_CONST;
23}
24static bool has_target(uint16_t op) {
25    return op == WASM_INS_BR || op == WASM_INS_BR_IF || op == WASM_INS_BR_UNLESS || op == WASM_INS_JUMP;
26}
27
28bool wasm_fuse(wasm_fn_code *c) {
29    uint32_t n = c->ncode;
30    bool *target = calloc(n + 1, sizeof *target);
31    uint32_t *map = malloc((n + 1) * sizeof *map);
32    wasm_ins *out = malloc((n ? n : 1) * sizeof *out);
33    if (!target || !map || !out) {
34        free(target);
35        free(map);
36        free(out);
37        return false;
38    }
39    for (uint32_t k = 0; k < n; k++)
40        if (has_target(c->code[k].op)) target[c->code[k].a] = true;
41    for (uint32_t k = 0; k < c->nbrs; k++) target[c->brs[k].target] = true;
42    uint32_t m = 0;
43    for (uint32_t k = 0; k < n;) {
44        wasm_ins x = c->code[k];
45        map[k] = m;
46        if (k + 1 < n && !target[k + 1]) {
47            wasm_ins y = c->code[k + 1];
48            if (x.op == WASM_OP_LOCAL_GET && wasm_fusible(y.op)) {
49                /* The plain op keeps its own b (a load's or store's offset); a is the local. */
50                out[m++] = (wasm_ins){(uint16_t)(WASM_INS_L + y.op), 0, x.a, y.b};
51                map[k + 1] = m - 1;
52                k += 2;
53                continue;
54            }
55            if (is_const(x.op) && wasm_fusible(y.op)) {
56                if (!is_mem(y.op)) {
57                    out[m++] = (wasm_ins){(uint16_t)(WASM_INS_I + y.op), 0, 0, x.b};
58                    map[k + 1] = m - 1;
59                    k += 2;
60                    continue;
61                }
62                /* A load's address or a store's value: only when it fits a (i32.const). */
63                if (x.op == WASM_OP_I32_CONST) {
64                    out[m++] = (wasm_ins){(uint16_t)(WASM_INS_I + y.op), 0, (uint32_t)x.b, y.b};
65                    map[k + 1] = m - 1;
66                    k += 2;
67                    continue;
68                }
69            }
70            if (x.op == WASM_OP_LOCAL_GET && y.op == WASM_OP_LOCAL_GET) {
71                out[m++] = (wasm_ins){WASM_INS_GET2, 0, x.a, y.a};
72                map[k + 1] = m - 1;
73                k += 2;
74                continue;
75            }
76        }
77        out[m++] = x;
78        k++;
79    }
80    map[n] = m;
81    for (uint32_t k = 0; k < m; k++)
82        if (has_target(out[k].op)) out[k].a = map[out[k].a];
83    for (uint32_t k = 0; k < c->nbrs; k++) c->brs[k].target = map[c->brs[k].target];
84    free(c->code);
85    c->code = out;
86    c->ncode = m;
87    free(target);
88    free(map);
89    return true;
90}