porffor 0.2.0-fbab1de → 0.2.0-fdf0fc5

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.
Files changed (57) hide show
  1. package/CONTRIBUTING.md +256 -0
  2. package/LICENSE +20 -20
  3. package/README.md +147 -89
  4. package/asur/README.md +2 -0
  5. package/asur/index.js +1262 -0
  6. package/byg/index.js +237 -0
  7. package/compiler/2c.js +317 -72
  8. package/compiler/{sections.js → assemble.js} +63 -15
  9. package/compiler/builtins/annexb_string.js +72 -0
  10. package/compiler/builtins/annexb_string.ts +18 -0
  11. package/compiler/builtins/array.ts +149 -0
  12. package/compiler/builtins/base64.ts +76 -0
  13. package/compiler/builtins/boolean.ts +20 -0
  14. package/compiler/builtins/crypto.ts +120 -0
  15. package/compiler/builtins/date.ts +2070 -0
  16. package/compiler/builtins/escape.ts +141 -0
  17. package/compiler/builtins/function.ts +7 -0
  18. package/compiler/builtins/int.ts +147 -0
  19. package/compiler/builtins/number.ts +534 -0
  20. package/compiler/builtins/object.ts +6 -0
  21. package/compiler/builtins/porffor.d.ts +59 -0
  22. package/compiler/builtins/set.ts +5 -0
  23. package/compiler/builtins/string.ts +1080 -0
  24. package/compiler/builtins.js +450 -270
  25. package/compiler/{codeGen.js → codegen.js} +1065 -414
  26. package/compiler/decompile.js +0 -1
  27. package/compiler/embedding.js +22 -22
  28. package/compiler/encoding.js +108 -10
  29. package/compiler/generated_builtins.js +1526 -0
  30. package/compiler/index.js +36 -34
  31. package/compiler/log.js +6 -3
  32. package/compiler/opt.js +50 -36
  33. package/compiler/parse.js +33 -23
  34. package/compiler/precompile.js +128 -0
  35. package/compiler/prefs.js +27 -0
  36. package/compiler/prototype.js +27 -42
  37. package/compiler/types.js +38 -0
  38. package/compiler/wasmSpec.js +28 -8
  39. package/compiler/wrap.js +51 -46
  40. package/package.json +9 -5
  41. package/porf +4 -0
  42. package/rhemyn/compile.js +46 -27
  43. package/rhemyn/parse.js +322 -320
  44. package/rhemyn/test/parse.js +58 -58
  45. package/runner/compare.js +34 -34
  46. package/runner/debug.js +122 -0
  47. package/runner/index.js +91 -11
  48. package/runner/profiler.js +102 -0
  49. package/runner/repl.js +42 -9
  50. package/runner/sizes.js +37 -37
  51. package/compiler/builtins/base64.js +0 -92
  52. package/r.js +0 -9
  53. package/runner/info.js +0 -89
  54. package/runner/profile.js +0 -46
  55. package/runner/results.json +0 -1
  56. package/runner/transform.js +0 -15
  57. package/util/enum.js +0 -20
