porffor 0.16.0-e5b6b94c8 → 0.16.0-f9dde1759

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.
@@ -1078,7 +1078,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1078
1078
  locals,
1079
1079
  localInd: allLocals.length,
1080
1080
  returns,
1081
- returnType: returnType ?? TYPES.number,
1081
+ returnType,
1082
1082
  internal: true,
1083
1083
  index: currentFuncIndex++,
1084
1084
  table
@@ -1254,7 +1254,7 @@ const getNodeType = (scope, node) => {
1254
1254
  const func = funcs.find(x => x.name === name);
1255
1255
 
1256
1256
  if (func) {
1257
- if (func.returnType) return func.returnType;
1257
+ if (func.returnType != null) return func.returnType;
1258
1258
  }
1259
1259
 
1260
1260
  if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
@@ -1270,15 +1270,7 @@ const getNodeType = (scope, node) => {
1270
1270
  const func = spl[spl.length - 1];
1271
1271
  const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
1272
1272
  if (protoFuncs.length === 1) {
1273
- if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
1274
- }
1275
-
1276
- if (protoFuncs.length > 0) {
1277
- if (scope.locals['#last_type']) return getLastType(scope);
1278
-
1279
- // presume
1280
- // todo: warn here?
1281
- return TYPES.number;
1273
+ if (protoFuncs[0].returnType != null) return protoFuncs[0].returnType;
1282
1274
  }
1283
1275
  }
1284
1276
 
@@ -1815,7 +1807,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1815
1807
  f64_store: { imms: 2, args: [ true, false ], returns: 0 },
1816
1808
 
1817
1809
  // value
1818
- i32_const: { imms: 1, args: [], returns: 1 },
1810
+ i32_const: { imms: 1, args: [], returns: 0 },
1819
1811
  };
1820
1812
 
1821
1813
  const opName = name.slice('__Porffor_wasm_'.length);
@@ -1978,7 +1970,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1978
1970
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
1979
1971
  const userFunc = func && !func.internal;
1980
1972
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1981
- const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1973
+ const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1982
1974
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
1983
1975
 
1984
1976
  let args = decl.arguments;
@@ -2007,8 +1999,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2007
1999
  }
2008
2000
 
2009
2001
  if (valtypeBinary !== Valtype.i32 && (
2010
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2011
- (importedFuncs[name] && name.startsWith('profile'))
2002
+ (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32)
2003
+ // (importedFuncs[name] && name.startsWith('profile'))
2012
2004
  )) {
2013
2005
  out.push(Opcodes.i32_to);
2014
2006
  }
@@ -2123,7 +2115,6 @@ const brTable = (input, bc, returns) => {
2123
2115
  }
2124
2116
 
2125
2117
  for (let i = 0; i < count; i++) {
2126
- // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2127
2118
  if (i === 0) out.push([ Opcodes.block, returns ]);
2128
2119
  else out.push([ Opcodes.block, Blocktype.void ]);
2129
2120
  }
@@ -2157,10 +2148,8 @@ const brTable = (input, bc, returns) => {
2157
2148
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2158
2149
  );
2159
2150
 
2160
- // if you can guess why we sort the wrong way and then reverse
2161
- // (instead of just sorting the correct way)
2162
- // dm me and if you are correct and the first person
2163
- // I will somehow shout you out or something
2151
+ // sort the wrong way and then reverse
2152
+ // so strings ('default') are at the start before any numbers
2164
2153
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2165
2154
 
2166
2155
  br = count - 1;
@@ -2186,10 +2175,10 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2186
2175
  return bc[known] ?? bc.default;
2187
2176
  }
2188
2177
 
2189
- if (Prefs.typeswitchUseBrtable)
2178
+ if (Prefs.typeswitchBrtable)
2190
2179
  return brTable(type, bc, returns);
2191
2180
 
