==> 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}