package/compiler/2c.js CHANGED
@@ -1,24 +1,106 @@
1
- import { read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
1
+ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from './encoding.js';
2
2
  import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
3
3
  import { operatorOpcode } from './expression.js';
4
4
  import { log } from "./log.js";
5
5
 
6
6
  const CValtype = {
7
- i8: 'char',
8
- i16: 'unsigned short', // presume all i16 stuff is unsigned
9
- i32: 'long',
10
- i32_u: 'unsigned long',
11
- i64: 'long long',
12
- i64_u: 'unsigned long long',
7
+ i8: 'i8',
8
+ i16: 'i16',
9
+ i32: 'i32',
10
+ u32: 'u32',
11
+ i64: 'i64',
12
+ u64: 'u64',
13
13
 
14
- f32: 'float',
15
- f64: 'double',
14
+ f32: 'f32',
15
+ f64: 'f64',
16
16
 
17
17
  undefined: 'void'
18
18
  };
19
19
 
20
+ const alwaysPreface = `typedef uint8_t i8;
21
+ typedef uint16_t i16;
22
+ typedef int32_t i32;
23
+ typedef uint32_t u32;
24
+ typedef int64_t i64;
25
+ typedef uint64_t u64;
26
+ typedef float f32;
27
+ typedef double f64;
28
+
29
+ f64 NAN = 0e+0/0e+0;
30
+
31
+ struct ReturnValue {
32
+ ${CValtype.f64} value;
33
+ ${CValtype.i32} type;
34
+ };\n\n`;
35
+
36
+ // todo: is memcpy/etc safe with host endianness?
37
+
38
+ // all:
39
+ // immediates: ['align', 'offset']
40
+ const CMemFuncs = {
41
+ [Opcodes.i32_store]: {
42
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
43
+ args: ['pointer', 'value'],
44
+ argTypes: [CValtype.i32, CValtype.i32],
45
+ returns: false
46
+ },
47
+ [Opcodes.i32_store16]: {
48
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
49
+ args: ['pointer', 'value'],
50
+ argTypes: [CValtype.i32, CValtype.i16],
51
+ returns: false
52
+ },
53
+ [Opcodes.i32_store8]: {
54
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
55
+ args: ['pointer', 'value'],
56
+ argTypes: [CValtype.i32, CValtype.i8],
57
+ returns: false
58
+ },
59
+
60
+ [Opcodes.i32_load]: {
61
+ c: `${CValtype.i32} out;
62
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
63
+ return out;`,
64
+ args: ['pointer'],
65
+ argTypes: [CValtype.i32],
66
+ returns: CValtype.i32
67
+ },
68
+ [Opcodes.i32_load16_u]: {
69
+ c: `${CValtype.i16} out;
70
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
71
+ return out;`,
72
+ args: ['pointer'],
73
+ argTypes: [CValtype.i32],
74
+ returns: CValtype.i32
75
+ },
76
+ [Opcodes.i32_load8_u]: {
77
+ c: `${CValtype.i8} out;
78
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
79
+ return out;`,
80
+ args: ['pointer'],
81
+ argTypes: [CValtype.i32],
82
+ returns: CValtype.i32
83
+ },
84
+
85
+ [Opcodes.f64_store]: {
86
+ c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
87
+ args: ['pointer', 'value'],
88
+ argTypes: [CValtype.i32, CValtype.f64],
89
+ returns: false
90
+ },
91
+ [Opcodes.f64_load]: {
92
+ c: `${CValtype.f64} out;
93
+ memcpy(&out, _memory + offset + pointer, sizeof(out));
94
+ return out;`,
95
+ args: ['pointer'],
96
+ argTypes: [CValtype.i32],
97
+ returns: CValtype.f64
98
+ },
99
+ };
100
+
20
101
  const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
21
102
  const invOpcodes = inv(Opcodes);
103
+ const invValtype = inv(Valtype);
22
104
 
23
105
  for (const x in CValtype) {
24
106
  if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
@@ -32,15 +114,22 @@ const todo = msg => {
32
114
  }
33
115
  }
34
116
 
35
- throw new TodoError(`todo: ${msg}`);
117
+ throw new TodoError(msg);
36
118
  };
37
119
 
38
120
  const removeBrackets = str => {
39
- if (str.startsWith('(long)(unsigned long)')) return '(long)(unsigned long)(' + removeBrackets(str.slice(22, -1)) + ')';
121
+ // return str;
122
+ // if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
123
+
124
+ for (const x in CValtype) {
125
+ const p = `(${x})`;
126
+ if (str.startsWith(p)) return p + removeBrackets(str.slice(p.length));
127
+ }
128
+
40
129
  return str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
41
130
  };
42
131
 