2192
- const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
2181
+ const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
2193
2182
  const out = [
2194
2183
  ...type,
2195
2184
  [ Opcodes.local_set, tmp ],
@@ -2571,7 +2560,7 @@ const generateUnary = (scope, decl) => {
2571
2560
  // * -1
2572
2561
 
2573
2562
  if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
2574
- // if -<N>, just return that
2563
+ // if -n, just return that as a const
2575
2564
  return number(-1 * decl.argument.value);
2576
2565
  }
2577
2566
 
@@ -2582,15 +2571,15 @@ const generateUnary = (scope, decl) => {
2582
2571
 
2583
2572
  case '!':
2584
2573
  const arg = decl.argument;
2585
- if (arg.type === "UnaryExpression" && arg.operator === "!") {
2586
- // !!x -> is x truthy
2574
+ if (arg.type === 'UnaryExpression' && arg.operator === '!') {
2575
+ // opt: !!x -> is x truthy
2587
2576
  return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
2588
2577
  }
2578
+
2589
2579
  // !=
2590
- return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
2580
+ return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
2591
2581
 
2592
2582
  case '~':
2593
- // todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
2594
2583
  return [
2595
2584
  ...generate(scope, decl.argument),
2596
2585
  Opcodes.i32_to,
@@ -3278,7 +3267,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3278
3267
  firstAssign = true;
3279
3268
 
3280
3269
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3281
- const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3270
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3282
3271
 
3283
3272
  let page;
3284
3273
  if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
@@ -3510,7 +3499,7 @@ const generateMember = (scope, decl, _global, _name) => {
3510
3499
  }
3511
3500
 
3512
3501
  if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
3513
- if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
3502
+ if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3514
3503
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3515
3504
 
3516
3505
  if (Prefs.fastLength) {
@@ -3666,7 +3655,7 @@ const generateMember = (scope, decl, _global, _name) => {
3666
3655
  });
3667
3656
  };
3668
3657
 
3669
- const randId = () => Math.random().toString(16).slice(0, -4);
3658
+ const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3670
3659
 
3671
3660
  const objectHack = node => {
3672
3661
  if (!node) return node;
@@ -3718,7 +3707,7 @@ const generateFunc = (scope, decl) => {
3718
3707
  if (decl.async) return todo(scope, 'async functions are not supported');
3719
3708
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3720
3709
 
3721
- const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3710
+ const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3722
3711
  const params = decl.params ?? [];
3723
3712
 
3724
3713
  // TODO: share scope/locals between !!!
@@ -3979,19 +3968,8 @@ export default program => {
3979
3968
  data = [];
3980
3969
  currentFuncIndex = importedFuncs.length;
3981
3970
 
3982
- globalThis.valtype = 'f64';
3983
-
3984
- const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
3985
- if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
3986
-
3987
- globalThis.valtypeBinary = Valtype[valtype];
3988
-
3989
3971
  const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
3990
3972
 
3991
- globalThis.pageSize = PageSize;
3992
- const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
3993
- if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
3994
-
3995
3973
  // set generic opcodes for current valtype
3996
3974
  Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
3997
3975
  Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
@@ -0,0 +1,535 @@
1
+ // cyclone: wasm partial constant evaluator (it is fast and dangerous hence "cyclone")
2
+ import { signedLEB128, ieee754_binary64, read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
3
+ import { Opcodes, Valtype } from './wasmSpec.js';
4
+
5
+ const number = (n, valtype = valtypeBinary) => {
6
+ switch (valtype) {
7
+ case Valtype.i32: return [ Opcodes.i32_const, ...signedLEB128(n) ];
8
+ case Valtype.i64: return [ Opcodes.i64_const, ...signedLEB128(n) ];
9
+ case Valtype.f64: return [ Opcodes.f64_const, ...ieee754_binary64(n) ];
10
+ }
11
+ };
12
+
13
+ const f64ToI32Op = {
14
+ [Opcodes.f64_eq]: Opcodes.i32_eq,
15
+ [Opcodes.f64_ne]: Opcodes.i32_ne,
16
+ [Opcodes.f64_lt]: Opcodes.i32_lt_s,
17
+ [Opcodes.f64_le]: Opcodes.i32_le_s,
18
+ [Opcodes.f64_gt]: Opcodes.i32_gt_s,
19
+ [Opcodes.f64_ge]: Opcodes.i32_ge_s,
20
+ [Opcodes.f64_add]: Opcodes.i32_add,
21
+ [Opcodes.f64_sub]: Opcodes.i32_sub,
22
+ [Opcodes.f64_mul]: Opcodes.i32_mul,
23
+ [Opcodes.f64_div]: Opcodes.i32_div_s,
24
+ };
25
+
26
+ export default wasm => {
27
+ let stack = []; // """""stack"""""
28
+ for (let i = 0; i < wasm.length; i++) {
29
+ let op = wasm[i];
30
+ if (!op) continue;
31
+
32
+ // op = [ ...op.filter(x => x != null && x <= 0xff) ];
33
+ op = [ ...op ];
34
+ wasm[i] = op;
35
+
36
+ let opcode = op[0];
37
+ if (opcode === 0xfc) { // multibyte op
38
+ opcode = (opcode << 8) + op[1];
39
+ }
40
+
41
+ const push = val => {
42
+ stack.push({ val, op });
43
+ };
44
+
45
+ const pop = () => {
46
+ const popped = stack.pop();
47
+
48
+ // remove the op
49
+ wasm.splice(wasm.indexOf(popped.op), 1);
50
+ i--;
51
+
52
+ return popped.val;
53
+ };
54
+
55
+ const pop2 = () => [ pop(), pop() ];
56
+
57
+ const bool = v => v ? 1 : 0;
58
+
59
+ const replaceOp = newOp => {
60
+ op.splice(0, op.length, ...newOp);
61
+ };
62
+ const replaceVal = (val, valtype) => replaceOp(number(val, valtype));
63
+
64
+ const empty = () => {
65
+ stack = [];
66
+ };
67
+
68
+ switch (opcode) {
69
+ case Opcodes.i32_const: {
70
+ const n = read_signedLEB128(op.slice(1));
71
+ push(n);
72
+ break;
73
+ }
74
+ case Opcodes.f64_const: {
75
+ const n = read_ieee754_binary64(op.slice(1));
76
+ push(n);
77
+ break;
78
+ }
79
+
80
+ case Opcodes.i32_eqz: {
81
+ if (stack.length < 1) { empty(); break; };
82
+ const v = bool(pop() === 0);
83
+
84
+ replaceVal(v, Valtype.i32);
85
+ push(v);
86
+ break;
87
+ }
88
+ case Opcodes.i32_eq: {
89
+ if (stack.length < 2) { empty(); break; };
90
+ const [ b, a ] = pop2();
91
+ const v = bool(a === b);
92
+
93
+ replaceVal(v, Valtype.i32);
94
+ push(v);
95
+ break;
96
+ }
97
+ case Opcodes.i32_ne: {
98
+ if (stack.length < 2) { empty(); break; };
99
+ const [ b, a ] = pop2();
100
+ const v = bool(a !== b);
101
+
102
+ replaceVal(v, Valtype.i32);
103
+ push(v);
104
+ break;
105
+ }
106
+ case Opcodes.i32_lt_s: {
107
+ if (stack.length < 2) { empty(); break; };
108
+ const [ b, a ] = pop2();
109
+ const v = bool(a < b);
110
+
111
+ replaceVal(v, Valtype.i32);
112
+ push(v);
113
+ break;
114
+ }
115
+ case Opcodes.i32_le_s: {
116
+ if (stack.length < 2) { empty(); break; };
117
+ const [ b, a ] = pop2();
118
+ const v = bool(a <= b);
119
+
120
+ replaceVal(v, Valtype.i32);
121
+ push(v);
122
+ break;
123
+ }
124
+ case Opcodes.i32_gt_s: {
125
+ if (stack.length < 2) { empty(); break; };
126
+ const [ b, a ] = pop2();
127
+ const v = bool(a > b);
128
+
129
+ replaceVal(v, Valtype.i32);
130
+ push(v);
131
+ break;
132
+ }
133
+ case Opcodes.i32_ge_s: {
134
+ if (stack.length < 2) { empty(); break; };
135
+ const [ b, a ] = pop2();
136
+ const v = bool(a >= b);
137
+
138
+ replaceVal(v, Valtype.i32);
139
+ push(v);
140
+ break;
141
+ }
142
+
143
+ case Opcodes.i32_add: {
144
+ if (stack.length < 2) { empty(); break; };
145
+ const [ b, a ] = pop2();
146
+ const v = a + b;
147
+
148
+ replaceVal(v, Valtype.i32);
149
+ push(v);
150
+ break;
151
+ }
152
+ case Opcodes.i32_sub: {
153
+ if (stack.length < 2) { empty(); break; };
154
+ const [ b, a ] = pop2();
155
+ const v = a - b;
156
+
157
+ replaceVal(v, Valtype.i32);
158
+ push(v);
159
+ break;
160
+ }
161
+ case Opcodes.i32_mul: {
162
+ if (stack.length < 2) { empty(); break; };
163
+ const [ b, a ] = pop2();
164
+ const v = a * b;
165
+
166
+ replaceVal(v, Valtype.i32);
167
+ push(v);
168
+ break;
169
+ }
170
+ case Opcodes.i32_div_s: {
171
+ if (stack.length < 2) { empty(); break; };
172
+ const [ b, a ] = pop2();
173
+ const v = a / b;
174
+
175
+ replaceVal(v, Valtype.i32);
176
+ push(v);
177
+ break;
178
+ }
179
+ case Opcodes.i32_rem_s: {
180
+ if (stack.length < 2) { empty(); break; };
181
+ const [ b, a ] = pop2();
182
+ const v = a % b; // not rem but good enough
183
+
184
+ replaceVal(v, Valtype.i32);
185
+ push(v);
186
+ break;
187
+ }
188
+
189
+ case Opcodes.i32_and: {
190
+ if (stack.length < 2) { empty(); break; };
191
+ const [ b, a ] = pop2();
192
+ const v = a & b;
193
+
194
+ replaceVal(v, Valtype.i32);
195
+ push(v);
196
+ break;
197
+ }
198
+ case Opcodes.i32_or: {
199
+ if (stack.length < 2) { empty(); break; };
200
+ const [ b, a ] = pop2();
201
+ const v = a | b;
202
+
203
+ replaceVal(v, Valtype.i32);
204
+ push(v);
205
+ break;
206
+ }
207
+ case Opcodes.i32_xor: {
208
+ if (stack.length < 2) { empty(); break; };
209
+ const [ b, a ] = pop2();
210
+ const v = a ^ b;
211
+
212
+ replaceVal(v, Valtype.i32);
213
+ push(v);
214
+ break;
215
+ }
216
+ case Opcodes.i32_shl: {
217
+ if (stack.length < 2) { empty(); break; };
218
+ const [ b, a ] = pop2();
219
+ const v = a << b;
220
+
221
+ replaceVal(v, Valtype.i32);
222
+ push(v);
223
+ break;
224
+ }
225
+ case Opcodes.i32_shr_s: {
226
+ if (stack.length < 2) { empty(); break; };
227
+ const [ b, a ] = pop2();
228
+ const v = a >> b;
229
+
230
+ replaceVal(v, Valtype.i32);
231
+ push(v);
232
+ break;
233
+ }
234
+ case Opcodes.i32_shr_u: {
235
+ if (stack.length < 2) { empty(); break; };
236
+ const [ b, a ] = pop2();
237
+ const v = a >>> b;
238
+
239
+ replaceVal(v, Valtype.i32);
240
+ push(v);
241
+ break;
242
+ }
243
+
244
+ case Opcodes.f64_eq: {
245
+ if (stack.length < 2) { empty(); break; };
246
+ const [ b, a ] = pop2();
247
+ const v = bool(a === b);
248
+
249
+ replaceVal(v, Valtype.f64);
250
+ push(v);
251
+ break;
252
+ }
253
+ case Opcodes.f64_ne: {
254
+ if (stack.length < 2) { empty(); break; };
255
+ const [ b, a ] = pop2();
256
+ const v = bool(a !== b);
257
+
258
+ replaceVal(v, Valtype.f64);
259
+ push(v);
260
+ break;
261
+ }
262
+ case Opcodes.f64_lt: {
263
+ if (stack.length < 2) { empty(); break; };
264
+ const [ b, a ] = pop2();
265
+ const v = bool(a < b);
266
+
267
+ replaceVal(v, Valtype.f64);
268
+ push(v);
269
+ break;
270
+ }
271
+ case Opcodes.f64_le: {
272
+ if (stack.length < 2) { empty(); break; };
273
+ const [ b, a ] = pop2();
274
+ const v = bool(a <= b);
275
+
276
+ replaceVal(v, Valtype.f64);
277
+ push(v);
278
+ break;
279
+ }
280
+ case Opcodes.f64_gt: {
281
+ if (stack.length < 2) { empty(); break; };
282
+ const [ b, a ] = pop2();
283
+ const v = bool(a > b);
284
+
285
+ replaceVal(v, Valtype.f64);
286
+ push(v);
287
+ break;
288
+ }
289
+ case Opcodes.f64_ge: {
290
+ if (stack.length < 2) { empty(); break; };
291
+ const [ b, a ] = pop2();
292
+ const v = bool(a >= b);
293
+
294
+ replaceVal(v, Valtype.f64);
295
+ push(v);
296
+ break;
297
+ }
298
+
299
+ case Opcodes.f64_abs: {
300
+ if (stack.length < 1) { empty(); break; };
301
+ const v = Math.abs(pop());
302
+
303
+ replaceVal(v, Valtype.f64);
304
+ push(v);
305
+ break;
306
+ }
307
+ case Opcodes.f64_neg: {
308
+ if (stack.length < 1) { empty(); break; };
309
+ const v = -pop();
310
+
311
+ replaceVal(v, Valtype.f64);
312
+ push(v);
313
+ break;
314
+ }
315
+
316
+ case Opcodes.f64_ceil: {
317
+ if (stack.length < 1) { empty(); break; };
318
+ const v = Math.ceil(pop());
319
+
320
+ replaceVal(v, Valtype.f64);
321
+ push(v);
322
+ break;
323
+ }
324
+ case Opcodes.f64_floor: {
325
+ if (stack.length < 1) { empty(); break; };
326
+ const v = Math.floor(pop());
327
+
328
+ replaceVal(v, Valtype.f64);
329
+ push(v);
330
+ break;
331
+ }
332
+ case Opcodes.f64_trunc: {
333
+ if (stack.length < 1) { empty(); break; };
334
+ const v = Math.trunc(pop());
335
+
336
+ replaceVal(v, Valtype.f64);
337
+ push(v);
338
+ break;
339
+ }
340
+ case Opcodes.f64_nearest: {
341
+ if (stack.length < 1) { empty(); break; };
342
+ const v = Math.round(pop());
343
+
344
+ replaceVal(v, Valtype.f64);
345
+ push(v);
346
+ break;
347
+ }
348
+
349
+ case Opcodes.f64_sqrt: {
350
+ if (stack.length < 1) { empty(); break; };
351
+ const v = Math.sqrt(pop());
352
+
353
+ replaceVal(v, Valtype.f64);
354
+ push(v);
355
+ break;
356
+ }
357
+
358
+ case Opcodes.f64_add: {
359
+ if (stack.length < 2) { empty(); break; };
360
+ const [ b, a ] = pop2();
361
+ const v = a + b;
362
+
363
+ replaceVal(v, Valtype.f64);
364
+ push(v);
365
+ break;
366
+ }
367
+ case Opcodes.f64_sub: {
368
+ if (stack.length < 2) { empty(); break; };
369
+ const [ b, a ] = pop2();
370
+ const v = a - b;
371
+
372
+ replaceVal(v, Valtype.f64);
373
+ push(v);
374
+ break;
375
+ }
376
+ case Opcodes.f64_mul: {
377
+ if (stack.length < 2) { empty(); break; };
378
+ const [ b, a ] = pop2();
379
+ const v = a * b;
380
+
381
+ replaceVal(v, Valtype.f64);
382
+ push(v);
383
+ break;
384
+ }
385
+ case Opcodes.f64_div: {
386
+ if (stack.length < 2) { empty(); break; };
387
+ const [ b, a ] = pop2();
388
+ const v = a / b;
389
+
390
+ replaceVal(v, Valtype.f64);
391
+ push(v);
392
+ break;
393
+ }
394
+
395
+ case Opcodes.f64_min: {
396
+ if (stack.length < 2) { empty(); break; };
397
+ const [ b, a ] = pop2();
398
+ const v = Math.min(a, b);
399
+
400
+ replaceVal(v, Valtype.f64);
401
+ push(v);
402
+ break;
403
+ }
404
+ case Opcodes.f64_max: {
405
+ if (stack.length < 2) { empty(); break; };
406
+ const [ b, a ] = pop2();
407
+ const v = a + b;
408
+
409
+ replaceVal(v, Valtype.f64);
410
+ push(v);
411
+ break;
412
+ }
413
+
414
+ case Opcodes.f64_copysign: { // ?
415
+ if (stack.length < 2) { empty(); break; };
416
+ const [ b, a ] = pop2();
417
+ const v = Math.abs(a) * (b > 0 ? 1 : -1);
418
+
419
+ replaceVal(v, Valtype.f64);
420
+ push(v);
421
+ break;
422
+ }
423
+
424
+ case Opcodes.f64_convert_i32_u:
425
+ case Opcodes.f64_convert_i32_s: {
426
+ if (stack.length < 1) { empty(); break; };
427
+ const v = pop();
428
+
429
+ replaceVal(v, Valtype.f64);
430
+ push(v);
431
+ break;
432
+ }
433
+
434
+ case 0xfc02: { // i32_trunc_sat_f64_s
435
+ if (stack.length < 1) { empty(); break; }
436
+ const v = pop();
437
+
438
+ replaceVal(v, Valtype.i32);
439
+ push(v);
440
+ break;
441
+ }
442
+
443
+ case 0xfc03: { // i32_trunc_sat_f64_u
444
+ if (stack.length < 1) { empty(); break; }
445
+ const v = pop();
446
+
447
+ replaceVal(v, Valtype.i32);
448
+ push(v);
449
+ break;
450
+ }
451
+
452
+ default: {
453
+ empty();
454
+ break;
455
+ }
456
+ }
457
+
458
+ // this does, eg:
459
+ // local.get 7 ;; $i (i32)
460
+ // f64.convert_i32_s
461
+ // f64.const 1
462
+ // f64.add
463
+ // i32.trunc_sat_f64_s <--
464
+ // local.set 7 ;; $i (i32)
465
+ // ->
466
+ // local.get 7 ;; $i (i32)
467
+ // i32.const 1
468
+ // i32.add
469
+ // local.set 7 ;; $i (i32)
470
+ if (
471
+ (opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
472
+ (opcode >= 0x61 && opcode <= 0x66) // main f64 eq op
473
+ ) {
474
+ const o2 = wasm[i - 1][0];
475
+ if (o2 === Opcodes.f64_const) { // f64.const
476
+ const o3 = wasm[i - 2][0];
477
+ if (o3 === Opcodes.f64_convert_i32_s || o3 === Opcodes.f64_convert_i32_u) {
478
+ // remove now unneeded i32 -> f64 convert
479
+ wasm.splice(i - 2, 1);
480
+ i--;
481
+
482
+ // convert f64.const -> i32.const
483
+ const n = read_ieee754_binary64(wasm[i - 1].slice(1));
484
+ wasm.splice(i - 1, 1, number(n, Valtype.i32));
485
+
486
+ // convert math op from f64 to i32
487
+ wasm[i][0] = f64ToI32Op[wasm[i][0]];
488
+
489
+ const nextOp = wasm[i + 1];
490
+ if (nextOp && opcode >= 0xa0 && opcode <= 0xa3) {
491
+ if (nextOp[0] === 0xfc && (nextOp[1] === 0x02 || nextOp[1] === 0x03)) {
492
+ // remove optional unneeded f64 -> i32 convert after
493
+ wasm.splice(i + 1, 1);
494
+ } else {
495
+ // add now needed i32 -> f64 convert after
496
+ wasm.splice(i + 1, Opcodes.i32_trunc_sat_f64_s);
497
+ }
498
+ }
499
+ }
500
+ }
501
+ }
502
+
503
+ if ((opcode === 0xfc02 || opcode === 0xfc03) && i > 3) { // i32.trunc_sat_f64_s/u
504
+ const o2 = wasm[i - 1][0];
505
+ if (
506
+ (o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
507
+ (o2 >= 0x61 && o2 <= 0x66) // main f64 eq op
508
+ ) {
509
+ const o3 = wasm[i - 2][0];
510
+ if (o3 === Opcodes.f64_const) { // f64.const
511
+ const o4 = wasm[i - 3][0];
512
+ if (o4 === Opcodes.f64_convert_i32_s || o4 === Opcodes.f64_convert_i32_u) {
513
+ // remove now unneeded i32 -> f64 convert
514
+ wasm.splice(i - 3, 1);
515
+ i--;
516
+ } else {
517
+ // add now needed f64 -> i32 convert prior
518
+ wasm.splice(i - 2, 0, Opcodes.i32_trunc_sat_f64_s);
519
+ i++;
520
+ }
521
+
522
+ // convert f64.const -> i32.const
523
+ const n = read_ieee754_binary64(wasm[i - 2].slice(1));
524
+ wasm.splice(i - 2, 1, number(n, Valtype.i32));
525
+
526
+ // convert math op from f64 to i32
527
+ wasm[i - 1][0] = f64ToI32Op[wasm[i - 1][0]];
528
+
529
+ // remove this now unneeded f64 -> i32 convert
530
+ wasm.splice(i, 1);
531
+ }
532
+ }
533
+ }
534
+ }
535
+ };