porffor 0.0.0-425ea20 → 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 +37 -16
- 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 +806 -328
- package/compiler/decompile.js +19 -3
- package/compiler/embedding.js +9 -5
- package/compiler/index.js +48 -6
- package/compiler/opt.js +331 -275
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +170 -30
- 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 -40
- package/runner/info.js +37 -2
- package/runner/repl.js +1 -1
- package/runner/results.json +1 -0
- package/runner/transform.js +2 -1
- package/tmp.c +61 -0
- package/t.js +0 -31
package/compiler/opt.js
CHANGED
@@ -16,7 +16,7 @@ const performWasmOp = (op, a, b) => {
|
|
16
16
|
}
|
17
17
|
};
|
18
18
|
|
19
|
-
export default (funcs, globals) => {
|
19
|
+
export default (funcs, globals, pages) => {
|
20
20
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
21
21
|
if (optLevel === 0) return;
|
22
22
|
|
@@ -104,362 +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]]++;
|
118
136
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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;
|
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
|
+
}
|
144
157
|
}
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
165
|
+
|
166
|
+
if (optLog) log('opt', `removed unneeded block in for loop`);
|
148
167
|
}
|
149
168
|
}
|
150
169
|
|
151
|
-
if (
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
+
}
|
155
187
|
|
156
|
-
|
188
|
+
wasm.splice(i - 3, 4 + j - i); // remove cond and this if
|
189
|
+
i -= 4;
|
190
|
+
inst = wasm[i];
|
157
191
|
|
158
|
-
|
192
|
+
if (optLog) log('opt', `removed unneeded typeswitch check`);
|
193
|
+
}
|
159
194
|
}
|
160
|
-
}
|
161
195
|
|
162
|
-
|
163
|
-
|
196
|
+
if (inst[0] === Opcodes.end && typeof inst[1] === 'string') {
|
197
|
+
// remove unneeded entire typeswitch
|
164
198
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
+
}
|
171
209
|
|
172
|
-
|
173
|
-
|
210
|
+
if (checks === 0) {
|
211
|
+
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
212
|
+
wasm.splice(i - 1, 1); // remove this inst
|
174
213
|
|
175
|
-
|
176
|
-
i--;
|
177
|
-
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
178
|
-
continue;
|
179
|
-
}
|
214
|
+
if (optLog) log('opt', `removed unneeded entire typeswitch`);
|
180
215
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
// -->
|
186
|
-
//
|
216
|
+
i--;
|
217
|
+
continue;
|
218
|
+
}
|
219
|
+
}
|
187
220
|
|
188
|
-
|
221
|
+
if (i < 1) continue;
|
222
|
+
let lastInst = wasm[i - 1];
|
189
223
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
194
230
|
|
195
|
-
|
196
|
-
|
197
|
-
// local.tee 0
|
198
|
-
// drop
|
199
|
-
// -->
|
200
|
-
// local.set 0
|
231
|
+
lastInst[0] = Opcodes.local_tee; // replace last inst opcode (set -> tee)
|
232
|
+
wasm.splice(i, 1); // remove this inst (get)
|
201
233
|
|
202
|
-
|
234
|
+
getCount[inst[1]]--;
|
235
|
+
i--;
|
236
|
+
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
237
|
+
continue;
|
238
|
+
}
|
203
239
|
|
204
|
-
lastInst[0]
|
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
|
+
//
|
205
246
|
|
206
|
-
|
207
|
-
i--;
|
208
|
-
continue;
|
209
|
-
}
|
247
|
+
getCount[lastInst[1]]--;
|
210
248
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
// -->
|
216
|
-
// <nothing>
|
249
|
+
wasm.splice(i - 1, 2); // remove this inst and last
|
250
|
+
i -= 2;
|
251
|
+
continue;
|
252
|
+
}
|
217
253
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
222
260
|
|
223
|
-
|
224
|
-
// replace const 0, eq -> eqz
|
225
|
-
// i32.const 0
|
226
|
-
// i32.eq
|
227
|
-
// -->
|
228
|
-
// i32.eqz
|
229
|
-
|
230
|
-
inst[0] = Opcodes.eqz[0][0]; // eq -> eqz
|
231
|
-
wasm.splice(i - 1, 1); // remove const 0
|
232
|
-
i--;
|
233
|
-
continue;
|
234
|
-
}
|
261
|
+
getCount[lastInst[1]]--;
|
235
262
|
|
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
|
-
}
|
263
|
+
lastInst[0] = Opcodes.local_set; // change last op
|
248
264
|
|
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
|
-
}
|
265
|
+
wasm.splice(i, 1); // remove this inst
|
266
|
+
i--;
|
267
|
+
continue;
|
268
|
+
}
|
261
269
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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>
|
268
276
|
|
269
|
-
|
277
|
+
wasm.splice(i - 1, 2); // remove these inst
|
278
|
+
i -= 2;
|
279
|
+
continue;
|
280
|
+
}
|
270
281
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
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
|
276
288
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
// f64.const 0
|
289
|
+
inst[0] = Opcodes.eqz[0][0]; // eq -> eqz
|
290
|
+
wasm.splice(i - 1, 1); // remove const 0
|
291
|
+
i--;
|
292
|
+
continue;
|
293
|
+
}
|
283
294
|
|
284
|
-
|
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>
|
285
301
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
+
}
|
307
|
+
|
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>
|
291
314
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
// return_call X
|
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
|
+
}
|
298
320
|
|
299
|
-
lastInst[0]
|
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
|
300
327
|
|
301
|
-
|
302
|
-
i--;
|
303
|
-
if (optLog) log('opt', `tail called return, call`);
|
304
|
-
continue;
|
305
|
-
}
|
328
|
+
wasm[i - 1] = number((valtype === 'f64' ? read_ieee754_binary64 : read_signedLEB128)(lastInst.slice(1)), Valtype.i32)[0]; // f64.const -> i32.const
|
306
329
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
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
|
+
}
|
335
|
+
|
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
|
319
342
|
|
320
|
-
|
321
|
-
const lastLastInst = wasm[i - 2];
|
343
|
+
wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
|
322
344
|
|
323
|
-
|
324
|
-
|
325
|
-
|
345
|
+
wasm.splice(i, 1); // remove this inst
|
346
|
+
i--;
|
347
|
+
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
348
|
+
continue;
|
349
|
+
}
|
350
|
+
|
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
|
357
|
+
|
358
|
+
lastInst[0] = Opcodes.return_call; // change last inst return -> return_call
|
359
|
+
|
360
|
+
wasm.splice(i, 1); // remove this inst (return)
|
361
|
+
i--;
|
362
|
+
if (optLog) log('opt', `tail called return, call`);
|
363
|
+
continue;
|
364
|
+
}
|
365
|
+
|
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
|
372
|
+
|
373
|
+
wasm.splice(i, 1); // remove this inst (return)
|
374
|
+
i--;
|
375
|
+
// if (optLog) log('opt', `removed redundant return at end`);
|
376
|
+
continue;
|
377
|
+
}
|
378
|
+
|
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)) {
|
326
381
|
// local.get 1
|
327
382
|
// local.get 1
|
328
383
|
// -->
|
329
384
|
// local.get 1
|
330
385
|
|
331
386
|
// remove drop at the end as well
|
332
|
-
if (wasm[i + 4][0] === Opcodes.drop)
|
333
|
-
wasm.splice(i + 4, 1);
|
334
|
-
}
|
387
|
+
if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
|
335
388
|
|
336
389
|
wasm.splice(i, 1); // remove this inst (second get)
|
337
390
|
i--;
|
338
391
|
continue;
|
339
392
|
}
|
340
|
-
}
|
341
393
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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
|
+
}
|
353
409
|
}
|
354
|
-
}
|
355
410
|
|
356
|
-
|
411
|
+
if (optLevel < 2) continue;
|
357
412
|
|
358
|
-
|
413
|
+
if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
359
414
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
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})`);
|
364
419
|
|
365
|
-
|
366
|
-
|
367
|
-
|
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;
|
368
423
|
|
369
|
-
|
370
|
-
|
424
|
+
const inst = wasm[i];
|
425
|
+
const lastInst = wasm[i - 1];
|
371
426
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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>
|
377
432
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
+
}
|
383
438
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
439
|
+
if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
|
440
|
+
// local.tee N
|
441
|
+
// -->
|
442
|
+
// <nothing>
|
388
443
|
|
389
|
-
|
390
|
-
|
444
|
+
wasm.splice(i, 1); // remove inst
|
445
|
+
i--;
|
391
446
|
|
392
|
-
|
393
|
-
|
394
|
-
|
447
|
+
const localName = Object.keys(f.locals)[inst[1]];
|
448
|
+
const removedIdx = f.locals[localName].idx;
|
449
|
+
delete f.locals[localName]; // remove from locals
|
395
450
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
+
}
|
401
456
|
|
402
|
-
|
403
|
-
|
404
|
-
|
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
|
+
}
|
405
460
|
|
406
|
-
|
407
|
-
|
461
|
+
unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
|
462
|
+
unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
|
408
463
|
|
409
|
-
|
464
|
+
if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
465
|
+
}
|
410
466
|
}
|
411
|
-
}
|
412
467
|
|
413
|
-
|
414
|
-
|
468
|
+
const useCount = {};
|
469
|
+
for (const x in f.locals) useCount[f.locals[x].idx] = 0;
|
415
470
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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]]++;
|
421
476
|
|
422
|
-
|
423
|
-
|
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();
|
424
479
|
|
425
|
-
|
426
|
-
|
427
|
-
|
480
|
+
if (i < 2) continue;
|
481
|
+
const lastInst = wasm[i - 1];
|
482
|
+
const lastLastInst = wasm[i - 2];
|
428
483
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
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
|
437
492
|
|
438
|
-
|
439
|
-
|
493
|
+
// does not work with leb encoded
|
494
|
+
if (lastInst.length > 2 || lastLastInst.length > 2) continue;
|
440
495
|
|
441
|
-
|
442
|
-
|
496
|
+
let a = lastLastInst[1];
|
497
|
+
let b = lastInst[1];
|
443
498
|
|
444
|
-
|
445
|
-
|
499
|
+
const val = performWasmOp(inst[0], a, b);
|
500
|
+
if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
446
501
|
|
447
|
-
|
448
|
-
|
502
|
+
wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
|
503
|
+
i -= 2;
|
504
|
+
}
|
449
505
|
}
|
450
|
-
}
|
451
506
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
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
|
+
}
|
459
515
|
}
|
460
|
-
}
|
461
516
|
|
462
|
-
|
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
|
+
}
|
463
519
|
}
|
464
520
|
|
465
521
|
// return funcs;
|