43
- export default ({ funcs, globals, tags, exceptions, pages }) => {
132
+ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
44
133
  const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
45
134
  for (const k in x) {
46
135
  acc[x[k]] = k;
@@ -56,12 +145,11 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
56
145
  }
57
146
 
58
147
  const includes = new Map(), unixIncludes = new Map(), winIncludes = new Map();
148
+ const prepend = new Map(), prependMain = new Map();
59
149
 
60
- // TODO: make type i16
61
- let out = `struct ReturnValue {
62
- ${CValtype.f64} value;
63
- ${CValtype.i32} type;
64
- };\n\n`;
150
+ includes.set('stdint.h', true);
151
+
152
+ let out = ``;
65
153
 
66
154
  for (const x in globals) {
67
155
  const g = globals[x];
@@ -70,6 +158,15 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
70
158
  out += ';\n';
71
159
  }
72
160
 
161
+ if (pages.size > 0) {
162
+ prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
163
+ includes.set('string.h', true);
164
+ }
165
+
166
+ if (data.length > 0) {
167
+ prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
168
+ }
169
+
73
170
  // for (const [ x, p ] of pages) {
74
171
  // out += `${CValtype[p.type]} ${x.replace(': ', '_').replace(/[^0-9a-zA-Z_]/g, '')}[100]`;
75
172
  // out += ';\n';
@@ -78,7 +175,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
78
175
  if (out) out += '\n';
79
176
 
80
177
  let depth = 1;
81
- const line = (str, semi = true) => out += `${' '.repeat(depth * 2)}${str}${semi ? ';' : ''}\n`;
178
+ let brDepth = 0;
179
+ const line = (str, semi = true) => out += `${' '.repeat(depth * 2 + brDepth * 2)}${str}${semi ? ';' : ''}\n`;
82
180
  const lines = lines => {
83
181
  for (const x of lines) {
84
182
  out += `${' '.repeat(depth * 2)}${x}\n`;
@@ -111,6 +209,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
111
209
  return tmp;
112
210
  };
113
211
 
212
+ let brId = 0;
213
+
114
214
  for (const f of funcs) {
115
215
  depth = 1;
116
216
 
@@ -124,7 +224,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
124
224
  const returns = f.returns.length > 0;
125
225
 
126
226
  const shouldInline = f.internal;
127
- out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? 'double' : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
227
+ out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
228
+
229
+ if (f.name === 'main') {
230
+ out += [...prependMain.values()].join('\n');
231
+ if (prependMain.size > 0) out += '\n\n';
232
+ }
128
233
 
129
234
  const localKeys = Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).slice(f.params.length).sort((a, b) => f.locals[a].idx - f.locals[b].idx);
130
235
  for (const x of localKeys) {
@@ -134,11 +239,58 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
134
239
 
135
240
  if (localKeys.length !== 0) out += '\n';
136
241
 
242
+ const rets = [];
243
+ const runOnEnd = [];
244
+
137
245
  let vals = [];
138
- const endNeedsCurly = [], ignoreEnd = [];
139
- let beginLoop = false, lastCond = false, ifTernary = false;
246
+ const endNeedsCurly = [];
247
+ const brs = [];
248
+ let lastCond = false;
249
+
250
+ // let brDepth = 0;
251
+
252
+ const blockStart = (i, loop) => {
253
+ // reset "stack"
254
+ // vals = [];
255
+
256
+ rets.push(i[1]);
257
+
258
+ const br = brId++;
259
+ brs.push(br);
260
+ if (loop) {
261
+ line(`j${br}:;`, false);
262
+ runOnEnd.push(null);
263
+ } else {
264
+ runOnEnd.push(() => line(`j${br}:;`, false));
265
+ }
266
+
267
+ if (i[1] !== Blocktype.void) line(`${CValtype[i[1]]} _r${br}`);
268
+
269
+ brDepth++;
270
+ };
271
+
272
+ const highlight = i => {
273
+ const surrounding = 6;
274
+
275
+ const decomp = decompile(f.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, f.locals, f.params, f.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
276
+
277
+ const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
278
+ let longest = 0;
279
+ for (let j = 0; j < decomp.length; j++) {
280
+ longest = Math.max(longest, noAnsi(decomp[j]).length);
281
+ }
282
+
283
+ const middle = Math.floor(decomp.length / 2);
284
+ decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
285
+
286
+ console.log('\x1B[90m...\x1B[0m');
287
+ console.log(decomp.join('\n'));
288
+ console.log('\x1B[90m...\x1B[0m\n');
289
+ };
290
+
140
291
  for (let _ = 0; _ < f.wasm.length; _++) {
141
292
  const i = f.wasm[_];
293
+ if (!i || !i[0]) continue;
142
294
 
143
295
  if (invOperatorOpcode[i[0]]) {
144
296
  const b = vals.pop();
@@ -160,12 +312,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
160
312
  switch (i[1]) {
161
313
  // i32_trunc_sat_f64_s
162
314
  case 0x02:
163
- vals.push(`(${CValtype.i32})${vals.pop()}`);
315
+ vals.push(`(${CValtype.i32})(${vals.pop()})`);
164
316
  break;
165
317
 
166
318
  // i32_trunc_sat_f64_u
167
319
  case 0x03:
168
- vals.push(`(${CValtype.i32})(${CValtype.i32_u})${vals.pop()}`);
320
+ vals.push(`(${CValtype.u32})(${vals.pop()})`);
169
321
  break;
170
322
  }
171
323
 
@@ -176,12 +328,19 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
176
328
  switch (i[0]) {
177
329
  case Opcodes.i32_const:
178
330
  case Opcodes.i64_const:
179
- vals.push(read_signedLEB128(i.slice(1)).toString());
331
+ // vals.push(read_signedLEB128(i.slice(1)).toString());
332
+ vals.push(new String(read_signedLEB128(i.slice(1)).toString()));
333
+ vals.at(-1).offset = _;
180
334
  break;
181
335
 
182
- case Opcodes.f64_const:
183
- vals.push(read_ieee754_binary64(i.slice(1)).toExponential());
336
+ case Opcodes.f64_const: {
337
+ // const val = read_ieee754_binary64(i.slice(1)).toExponential();
338
+ const val = new String(read_ieee754_binary64(i.slice(1)).toExponential());
339
+ // vals.push(val == 'NaN' ? 'NAN' : val);
340
+ vals.push(val == 'NaN' ? new String('NAN') : val);
341
+ vals.at(-1).offset = _;
184
342
  break;
343
+ }
185
344
 
186
345
  case Opcodes.local_get:
187
346
  vals.push(`${invLocals[i[1]]}`);
@@ -192,9 +351,9 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
192
351
  break;
193
352
 
194
353
  case Opcodes.local_tee:
195
- // line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
196
- // vals.push(`${invLocals[i[1]]}`);
197
- vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
354
+ line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
355
+ vals.push(`${invLocals[i[1]]}`);
356
+ // vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
198
357
  break;
199
358
 
200
359
  case Opcodes.global_get:
@@ -207,78 +366,102 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
207
366
 
208
367
  case Opcodes.f64_trunc:
209
368
  // vals.push(`trunc(${vals.pop()})`);
210
- vals.push(`(int)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
369
+ vals.push(`(${CValtype.i32})(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
211
370
  break;
212
371
 
213
372
  case Opcodes.f64_convert_i32_u:
214
373
  case Opcodes.f64_convert_i32_s:
215
374
  case Opcodes.f64_convert_i64_u:
216
375
  case Opcodes.f64_convert_i64_s:
217
- // int to double
218
- vals.push(`(double)${vals.pop()}`);
376
+ // int to f64
377
+ vals.push(`(${CValtype.f64})(${removeBrackets(vals.pop())})`);
219
378
  break;
220
379
 
380
+ case Opcodes.i32_eqz:
381
+ if (lastCond) {
382
+ vals.push(`!(${removeBrackets(vals.pop())})`);
383
+ } else {
384
+ let cond = '(' + removeBrackets(vals.pop());
385
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `${cond.slice(`(${CValtype.i32})`.length)}) == 0e+0`;
386
+ else cond += ') == 0';
387
+ vals.push(cond);
388
+ }
389
+ lastCond = true;
390
+ continue;
391
+
221
392
  case Opcodes.return:
222
393
  // line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
223
394
  line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
224
395
  break;
225
396
 
226
- case Opcodes.if:
397
+ case Opcodes.if: {
227
398
  let cond = removeBrackets(vals.pop());
228
399
  if (!lastCond) {
229
- if (cond.startsWith('(long)')) cond = `${cond.slice(6)} == 1e+0`;
230
- else cond += ' == 1';
231
- }
232
-
233
- ifTernary = i[1] !== Blocktype.void;
234
- if (ifTernary) {
235
- ifTernary = cond;
236
- break;
400
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
401
+ else cond = `(${cond}) != 0`;
237
402
  }
238
403
 
239
- if (beginLoop) {
240
- beginLoop = false;
241
- line(`while (${cond}) {`, false);
242
-
243
- depth++;
244
- endNeedsCurly.push(true);
245
- ignoreEnd.push(false, true);
246
- break;
247
- }
404
+ line(`// if ${invValtype[i[1]] ?? ''}`, false);
405
+ blockStart(i, false);
248
406
 
249
407
  line(`if (${cond}) {`, false);
250
408
 
251
409
  depth++;
252
410
  endNeedsCurly.push(true);
253
- ignoreEnd.push(false);
254
411
  break;
412
+ }
255
413
 
256
- case Opcodes.else:
257
- if (ifTernary) break;
414
+ case Opcodes.else: {
415
+ const br = brs.at(-1);
416
+ const ret = rets.at(-1);
417
+ if (ret && ret !== Blocktype.void) {
418
+ // console.log(vals, ret);
419
+ // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
420
+ if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
421
+ // vals.push(`_r${br}`);
422
+ }
258
423
 
259
424
  depth--;
260
425
  line(`} else {`, false);
261
426
  depth++;
427
+
428
+ // reset "stack"
429
+ // vals = [];
262
430
  break;
431
+ }
263
432
 
264
- case Opcodes.loop:
265
- // not doing properly, fake a while loop
266
- beginLoop = true;
433
+ case Opcodes.loop: {
434
+ line(`// loop ${invValtype[i[1]] ?? ''}`, false);
435
+ blockStart(i, true);
436
+ endNeedsCurly.push(false);
267
437
  break;
438
+ }
268
439
 
269
- case Opcodes.end:
270
- if (ignoreEnd.pop()) break;
440
+ case Opcodes.end: {
441
+ const br = brs.pop();
442
+ const ret = rets.pop();
443
+ if (ret && ret !== Blocktype.void) {
444
+ // console.log(vals, ret);
445
+ // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
446
+ if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
447
+ vals.push(`_r${br}`);
448
+ }
271
449
 
272
- if (ifTernary) {
273
- const b = vals.pop();
274
- const a = vals.pop();
275
- vals.push(`${ifTernary} ? ${a} : ${b}`);
276
- break;
450
+ const enc = endNeedsCurly.pop() === true;
451
+ if (enc) {
452
+ depth--;
453
+ line('}', false);
277
454
  }
278
455
 
279
- depth--;
280
- if (endNeedsCurly.pop() === true) line('}', false);
456
+ brDepth--;
457
+
458
+ line(`// end`, false);
459
+
460
+ const roe = runOnEnd.pop();
461
+ if (roe) roe();
462
+
281
463
  break;
464
+ }
282
465
 
283
466
  case Opcodes.call:
284
467
  let func = funcs.find(x => x.index === i[1]);
@@ -286,7 +469,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
286
469
  const importFunc = importFuncs[i[1]];
287
470
  switch (importFunc.name) {
288
471
  case 'print':
289
- line(`printf("%f\\n", ${vals.pop()})`);
472
+ // line(`printf("%f\\n", ${vals.pop()})`);
473
+ line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
290
474
  includes.set('stdio.h', true);
291
475
  break;
292
476
  case 'printChar':
@@ -349,13 +533,74 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
349
533
  vals.pop();
350
534
  break;
351
535
 
352
- case Opcodes.br:
353
- // ignore
354
- // reset "stack"
355
- vals = [];
536
+ case Opcodes.block:
537
+ line(`// block ${invValtype[i[1]] ?? ''}`, false);
538
+ blockStart(i, false);
539
+ endNeedsCurly.push(false);
540
+ break;
541
+
542
+ case Opcodes.br: {
543
+ const ret = rets[brDepth - i[1] - 1];
544
+ // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
545
+ if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
546
+ line(`goto j${brs[brDepth - i[1] - 1]}`);
547
+
548
+ // // reset "stack"
549
+ // vals = [];
550
+ break;
551
+ }
552
+
553
+ case Opcodes.br_if: {
554
+ const ret = rets[brDepth - i[1] - 1];
555
+ // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
556
+
557
+ let cond = removeBrackets(vals.pop());
558
+ if (!lastCond) {
559
+ if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
560
+ else cond = `(${cond}) != 0`;
561
+ }
562
+
563
+ line(`if (${cond}) {`, false);
564
+ depth++;
565
+ if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.at(-1))}`);
566
+ line(`goto j${brs[brDepth - i[1] - 1]}`);
567
+ depth--;
568
+ line(`}`, false);
569
+
356
570
  break;
571
+ }
572
+
573
+ case Opcodes.throw: {
574
+ const id = vals.pop();
575
+
576
+ line(`printf("Uncaught ${exceptions[id].constructor}: ${exceptions[id].message}\\n")`);
577
+ line(`exit(1)`);
578
+
579
+ includes.set('stdlib.h', true);
580
+
581
+ break;
582
+ }
357
583
 
358
584
  default:
585
+ if (CMemFuncs[i[0]]) {
586
+ const name = invOpcodes[i[0]];
587
+ const func = CMemFuncs[i[0]];
588
+ if (!prepend.has(name)) {
589
+ prepend.set(name, `${func.returns || 'void'} ${name}(${CValtype.i32} align, ${CValtype.i32} offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
590
+ // generate func c and prepend
591
+ }
592
+
593
+ const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
594
+
595
+ let args = [];
596
+ for (let j = 0; j < func.args.length; j++) args.unshift(removeBrackets(vals.pop()));
597
+
598
+ if (func.returns !== false) {
599
+ vals.push(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
600
+ } else line(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
601
+ break;
602
+ }
603
+
359
604
  log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
360
605
  // todo(`unimplemented op: ${invOpcodes[i[0]]}`);
361
606
  }
@@ -379,7 +624,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
379
624
 
380
625
  const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
381
626
 
382
- out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + out;
627
+ out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
383
628
 
384
- return out;
629
+ return out.trim();
385
630
  };
@@ -1,8 +1,9 @@
1
1
  import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
2
- import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
3
- import { number } from './embedding.js';
2
+ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, ieee754_binary64, unsignedLEB128_into, signedLEB128_into, ieee754_binary64_into } from './encoding.js';
3
+ // import { number } from './embedding.js';
4
4
  import { importedFuncs } from './builtins.js';
5
5
  import { log } from "./log.js";
6
+ import Prefs from './prefs.js';
6
7
 
7
8
  const createSection = (type, data) => [
8
9
  type,
@@ -26,12 +27,12 @@ export default (funcs, globals, tags, pages, data, flags) => {
26
27
 
27
28
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
28
29
 
29
- const compileHints = process.argv.includes('-compile-hints');
30
- if (compileHints) log.warning('sections', 'compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
30
+ const compileHints = Prefs.compileHints;
31
+ if (compileHints) log.warning('assemble', 'compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
31
32
 
32
33
  const getType = (params, returns) => {
33
34
  const hash = `${params.join(',')}_${returns.join(',')}`;
34
- if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
35
+ if (Prefs.optLog) log('assemble', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
35
36
  if (optLevel >= 1 && typeCache[hash] !== undefined) return typeCache[hash];
36
37
 
37
38
  const type = [ FuncType, ...encodeVector(params), ...encodeVector(returns) ];
@@ -44,7 +45,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
44
45
 
45
46
  let importFuncs = [];
46
47
 
47
- if (optLevel < 1) {
48
+ if (optLevel < 1 || !Prefs.treeshakeWasmImports) {
48
49
  importFuncs = importedFuncs;
49
50
  } else {
50
51
  let imports = new Map();
@@ -79,11 +80,11 @@ export default (funcs, globals, tags, pages, data, flags) => {
79
80
  }
80
81
  globalThis.importFuncs = importFuncs;
81
82
 
82
- if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
83
+ if (Prefs.optLog) log('assemble', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
83
84
 
84
85
  const importSection = importFuncs.length === 0 ? [] : createSection(
85
86
  Section.import,
86
- encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(new Array(x.params).fill(valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
87
+ encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(new Array(x.params).fill(x.name.startsWith('profile') ? Valtype.i32 : valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
87
88
  );
88
89
 
89
90
  const funcSection = createSection(
@@ -95,18 +96,65 @@ export default (funcs, globals, tags, pages, data, flags) => {
95
96
  // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
96
97
  const chSection = !compileHints ? [] : customSection(
97
98
  'compilationHints',
98
- // for now just do everything as optimise eager
99
+ // for now just do everything as optimize eager
99
100
  encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
100
101
  );
101
102
 
102
- const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
103
- Section.global,
104
- encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
105
- );
103
+ // const t0 = performance.now();
104
+
105
+ // specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection(...)
106
+ const globalsValues = Object.values(globals);
107
+
108
+ let globalSection = [];
109
+ if (globalsValues.length > 0) {
110
+ let data = unsignedLEB128(globalsValues.length);
111
+ for (let i = 0; i < globalsValues.length; i++) {
112
+ const global = globalsValues[i];
113
+
114
+ switch (global.type) {
115
+ case Valtype.i32:
116
+ if (i > 0) data.push(Opcodes.end, Valtype.i32, 0x01, Opcodes.i32_const);
117
+ else data.push(Valtype.i32, 0x01, Opcodes.i32_const);
118
+
119
+ signedLEB128_into(global.init ?? 0, data);
120
+ break;
121
+
122
+ case Valtype.i64:
123
+ if (i > 0) data.push(Opcodes.end, Valtype.i64, 0x01, Opcodes.i64_const);
124
+ else data.push(Valtype.i64, 0x01, Opcodes.i64_const);
125
+
126
+ signedLEB128_into(global.init ?? 0, data);
127
+ break;
128
+
129
+ case Valtype.f64:
130
+ if (i > 0) data.push(Opcodes.end, Valtype.f64, 0x01, Opcodes.f64_const);
131
+ else data.push(Valtype.f64, 0x01, Opcodes.f64_const);
132
+
133
+ ieee754_binary64_into(global.init ?? 0, data);
134
+ break;
135
+ }
136
+ }
137
+
138
+ data.push(Opcodes.end);
139
+
140
+ globalSection.push(Section.global);
141
+
142
+ unsignedLEB128_into(data.length, globalSection);
143
+ globalSection = globalSection.concat(data);
144
+ }
145
+
146
+ // if (Prefs.profileCompiler) {
147
+ // const log = console.log;
148
+ // console.log = function () {
149
+ // log.apply(this, arguments);
150
+ // console.log = log;
151
+ // console.log(` a. assembled global section in ${(performance.now() - t0).toFixed(2)}ms\n`);
152
+ // };
153
+ // }
106
154
 
107
155
  const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
108
156
 
109
- if (process.argv.includes('-always-memory') && pages.size === 0) pages.set('-always-memory', 0);
157
+ if (Prefs.alwaysMemory && pages.size === 0) pages.set('--always-memory', 0);
110
158
  if (optLevel === 0) pages.set('O0 precaution', 0);
111
159
 
112
160
  const usesMemory = pages.size > 0;
@@ -169,7 +217,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
169
217
  unsignedLEB128(data.length)
170
218
  );
171
219
 
172
- if (process.argv.includes('-sections')) console.log({
220
+ if (Prefs.sections) console.log({
173
221
  typeSection: typeSection.map(x => x.toString(16)),
174
222
  importSection: importSection.map(x => x.toString(16)),
175
223
  funcSection: funcSection.map(x => x.toString(16)),