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/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 depth = [];
115
+ let runs = 2; // how many by default? add arg?
116
+ while (runs > 0) {
117
+ runs--;
112
118
 
113
- let getCount = {}, setCount = {};
114
- for (const x in f.locals) {
115
- getCount[f.locals[x].idx] = 0;
116
- setCount[f.locals[x].idx] = 0;
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
- // main pass
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;
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
- if (op === Opcodes.br) {
146
- hasBranch = true;
147
- break;
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 (!hasBranch) {
152
- wasm.splice(i, 1); // remove this inst (block)
153
- i--;
154
- inst = wasm[i];
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
- wasm.splice(j - 1, 1); // remove end of this block
188
+ wasm.splice(i - 3, 4 + j - i); // remove cond and this if
189
+ i -= 4;
190
+ inst = wasm[i];
157
191
 
158
- if (optLog) log('opt', `removed unneeded block in for loop`);
192
+ if (optLog) log('opt', `removed unneeded typeswitch check`);
193
+ }
159
194
  }
160
- }
161
195
 
162
- if (i < 1) continue;
163
- let lastInst = wasm[i - 1];
196
+ if (inst[0] === Opcodes.end && typeof inst[1] === 'string') {
197
+ // remove unneeded entire typeswitch
164
198
 
165
- if (lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_set && inst[0] === Opcodes.local_get) {
166
- // replace set, get -> tee (sets and returns)
167
- // local.set 0
168
- // local.get 0
169
- // -->
170
- // local.tee 0
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
- lastInst[0] = Opcodes.local_tee; // replace last inst opcode (set -> tee)
173
- wasm.splice(i, 1); // remove this inst (get)
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
- getCount[inst[1]]--;
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
- if ((lastInst[0] === Opcodes.local_get || lastInst[0] === Opcodes.global_get) && inst[0] === Opcodes.drop) {
182
- // replace get, drop -> nothing
183
- // local.get 0
184
- // drop
185
- // -->
186
- //
216
+ i--;
217
+ continue;
218
+ }
219
+ }
187
220
 
188
- getCount[lastInst[1]]--;
221
+ if (i < 1) continue;
222
+ let lastInst = wasm[i - 1];
189
223
 
190
- wasm.splice(i - 1, 2); // remove this inst and last
191
- i -= 2;
192
- continue;
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
- if (lastInst[0] === Opcodes.local_tee && inst[0] === Opcodes.drop) {
196
- // replace tee, drop -> set
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
- getCount[lastInst[1]]--;
234
+ getCount[inst[1]]--;
235
+ i--;
236
+ // if (optLog) log('opt', `consolidated set, get -> tee`);
237
+ continue;
238
+ }
203
239
 
204
- lastInst[0] = Opcodes.local_set; // change last op
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
- wasm.splice(i, 1); // remove this inst
207
- i--;
208
- continue;
209
- }
247
+ getCount[lastInst[1]]--;
210
248
 
211
- if ((lastInst[0] === Opcodes.i32_const || lastInst[0] === Opcodes.i64_const || lastInst[0] === Opcodes.f64_const) && inst[0] === Opcodes.drop) {
212
- // replace const, drop -> <nothing>
213
- // i32.const 0
214
- // drop
215
- // -->
216
- // <nothing>
249
+ wasm.splice(i - 1, 2); // remove this inst and last
250
+ i -= 2;
251
+ continue;
252
+ }
217
253
 
218
- wasm.splice(i - 1, 2); // remove these inst
219
- i -= 2;
220
- continue;
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
- if (inst[0] === Opcodes.eq && lastInst[0] === Opcodes.const && lastInst[1] === 0 && valtype !== 'f64') {
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
- if (inst[0] === Opcodes.i32_wrap_i64 && (lastInst[0] === Opcodes.i64_extend_i32_s || lastInst[0] === Opcodes.i64_extend_i32_u)) {
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
- if (inst[0] === Opcodes.i32_trunc_sat_f64_s[0] && (lastInst[0] === Opcodes.f64_convert_i32_u || lastInst[0] === Opcodes.f64_convert_i32_s)) {
250
- // remove unneeded i32 -> f64 -> i32
251
- // f64.convert_i32_s || f64.convert_i32_u
252
- // i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
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
- if (lastInst[0] === Opcodes.const && (inst === Opcodes.i32_to || inst === Opcodes.i32_to_u)) {
263
- // change const and immediate i32 convert to i32 const
264
- // f64.const 0
265
- // i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
266
- // -->
267
- // i32.const 0
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
- wasm[i - 1] = number((valtype === 'f64' ? read_ieee754_binary64 : read_signedLEB128)(lastInst.slice(1)), Valtype.i32)[0]; // f64.const -> i32.const
277
+ wasm.splice(i - 1, 2); // remove these inst
278
+ i -= 2;
279
+ continue;
280
+ }
270
281
 
271
- wasm.splice(i, 1); // remove this inst
272
- i--;
273
- if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
274
- continue;
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
- if (lastInst[0] === Opcodes.i32_const && (inst === Opcodes.i32_from || inst === Opcodes.i32_from_u)) {
278
- // change i32 const and immediate convert to const (opposite way of previous)
279
- // i32.const 0
280
- // f64.convert_i32_s || f64.convert_i32_u
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
- wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
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
- wasm.splice(i, 1); // remove this inst
287
- i--;
288
- if (optLog) log('opt', `converted i32 const -> convert into const`);
289
- continue;
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
- if (tailCall && lastInst[0] === Opcodes.call && inst[0] === Opcodes.return) {
293
- // replace call, return with tail calls (return_call)
294
- // call X
295
- // return
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] = Opcodes.return_call; // change last inst return -> return_call
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
- wasm.splice(i, 1); // remove this inst (return)
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
- if (false && i === wasm.length - 1 && inst[0] === Opcodes.return) {
308
- // replace final return, end -> end (wasm has implicit return)
309
- // return
310
- // end
311
- // -->
312
- // end
313
-
314
- wasm.splice(i, 1); // remove this inst (return)
315
- i--;
316
- // if (optLog) log('opt', `removed redundant return at end`);
317
- continue;
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
- if (i < 2) continue;
321
- const lastLastInst = wasm[i - 2];
343
+ wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
322
344
 
323
- if (depth.length === 2) {
324
- // hack to remove unneeded before get in for loops with (...; i++)
325
- if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
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
- if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
343
- // local.set x
344
- // local.tee y
345
- // local.get x
346
- // -->
347
- // <nothing>
348
-
349
- wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
350
- if (optLog) log('opt', `removed redundant inline param local handling`);
351
- i -= 3;
352
- continue;
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
- if (optLevel < 2) continue;
411
+ if (optLevel < 2) continue;
357
412
 
358
- if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
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
- // remove unneeded var: remove pass
361
- // 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
362
- let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
363
- if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
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
- // note: disabled for now due to instability
366
- if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
367
- if (i < 1) continue;
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
- const inst = wasm[i];
370
- const lastInst = wasm[i - 1];
424
+ const inst = wasm[i];
425
+ const lastInst = wasm[i - 1];
371
426
 
372
- if (lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_set && inst[0] === Opcodes.local_get && unneededCandidates.includes(inst[1])) {
373
- // local.set N
374
- // local.get N
375
- // -->
376
- // <nothing>
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
- wasm.splice(i - 1, 2); // remove insts
379
- i -= 2;
380
- delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
381
- if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
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
- if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
385
- // local.tee N
386
- // -->
387
- // <nothing>
439
+ if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
440
+ // local.tee N
441
+ // -->
442
+ // <nothing>
388
443
 
389
- wasm.splice(i, 1); // remove inst
390
- i--;
444
+ wasm.splice(i, 1); // remove inst
445
+ i--;
391
446
 
392
- const localName = Object.keys(f.locals)[inst[1]];
393
- const removedIdx = f.locals[localName].idx;
394
- delete f.locals[localName]; // remove from locals
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
- // fix locals index for locals after
397
- for (const x in f.locals) {
398
- const local = f.locals[x];
399
- if (local.idx > removedIdx) local.idx--;
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
- for (const inst of wasm) {
403
- if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) && inst[1] > removedIdx) inst[1]--;
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
- unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
407
- unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
461
+ unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
462
+ unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
408
463
 
409
- if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
464
+ if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
465
+ }
410
466
  }
411
- }
412
467
 
413
- const useCount = {};
414
- for (const x in f.locals) useCount[f.locals[x].idx] = 0;
468
+ const useCount = {};
469
+ for (const x in f.locals) useCount[f.locals[x].idx] = 0;
415
470
 
416
- // final pass
417
- depth = [];
418
- for (let i = 0; i < wasm.length; i++) {
419
- let inst = wasm[i];
420
- if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) useCount[inst[1]]++;
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
- if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) depth.push(inst[0]);
423
- if (inst[0] === Opcodes.end) depth.pop();
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
- if (i < 2) continue;
426
- const lastInst = wasm[i - 1];
427
- const lastLastInst = wasm[i - 2];
480
+ if (i < 2) continue;
481
+ const lastInst = wasm[i - 1];
482
+ const lastLastInst = wasm[i - 2];
428
483
 
429
- // todo: add more math ops
430
- if (optLevel >= 3 && (inst[0] === Opcodes.add || inst[0] === Opcodes.sub || inst[0] === Opcodes.mul) && lastLastInst[0] === Opcodes.const && lastInst[0] === Opcodes.const) {
431
- // inline const math ops
432
- // i32.const a
433
- // i32.const b
434
- // i32.add
435
- // -->
436
- // i32.const a + b
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
- // does not work with leb encoded
439
- if (lastInst.length > 2 || lastLastInst.length > 2) continue;
493
+ // does not work with leb encoded
494
+ if (lastInst.length > 2 || lastLastInst.length > 2) continue;
440
495
 
441
- let a = lastLastInst[1];
442
- let b = lastInst[1];
496
+ let a = lastLastInst[1];
497
+ let b = lastInst[1];
443
498
 
444
- const val = performWasmOp(inst[0], a, b);
445
- if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
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
- wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
448
- i -= 2;
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
- const localIdxs = Object.values(f.locals).map(x => x.idx);
453
- // remove unused locals (cleanup)
454
- for (const x in useCount) {
455
- if (useCount[x] === 0) {
456
- const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
457
- if (optLog) log('opt', `removed internal local ${x} (${name})`);
458
- delete f.locals[name];
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
- if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
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;