porffor 0.0.0-44bc2d8 → 0.0.0-48403fd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -15
- package/c +0 -0
- package/c.exe +0 -0
- package/compiler/2c.js +354 -0
- package/compiler/builtins.js +14 -12
- package/compiler/codeGen.js +859 -332
- package/compiler/decompile.js +21 -5
- package/compiler/embedding.js +9 -5
- package/compiler/encoding.js +4 -2
- package/compiler/index.js +56 -5
- package/compiler/opt.js +337 -251
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +172 -34
- package/compiler/sections.js +46 -5
- package/compiler/wasmSpec.js +3 -0
- package/compiler/wrap.js +11 -5
- package/cool.exe +0 -0
- package/fib.js +10 -0
- package/g +0 -0
- package/g.exe +0 -0
- package/hi.c +37 -0
- package/out +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/r.js +39 -0
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +54 -34
- package/runner/info.js +37 -2
- package/runner/repl.js +4 -11
- package/runner/results.json +1 -0
- package/runner/transform.js +2 -1
- package/runner/version.js +10 -0
- package/tmp.c +61 -0
- package/t.js +0 -31
package/compiler/opt.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Opcodes, Valtype } from "./wasmSpec.js";
|
2
2
|
import { number } from "./embedding.js";
|
3
|
+
import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
|
3
4
|
|
4
5
|
// deno compat
|
5
6
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -15,7 +16,7 @@ const performWasmOp = (op, a, b) => {
|
|
15
16
|
}
|
16
17
|
};
|
17
18
|
|
18
|
-
export default (funcs, globals) => {
|
19
|
+
export default (funcs, globals, pages) => {
|
19
20
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
20
21
|
if (optLevel === 0) return;
|
21
22
|
|
@@ -95,7 +96,6 @@ export default (funcs, globals) => {
|
|
95
96
|
}
|
96
97
|
|
97
98
|
if (t.index > c.index) t.index--; // adjust index if after removed func
|
98
|
-
if (c.memory) t.memory = true;
|
99
99
|
}
|
100
100
|
|
101
101
|
funcs.splice(funcs.indexOf(c), 1); // remove func from funcs
|
@@ -104,332 +104,418 @@ export default (funcs, globals) => {
|
|
104
104
|
|
105
105
|
if (process.argv.includes('-opt-inline-only')) return;
|
106
106
|
|
107
|
+
const pageKeys = [...pages.keys()];
|
108
|
+
const hasString = pageKeys.some(x => x.startsWith('string:'));
|
109
|
+
const hasArray = pageKeys.some(x => x.startsWith('array:'));
|
110
|
+
|
107
111
|
// wasm transform pass
|
108
112
|
for (const f of funcs) {
|
109
113
|
const wasm = f.wasm;
|
110
114
|
|
111
|
-
let
|
115
|
+
let runs = 2; // how many by default? add arg?
|
116
|
+
while (runs > 0) {
|
117
|
+
runs--;
|
112
118
|
|
113
|
-
|
114
|
-
|
115
|
-
getCount
|
116
|
-
|
117
|
-
|
119
|
+
let depth = [];
|
120
|
+
|
121
|
+
let getCount = {}, setCount = {};
|
122
|
+
for (const x in f.locals) {
|
123
|
+
getCount[f.locals[x].idx] = 0;
|
124
|
+
setCount[f.locals[x].idx] = 0;
|
125
|
+
}
|
126
|
+
|
127
|
+
// main pass
|
128
|
+
for (let i = 0; i < wasm.length; i++) {
|
129
|
+
let inst = wasm[i];
|
130
|
+
|
131
|
+
if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) depth.push(inst[0]);
|
132
|
+
if (inst[0] === Opcodes.end) depth.pop();
|
133
|
+
|
134
|
+
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
135
|
+
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
136
|
+
|
137
|
+
if (inst[0] === Opcodes.block) {
|
138
|
+
// remove unneeded blocks (no brs inside)
|
139
|
+
// block
|
140
|
+
// ...
|
141
|
+
// end
|
142
|
+
// -->
|
143
|
+
// ...
|
144
|
+
|
145
|
+
let hasBranch = false, j = i, depth = 0;
|
146
|
+
for (; j < wasm.length; j++) {
|
147
|
+
const op = wasm[j][0];
|
148
|
+
if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) depth++;
|
149
|
+
if (op === Opcodes.end) {
|
150
|
+
depth--;
|
151
|
+
if (depth <= 0) break;
|
152
|
+
}
|
153
|
+
if (op === Opcodes.br || op === Opcodes.br_if) {
|
154
|
+
hasBranch = true;
|
155
|
+
break;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
if (!hasBranch) {
|
160
|
+
wasm.splice(i, 1); // remove this inst (block)
|
161
|
+
i--;
|
162
|
+
inst = wasm[i];
|
163
|
+
|
164
|
+
wasm.splice(j - 1, 1); // remove end of this block
|
118
165
|
|
119
|
-
|
120
|
-
for (let i = 0; i < wasm.length; i++) {
|
121
|
-
let inst = wasm[i];
|
122
|
-
|
123
|
-
if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) depth.push(inst[0]);
|
124
|
-
if (inst[0] === Opcodes.end) depth.pop();
|
125
|
-
|
126
|
-
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
127
|
-
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
128
|
-
|
129
|
-
if (inst[0] === Opcodes.block) {
|
130
|
-
// remove unneeded blocks (no brs inside)
|
131
|
-
// block
|
132
|
-
// ...
|
133
|
-
// end
|
134
|
-
// -->
|
135
|
-
// ...
|
136
|
-
|
137
|
-
let hasBranch = false, j = i, depth = 0;
|
138
|
-
for (; j < wasm.length; j++) {
|
139
|
-
const op = wasm[j][0];
|
140
|
-
if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) depth++;
|
141
|
-
if (op === Opcodes.end) {
|
142
|
-
depth--;
|
143
|
-
if (depth <= 0) break;
|
166
|
+
if (optLog) log('opt', `removed unneeded block in for loop`);
|
144
167
|
}
|
145
|
-
|
146
|
-
|
147
|
-
|
168
|
+
}
|
169
|
+
|
170
|
+
if (inst[0] === Opcodes.if && typeof inst[2] === 'string') {
|
171
|
+
// remove unneeded typeswitch checks
|
172
|
+
|
173
|
+
const type = inst[2].split('|')[1];
|
174
|
+
if (({
|
175
|
+
Array: hasArray,
|
176
|
+
String: hasString
|
177
|
+
})[type] === false) {
|
178
|
+
let j = i, depth = 0;
|
179
|
+
for (; j < wasm.length; j++) {
|
180
|
+
const op = wasm[j][0];
|
181
|
+
if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) depth++;
|
182
|
+
if (op === Opcodes.end) {
|
183
|
+
depth--;
|
184
|
+
if (depth <= 0) break;
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
wasm.splice(i - 3, 4 + j - i); // remove cond and this if
|
189
|
+
i -= 4;
|
190
|
+
inst = wasm[i];
|
191
|
+
|
192
|
+
if (optLog) log('opt', `removed unneeded typeswitch check`);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
if (inst[0] === Opcodes.end && typeof inst[1] === 'string') {
|
197
|
+
// remove unneeded entire typeswitch
|
198
|
+
|
199
|
+
let j = i, depth = -1, checks = 0;
|
200
|
+
for (; j > 0; j--) {
|
201
|
+
const op = wasm[j][0];
|
202
|
+
if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) {
|
203
|
+
depth++;
|
204
|
+
if (depth === 0) break;
|
205
|
+
}
|
206
|
+
if (op === Opcodes.end) depth--;
|
207
|
+
if (wasm[j][2]?.startsWith?.('TYPESWITCH')) checks++;
|
208
|
+
}
|
209
|
+
|
210
|
+
if (checks === 0) {
|
211
|
+
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
212
|
+
wasm.splice(i - 1, 1); // remove this inst
|
213
|
+
|
214
|
+
if (optLog) log('opt', `removed unneeded entire typeswitch`);
|
215
|
+
|
216
|
+
i--;
|
217
|
+
continue;
|
148
218
|
}
|
149
219
|
}
|
150
220
|
|
151
|
-
if (
|
152
|
-
|
221
|
+
if (i < 1) continue;
|
222
|
+
let lastInst = wasm[i - 1];
|
223
|
+
|
224
|
+
if (lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_set && inst[0] === Opcodes.local_get) {
|
225
|
+
// replace set, get -> tee (sets and returns)
|
226
|
+
// local.set 0
|
227
|
+
// local.get 0
|
228
|
+
// -->
|
229
|
+
// local.tee 0
|
230
|
+
|
231
|
+
lastInst[0] = Opcodes.local_tee; // replace last inst opcode (set -> tee)
|
232
|
+
wasm.splice(i, 1); // remove this inst (get)
|
233
|
+
|
234
|
+
getCount[inst[1]]--;
|
153
235
|
i--;
|
154
|
-
|
236
|
+
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
237
|
+
continue;
|
238
|
+
}
|
239
|
+
|
240
|
+
if ((lastInst[0] === Opcodes.local_get || lastInst[0] === Opcodes.global_get) && inst[0] === Opcodes.drop) {
|
241
|
+
// replace get, drop -> nothing
|
242
|
+
// local.get 0
|
243
|
+
// drop
|
244
|
+
// -->
|
245
|
+
//
|
155
246
|
|
156
|
-
|
247
|
+
getCount[lastInst[1]]--;
|
157
248
|
|
158
|
-
|
249
|
+
wasm.splice(i - 1, 2); // remove this inst and last
|
250
|
+
i -= 2;
|
251
|
+
continue;
|
159
252
|
}
|
160
|
-
}
|
161
253
|
|
162
|
-
|
163
|
-
|
254
|
+
if (lastInst[0] === Opcodes.local_tee && inst[0] === Opcodes.drop) {
|
255
|
+
// replace tee, drop -> set
|
256
|
+
// local.tee 0
|
257
|
+
// drop
|
258
|
+
// -->
|
259
|
+
// local.set 0
|
164
260
|
|
165
|
-
|
166
|
-
// replace set, get -> tee (sets and returns)
|
167
|
-
// local.set 0
|
168
|
-
// local.get 0
|
169
|
-
// -->
|
170
|
-
// local.tee 0
|
261
|
+
getCount[lastInst[1]]--;
|
171
262
|
|
172
|
-
|
173
|
-
wasm.splice(i, 1); // remove this inst (get)
|
263
|
+
lastInst[0] = Opcodes.local_set; // change last op
|
174
264
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
}
|
265
|
+
wasm.splice(i, 1); // remove this inst
|
266
|
+
i--;
|
267
|
+
continue;
|
268
|
+
}
|
180
269
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
270
|
+
if ((lastInst[0] === Opcodes.i32_const || lastInst[0] === Opcodes.i64_const || lastInst[0] === Opcodes.f64_const) && inst[0] === Opcodes.drop) {
|
271
|
+
// replace const, drop -> <nothing>
|
272
|
+
// i32.const 0
|
273
|
+
// drop
|
274
|
+
// -->
|
275
|
+
// <nothing>
|
187
276
|
|
188
|
-
|
277
|
+
wasm.splice(i - 1, 2); // remove these inst
|
278
|
+
i -= 2;
|
279
|
+
continue;
|
280
|
+
}
|
189
281
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
282
|
+
if (inst[0] === Opcodes.eq && lastInst[0] === Opcodes.const && lastInst[1] === 0 && valtype !== 'f64') {
|
283
|
+
// replace const 0, eq -> eqz
|
284
|
+
// i32.const 0
|
285
|
+
// i32.eq
|
286
|
+
// -->
|
287
|
+
// i32.eqz
|
288
|
+
|
289
|
+
inst[0] = Opcodes.eqz[0][0]; // eq -> eqz
|
290
|
+
wasm.splice(i - 1, 1); // remove const 0
|
291
|
+
i--;
|
292
|
+
continue;
|
293
|
+
}
|
294
|
+
|
295
|
+
if (inst[0] === Opcodes.i32_wrap_i64 && (lastInst[0] === Opcodes.i64_extend_i32_s || lastInst[0] === Opcodes.i64_extend_i32_u)) {
|
296
|
+
// remove unneeded i32 -> i64 -> i32
|
297
|
+
// i64.extend_i32_s
|
298
|
+
// i32.wrap_i64
|
299
|
+
// -->
|
300
|
+
// <nothing>
|
194
301
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
// local.set 0
|
302
|
+
wasm.splice(i - 1, 2); // remove this inst and last
|
303
|
+
i -= 2;
|
304
|
+
// if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
305
|
+
continue;
|
306
|
+
}
|
201
307
|
|
202
|
-
|
308
|
+
if (inst[0] === Opcodes.i32_trunc_sat_f64_s[0] && (lastInst[0] === Opcodes.f64_convert_i32_u || lastInst[0] === Opcodes.f64_convert_i32_s)) {
|
309
|
+
// remove unneeded i32 -> f64 -> i32
|
310
|
+
// f64.convert_i32_s || f64.convert_i32_u
|
311
|
+
// i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
|
312
|
+
// -->
|
313
|
+
// <nothing>
|
203
314
|
|
204
|
-
|
315
|
+
wasm.splice(i - 1, 2); // remove this inst and last
|
316
|
+
i -= 2;
|
317
|
+
// if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
318
|
+
continue;
|
319
|
+
}
|
205
320
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
321
|
+
if (lastInst[0] === Opcodes.const && (inst === Opcodes.i32_to || inst === Opcodes.i32_to_u)) {
|
322
|
+
// change const and immediate i32 convert to i32 const
|
323
|
+
// f64.const 0
|
324
|
+
// i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
|
325
|
+
// -->
|
326
|
+
// i32.const 0
|
210
327
|
|
211
|
-
|
212
|
-
// replace const, drop -> <nothing>
|
213
|
-
// i32.const 0
|
214
|
-
// drop
|
215
|
-
// -->
|
216
|
-
// <nothing>>
|
328
|
+
wasm[i - 1] = number((valtype === 'f64' ? read_ieee754_binary64 : read_signedLEB128)(lastInst.slice(1)), Valtype.i32)[0]; // f64.const -> i32.const
|
217
329
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
330
|
+
wasm.splice(i, 1); // remove this inst
|
331
|
+
i--;
|
332
|
+
if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
333
|
+
continue;
|
334
|
+
}
|
222
335
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
inst[0] = Opcodes.eqz[0][0]; // eq -> eqz
|
231
|
-
wasm.splice(i - 1, 1); // remove const 0
|
232
|
-
i--;
|
233
|
-
continue;
|
234
|
-
}
|
336
|
+
if (lastInst[0] === Opcodes.i32_const && (inst === Opcodes.i32_from || inst === Opcodes.i32_from_u)) {
|
337
|
+
// change i32 const and immediate convert to const (opposite way of previous)
|
338
|
+
// i32.const 0
|
339
|
+
// f64.convert_i32_s || f64.convert_i32_u
|
340
|
+
// -->
|
341
|
+
// f64.const 0
|
235
342
|
|
236
|
-
|
237
|
-
// remove unneeded i32 -> i64 -> i32
|
238
|
-
// i64.extend_i32_s
|
239
|
-
// i32.wrap_i64
|
240
|
-
// -->
|
241
|
-
// <nothing>
|
242
|
-
|
243
|
-
wasm.splice(i - 1, 2); // remove this inst and last
|
244
|
-
i -= 2;
|
245
|
-
// if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
246
|
-
continue;
|
247
|
-
}
|
343
|
+
wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
|
248
344
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
// <nothing>
|
255
|
-
|
256
|
-
wasm.splice(i - 1, 2); // remove this inst and last
|
257
|
-
i -= 2;
|
258
|
-
// if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
259
|
-
continue;
|
260
|
-
}
|
345
|
+
wasm.splice(i, 1); // remove this inst
|
346
|
+
i--;
|
347
|
+
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
348
|
+
continue;
|
349
|
+
}
|
261
350
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
351
|
+
if (tailCall && lastInst[0] === Opcodes.call && inst[0] === Opcodes.return) {
|
352
|
+
// replace call, return with tail calls (return_call)
|
353
|
+
// call X
|
354
|
+
// return
|
355
|
+
// -->
|
356
|
+
// return_call X
|
268
357
|
|
269
|
-
|
358
|
+
lastInst[0] = Opcodes.return_call; // change last inst return -> return_call
|
270
359
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
360
|
+
wasm.splice(i, 1); // remove this inst (return)
|
361
|
+
i--;
|
362
|
+
if (optLog) log('opt', `tail called return, call`);
|
363
|
+
continue;
|
364
|
+
}
|
276
365
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
wasm.splice(i, 1); // remove this inst (return)
|
285
|
-
i--;
|
286
|
-
// if (optLog) log('opt', `removed redundant return at end`);
|
287
|
-
continue;
|
288
|
-
}
|
366
|
+
if (false && i === wasm.length - 1 && inst[0] === Opcodes.return) {
|
367
|
+
// replace final return, end -> end (wasm has implicit return)
|
368
|
+
// return
|
369
|
+
// end
|
370
|
+
// -->
|
371
|
+
// end
|
289
372
|
|
290
|
-
|
291
|
-
|
373
|
+
wasm.splice(i, 1); // remove this inst (return)
|
374
|
+
i--;
|
375
|
+
// if (optLog) log('opt', `removed redundant return at end`);
|
376
|
+
continue;
|
377
|
+
}
|
292
378
|
|
293
|
-
|
294
|
-
|
295
|
-
if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
|
379
|
+
// remove unneeded before get with update exprs (n++, etc) when value is unused
|
380
|
+
if (i < wasm.length - 4 && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get && wasm[i + 1][0] === Opcodes.const && [Opcodes.add, Opcodes.sub].includes(wasm[i + 2][0]) && wasm[i + 3][0] === Opcodes.local_set && wasm[i + 3][1] === inst[1] && (wasm[i + 4][0] === Opcodes.drop || wasm[i + 4][0] === Opcodes.br)) {
|
296
381
|
// local.get 1
|
297
382
|
// local.get 1
|
298
383
|
// -->
|
299
384
|
// local.get 1
|
300
385
|
|
301
386
|
// remove drop at the end as well
|
302
|
-
if (wasm[i + 4][0] === Opcodes.drop)
|
303
|
-
wasm.splice(i + 4, 1);
|
304
|
-
}
|
387
|
+
if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
|
305
388
|
|
306
389
|
wasm.splice(i, 1); // remove this inst (second get)
|
307
390
|
i--;
|
308
391
|
continue;
|
309
392
|
}
|
310
|
-
}
|
311
393
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
394
|
+
if (i < 2) continue;
|
395
|
+
const lastLastInst = wasm[i - 2];
|
396
|
+
|
397
|
+
if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
|
398
|
+
// local.set x
|
399
|
+
// local.tee y
|
400
|
+
// local.get x
|
401
|
+
// -->
|
402
|
+
// <nothing>
|
403
|
+
|
404
|
+
wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
|
405
|
+
if (optLog) log('opt', `removed redundant inline param local handling`);
|
406
|
+
i -= 3;
|
407
|
+
continue;
|
408
|
+
}
|
323
409
|
}
|
324
|
-
}
|
325
410
|
|
326
|
-
|
411
|
+
if (optLevel < 2) continue;
|
327
412
|
|
328
|
-
|
413
|
+
if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
329
414
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
415
|
+
// remove unneeded var: remove pass
|
416
|
+
// locals only got once. we don't need to worry about sets/else as these are only candidates and we will check for matching set + get insts in wasm
|
417
|
+
let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
|
418
|
+
if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
|
334
419
|
|
335
|
-
|
336
|
-
|
337
|
-
|
420
|
+
// note: disabled for now due to instability
|
421
|
+
if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
|
422
|
+
if (i < 1) continue;
|
338
423
|
|
339
|
-
|
340
|
-
|
424
|
+
const inst = wasm[i];
|
425
|
+
const lastInst = wasm[i - 1];
|
341
426
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
427
|
+
if (lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_set && inst[0] === Opcodes.local_get && unneededCandidates.includes(inst[1])) {
|
428
|
+
// local.set N
|
429
|
+
// local.get N
|
430
|
+
// -->
|
431
|
+
// <nothing>
|
347
432
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
433
|
+
wasm.splice(i - 1, 2); // remove insts
|
434
|
+
i -= 2;
|
435
|
+
delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
|
436
|
+
if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
437
|
+
}
|
353
438
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
439
|
+
if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
|
440
|
+
// local.tee N
|
441
|
+
// -->
|
442
|
+
// <nothing>
|
358
443
|
|
359
|
-
|
360
|
-
|
444
|
+
wasm.splice(i, 1); // remove inst
|
445
|
+
i--;
|
361
446
|
|
362
|
-
|
363
|
-
|
364
|
-
|
447
|
+
const localName = Object.keys(f.locals)[inst[1]];
|
448
|
+
const removedIdx = f.locals[localName].idx;
|
449
|
+
delete f.locals[localName]; // remove from locals
|
365
450
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
451
|
+
// fix locals index for locals after
|
452
|
+
for (const x in f.locals) {
|
453
|
+
const local = f.locals[x];
|
454
|
+
if (local.idx > removedIdx) local.idx--;
|
455
|
+
}
|
371
456
|
|
372
|
-
|
373
|
-
|
374
|
-
|
457
|
+
for (const inst of wasm) {
|
458
|
+
if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) && inst[1] > removedIdx) inst[1]--;
|
459
|
+
}
|
375
460
|
|
376
|
-
|
377
|
-
|
461
|
+
unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
|
462
|
+
unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
|
378
463
|
|
379
|
-
|
464
|
+
if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
465
|
+
}
|
380
466
|
}
|
381
|
-
}
|
382
467
|
|
383
|
-
|
384
|
-
|
468
|
+
const useCount = {};
|
469
|
+
for (const x in f.locals) useCount[f.locals[x].idx] = 0;
|
385
470
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
471
|
+
// final pass
|
472
|
+
depth = [];
|
473
|
+
for (let i = 0; i < wasm.length; i++) {
|
474
|
+
let inst = wasm[i];
|
475
|
+
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) useCount[inst[1]]++;
|
391
476
|
|
392
|
-
|
393
|
-
|
477
|
+
if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) depth.push(inst[0]);
|
478
|
+
if (inst[0] === Opcodes.end) depth.pop();
|
394
479
|
|
395
|
-
|
396
|
-
|
397
|
-
|
480
|
+
if (i < 2) continue;
|
481
|
+
const lastInst = wasm[i - 1];
|
482
|
+
const lastLastInst = wasm[i - 2];
|
398
483
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
484
|
+
// todo: add more math ops
|
485
|
+
if (optLevel >= 3 && (inst[0] === Opcodes.add || inst[0] === Opcodes.sub || inst[0] === Opcodes.mul) && lastLastInst[0] === Opcodes.const && lastInst[0] === Opcodes.const) {
|
486
|
+
// inline const math ops
|
487
|
+
// i32.const a
|
488
|
+
// i32.const b
|
489
|
+
// i32.add
|
490
|
+
// -->
|
491
|
+
// i32.const a + b
|
407
492
|
|
408
|
-
|
409
|
-
|
493
|
+
// does not work with leb encoded
|
494
|
+
if (lastInst.length > 2 || lastLastInst.length > 2) continue;
|
410
495
|
|
411
|
-
|
412
|
-
|
496
|
+
let a = lastLastInst[1];
|
497
|
+
let b = lastInst[1];
|
413
498
|
|
414
|
-
|
415
|
-
|
499
|
+
const val = performWasmOp(inst[0], a, b);
|
500
|
+
if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
416
501
|
|
417
|
-
|
418
|
-
|
502
|
+
wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
|
503
|
+
i -= 2;
|
504
|
+
}
|
419
505
|
}
|
420
|
-
}
|
421
506
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
507
|
+
const localIdxs = Object.values(f.locals).map(x => x.idx);
|
508
|
+
// remove unused locals (cleanup)
|
509
|
+
for (const x in useCount) {
|
510
|
+
if (useCount[x] === 0) {
|
511
|
+
const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
|
512
|
+
if (optLog) log('opt', `removed internal local ${x} (${name})`);
|
513
|
+
delete f.locals[name];
|
514
|
+
}
|
429
515
|
}
|
430
|
-
}
|
431
516
|
|
432
|
-
|
517
|
+
if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
|
518
|
+
}
|
433
519
|
}
|
434
520
|
|
435
521
|
// return funcs;
|