porffor 0.2.0-c6c8c81 → 0.2.0-c87ffeb

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 (54) hide show
  1. package/.vscode/launch.json +18 -0
  2. package/LICENSE +20 -20
  3. package/README.md +131 -71
  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 +325 -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 +19 -0
  11. package/compiler/builtins/array.ts +145 -0
  12. package/compiler/builtins/base64.ts +151 -0
  13. package/compiler/builtins/crypto.ts +120 -0
  14. package/compiler/builtins/date.ts +7 -0
  15. package/compiler/builtins/escape.ts +141 -0
  16. package/compiler/builtins/int.ts +147 -0
  17. package/compiler/builtins/number.ts +527 -0
  18. package/compiler/builtins/porffor.d.ts +42 -0
  19. package/compiler/builtins/string.ts +1055 -0
  20. package/compiler/builtins/tostring.ts +45 -0
  21. package/compiler/builtins.js +601 -269
  22. package/compiler/{codeGen.js → codegen.js} +1231 -472
  23. package/compiler/decompile.js +11 -12
  24. package/compiler/embedding.js +22 -22
  25. package/compiler/encoding.js +98 -114
  26. package/compiler/generated_builtins.js +695 -0
  27. package/compiler/index.js +40 -37
  28. package/compiler/log.js +6 -3
  29. package/compiler/opt.js +65 -29
  30. package/compiler/parse.js +42 -11
  31. package/compiler/precompile.js +123 -0
  32. package/compiler/prefs.js +26 -0
  33. package/compiler/prototype.js +177 -37
  34. package/compiler/types.js +37 -0
  35. package/compiler/wasmSpec.js +30 -7
  36. package/compiler/wrap.js +141 -45
  37. package/package.json +9 -5
  38. package/porf +4 -0
  39. package/rhemyn/compile.js +5 -3
  40. package/rhemyn/parse.js +323 -320
  41. package/rhemyn/test/parse.js +58 -58
  42. package/runner/compare.js +34 -34
  43. package/runner/debug.js +122 -0
  44. package/runner/index.js +49 -10
  45. package/runner/profiler.js +102 -0
  46. package/runner/repl.js +42 -9
  47. package/runner/sizes.js +37 -37
  48. package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
  49. package/compiler/builtins/base64.js +0 -92
  50. package/runner/info.js +0 -89
  51. package/runner/profile.js +0 -46
  52. package/runner/results.json +0 -1
  53. package/runner/transform.js +0 -15
  54. package/util/enum.js +0 -20
package/compiler/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { underline, bold, log } from './log.js';
2
2
  import parse from './parse.js';
3
- import codeGen from './codeGen.js';
3
+ import codegen from './codegen.js';
4
4
  import opt from './opt.js';
5
- import produceSections from './sections.js';
5
+ import assemble from './assemble.js';
6
6
  import decompile from './decompile.js';
7
- import { BuiltinPreludes } from './builtins.js';
8
7
  import toc from './2c.js';
8
+ import Prefs from './prefs.js';
9
9
 
10
10
  globalThis.decompile = decompile;
11
11
 
@@ -26,79 +26,82 @@ const logFuncs = (funcs, globals, exceptions) => {
26
26
  console.log();
27
27
  };
28
28
 
29
- const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
30
-
31
- const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
32
- const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
29
+ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs')) : undefined);
30
+ const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
33
31
 
34
32
  export default (code, flags) => {
35
- globalThis.optLog = process.argv.includes('-opt-log');
36
- globalThis.codeLog = process.argv.includes('-code-log');
37
- globalThis.allocLog = process.argv.includes('-alloc-log');
38
- globalThis.regexLog = process.argv.includes('-regex-log');
39
-
40
- for (const x in BuiltinPreludes) {
41
- if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
42
- }
43
-
44
33
  const t0 = performance.now();
45
34
  const program = parse(code, flags);
46
- if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
35
+ if (Prefs.profileCompiler) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
47
36
 
48
37
  const t1 = performance.now();
49
- const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
50
- if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
38
+ const { funcs, globals, tags, exceptions, pages, data } = codegen(program);
39
+ if (Prefs.profileCompiler) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
51
40
 
52
- if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
41
+ if (Prefs.funcs) logFuncs(funcs, globals, exceptions);
53
42
 
54
43
  const t2 = performance.now();
55
- opt(funcs, globals, pages);
56
- if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
44
+ opt(funcs, globals, pages, tags, exceptions);
45
+ if (Prefs.profileCompiler) console.log(`3. optimized in ${(performance.now() - t2).toFixed(2)}ms`);
57
46
 
58
- if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
47
+ if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
59
48
 
60
49
  const t3 = performance.now();
61
- const sections = produceSections(funcs, globals, tags, pages, data, flags);
62
- if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
50
+ const wasm = assemble(funcs, globals, tags, pages, data, flags);
51
+ if (Prefs.profileCompiler) console.log(`4. assembled in ${(performance.now() - t3).toFixed(2)}ms`);
63
52
 
64
- if (allocLog) {
53
+ if (Prefs.allocLog) {
65
54
  const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
66
55
  const bytes = wasmPages * 65536;
67
56
  log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
68
57
  console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
69
58
  }
70
59
 
71
- const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
60
+ const out = { wasm, funcs, globals, tags, exceptions, pages, data };
61
+
62
+ const target = Prefs.target ?? 'wasm';
63
+ const outFile = Prefs.o;
72
64
 
73
- const target = getArg('target') ?? getArg('t') ?? 'wasm';
74
- const outFile = getArg('o');
65
+ if (target === 'wasm' && outFile) {
66
+ fs.writeFileSync(outFile, Buffer.from(wasm));
67
+
68
+ if (process.version) process.exit();
69
+ }
75
70
 
76
71
  if (target === 'c') {
77
72
  const c = toc(out);
73
+ out.c = c;
78
74
 
79
75
  if (outFile) {
80
- writeFileSync(outFile, c);
76
+ fs.writeFileSync(outFile, c);
81
77
  } else {
82
78
  console.log(c);
83
79
  }
84
80
 
85
- process.exit();
81
+ if (process.version) process.exit();
86
82
  }
87
83
 
88
84
  if (target === 'native') {
89
- const compiler = getArg('compiler') ?? 'clang';
90
- const cO = getArg('cO') ?? 'Ofast';
85
+ let compiler = Prefs.compiler ?? 'clang';
86
+ const cO = Prefs._cO ?? 'Ofast';
91
87
 
92
- const tmpfile = 'tmp.c';
93
- const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
88
+ if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
89
+ else compiler = [ compiler ];
90
+
91
+ const tmpfile = 'porffor_tmp.c';
92
+ // const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-fno-unwind-tables', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl', '-fno-ident', '-fno-exceptions', '-ffast-math' ];
93
+ // const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-target', 'x86_64-linux' ];
94
+ const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
94
95
 
95
96
  const c = toc(out);
96
- writeFileSync(tmpfile, c);
97
+ fs.writeFileSync(tmpfile, c);
97
98
 
98
99
  // obvious command escape is obvious
99
100
  execSync(args.join(' '), { stdio: 'inherit' });
100
101
 
101
- process.exit();
102
+ fs.unlinkSync(tmpfile);
103
+
104
+ if (process.version) process.exit();
102
105
  }
103
106
 
104
107
  return out;
package/compiler/log.js CHANGED
@@ -5,11 +5,14 @@ export const bold = x => `\u001b[1m${x}\u001b[0m`;
5
5
  const areaColors = {
6
6
  codegen: [ 20, 80, 250 ],
7
7
  opt: [ 250, 20, 80 ],
8
- sections: [ 20, 250, 80 ],
8
+ assemble: [ 20, 250, 80 ],
9
9
  alloc: [ 250, 250, 20 ],
10
- parser: [ 240, 240, 240 ],
11
- '2c': [ 20, 250, 250 ]
10
+ parse: [ 240, 240, 240 ],
11
+ '2c': [ 20, 250, 250 ],
12
+ wrap: [ 250, 100, 20 ]
12
13
  };
13
14
 
15
+ // for (const x in areaColors) console.log(rgb(areaColors[x][0], areaColors[x][1], areaColors[x][2], x));
16
+
14
17
  export const log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
15
18
  log.warning = (area, ...args) => log(area, '\u001b[93m' + args[0], ...args.slice(1), '\u001b[0m');
package/compiler/opt.js CHANGED
@@ -2,6 +2,7 @@ import { Opcodes, Valtype } from "./wasmSpec.js";
2
2
  import { number } from "./embedding.js";
3
3
  import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
4
4
  import { log } from "./log.js";
5
+ import Prefs from './prefs.js';
5
6
 
6
7
  const performWasmOp = (op, a, b) => {
7
8
  switch (op) {
@@ -11,21 +12,21 @@ const performWasmOp = (op, a, b) => {
11
12
  }
12
13
  };
13
14
 
14
- export default (funcs, globals, pages) => {
15
+ export default (funcs, globals, pages, tags, exceptions) => {
15
16
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
16
17
  if (optLevel === 0) return;
17
18
 
18
- const tailCall = process.argv.includes('-tail-call');
19
+ const tailCall = Prefs.tailCall;
19
20
  if (tailCall) log.warning('opt', 'tail call proposal is not widely implemented! (you used -tail-call)');
20
21
 
21
- if (optLevel >= 2 && !process.argv.includes('-opt-no-inline')) {
22
+ if (optLevel >= 2 && !Prefs.optNoInline) {
22
23
  // inline pass (very WIP)
23
24
  // get candidates for inlining
24
25
  // todo: pick smart in future (if func is used <N times? or?)
25
26
  const callsSelf = f => f.wasm.some(x => x[0] === Opcodes.call && x[1] === f.index);
26
27
  const suitableReturns = wasm => wasm.reduce((acc, x) => acc + (x[0] === Opcodes.return), 0) <= 1;
27
28
  const candidates = funcs.filter(x => x.name !== 'main' && Object.keys(x.locals).length === x.params.length && (x.returns.length === 0 || suitableReturns(x.wasm)) && !callsSelf(x) && !x.throws).reverse();
28
- if (optLog) {
29
+ if (Prefs.optLog) {
29
30
  log('opt', `found inline candidates: ${candidates.map(x => x.name).join(', ')} (${candidates.length}/${funcs.length - 1})`);
30
31
 
31
32
  let reasons = {};
@@ -53,7 +54,7 @@ export default (funcs, globals, pages) => {
53
54
  for (let i = 0; i < tWasm.length; i++) {
54
55
  const inst = tWasm[i];
55
56
  if (inst[0] === Opcodes.call && inst[1] === c.index) {
56
- if (optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
57
+ if (Prefs.optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
57
58
  tWasm.splice(i, 1); // remove this call
58
59
 
59
60
  // add params as locals and set in reverse order
@@ -80,7 +81,7 @@ export default (funcs, globals, pages) => {
80
81
  // adjust local operands to go to correct param index
81
82
  for (const inst of iWasm) {
82
83
  if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) && inst[1] < c.params.length) {
83
- if (optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
84
+ if (Prefs.optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
84
85
  inst[1] = paramIdx[inst[1]];
85
86
  }
86
87
  }
@@ -97,13 +98,19 @@ export default (funcs, globals, pages) => {
97
98
  }
98
99
  }
99
100
 
100
- if (process.argv.includes('-opt-inline-only')) return;
101
+ if (Prefs.optInlineOnly) return;
102
+
103
+ // todo: this breaks exceptions after due to indexes not being adjusted
104
+ // const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
105
+ // const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
101
106
 
102
107
  // wasm transform pass
103
108
  for (const f of funcs) {
104
109
  const wasm = f.wasm;
105
110
 
106
- let runs = 2; // how many by default? add arg?
111
+ const lastType = f.locals['#last_type'];
112
+
113
+ let runs = (+Prefs.optWasmRuns) || 2; // todo: how many by default?
107
114
  while (runs > 0) {
108
115
  runs--;
109
116
 
@@ -125,6 +132,13 @@ export default (funcs, globals, pages) => {
125
132
  if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
126
133
  if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
127
134
 
135
+ // if (inst[0] === Opcodes.throw) {
136
+ // tagUse[inst[1]]++;
137
+
138
+ // const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
139
+ // exceptionUse[exceptId]++;
140
+ // }
141
+
128
142
  if (inst[0] === Opcodes.block) {
129
143
  // remove unneeded blocks (no brs inside)
130
144
  // block
@@ -141,7 +155,7 @@ export default (funcs, globals, pages) => {
141
155
  depth--;
142
156
  if (depth <= 0) break;
143
157
  }
144
- if (op === Opcodes.br || op === Opcodes.br_if) {
158
+ if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
145
159
  hasBranch = true;
146
160
  break;
147
161
  }
@@ -154,18 +168,18 @@ export default (funcs, globals, pages) => {
154
168
 
155
169
  wasm.splice(j - 1, 1); // remove end of this block
156
170
 
157
- if (optLog) log('opt', `removed unneeded block in for loop`);
171
+ if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
158
172
  }
159
173
  }
160
174
 
161
- if (inst[inst.length - 1] === 'string_only' && !pages.hasString) {
175
+ if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString) {
162
176
  // remove this inst
163
177
  wasm.splice(i, 1);
164
178
  if (i > 0) i--;
165
179
  inst = wasm[i];
166
180
  }
167
181
 
168
- if (inst[inst.length - 1] === 'string_only|start' && !pages.hasString) {
182
+ if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString) {
169
183
  let j = i;
170
184
  for (; j < wasm.length; j++) {
171
185
  const op = wasm[j];
@@ -186,6 +200,7 @@ export default (funcs, globals, pages) => {
186
200
  let missing = false;
187
201
  if (type === 'Array') missing = !pages.hasArray;
188
202
  if (type === 'String') missing = !pages.hasString;
203
+ if (type === 'ByteString') missing = !pages.hasByteString;
189
204
 
190
205
  if (missing) {
191
206
  let j = i, depth = 0;
@@ -202,7 +217,7 @@ export default (funcs, globals, pages) => {
202
217
  i -= 4;
203
218
  inst = wasm[i];
204
219
 
205
- if (optLog) log('opt', `removed unneeded typeswitch check`);
220
+ if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
206
221
  }
207
222
  }
208
223
 
@@ -221,16 +236,24 @@ export default (funcs, globals, pages) => {
221
236
  }
222
237
 
223
238
  if (checks === 0) {
239
+ // todo: review indexes below
224
240
  wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
225
241
  wasm.splice(i - 1, 1); // remove this inst
226
242
 
227
- if (optLog) log('opt', 'removed unneeded entire typeswitch');
243
+ if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
228
244
 
229
245
  if (i > 0) i--;
230
246
  continue;
231
247
  }
232
248
  }
233
249
 
250
+ // remove setting last type if it is never gotten
251
+ if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
252
+ // replace this inst with drop
253
+ wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
254
+ if (i > 0) i--;
255
+ }
256
+
234
257
  if (i < 1) continue;
235
258
  let lastInst = wasm[i - 1];
236
259
 
@@ -246,7 +269,7 @@ export default (funcs, globals, pages) => {
246
269
 
247
270
  getCount[inst[1]]--;
248
271
  i--;
249
- // if (optLog) log('opt', `consolidated set, get -> tee`);
272
+ // if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
250
273
  continue;
251
274
  }
252
275
 
@@ -314,7 +337,7 @@ export default (funcs, globals, pages) => {
314
337
 
315
338
  wasm.splice(i - 1, 2); // remove this inst and last
316
339
  i -= 2;
317
- // if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
340
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
318
341
  continue;
319
342
  }
320
343
 
@@ -327,7 +350,7 @@ export default (funcs, globals, pages) => {
327
350
 
328
351
  wasm.splice(i - 1, 2); // remove this inst and last
329
352
  i -= 2;
330
- // if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
353
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
331
354
  continue;
332
355
  }
333
356
 
@@ -342,7 +365,7 @@ export default (funcs, globals, pages) => {
342
365
 
343
366
  wasm.splice(i, 1); // remove this inst
344
367
  i--;
345
- if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
368
+ if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
346
369
  continue;
347
370
  }
348
371
 
@@ -357,7 +380,7 @@ export default (funcs, globals, pages) => {
357
380
 
358
381
  wasm.splice(i, 1); // remove this inst
359
382
  i--;
360
- if (optLog) log('opt', `converted i32 const -> convert into const`);
383
+ if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
361
384
  continue;
362
385
  }
363
386
 
@@ -372,7 +395,7 @@ export default (funcs, globals, pages) => {
372
395
 
373
396
  wasm.splice(i, 1); // remove this inst (return)
374
397
  i--;
375
- if (optLog) log('opt', `tail called return, call`);
398
+ if (Prefs.optLog) log('opt', `tail called return, call`);
376
399
  continue;
377
400
  }
378
401
 
@@ -385,7 +408,7 @@ export default (funcs, globals, pages) => {
385
408
 
386
409
  wasm.splice(i, 1); // remove this inst (return)
387
410
  i--;
388
- // if (optLog) log('opt', `removed redundant return at end`);
411
+ // if (Prefs.optLog) log('opt', `removed redundant return at end`);
389
412
  continue;
390
413
  }
391
414
 
@@ -415,7 +438,7 @@ export default (funcs, globals, pages) => {
415
438
  // <nothing>
416
439
 
417
440
  wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
418
- if (optLog) log('opt', `removed redundant inline param local handling`);
441
+ if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
419
442
  i -= 3;
420
443
  continue;
421
444
  }
@@ -423,12 +446,12 @@ export default (funcs, globals, pages) => {
423
446
 
424
447
  if (optLevel < 2) continue;
425
448
 
426
- if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
449
+ if (Prefs.optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
427
450
 
428
451
  // remove unneeded var: remove pass
429
452
  // 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
430
453
  let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
431
- if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
454
+ if (Prefs.optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
432
455
 
433
456
  // note: disabled for now due to instability
434
457
  if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
@@ -446,7 +469,7 @@ export default (funcs, globals, pages) => {
446
469
  wasm.splice(i - 1, 2); // remove insts
447
470
  i -= 2;
448
471
  delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
449
- if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
472
+ if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
450
473
  }
451
474
 
452
475
  if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
@@ -474,7 +497,7 @@ export default (funcs, globals, pages) => {
474
497
  unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
475
498
  unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
476
499
 
477
- if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
500
+ if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
478
501
  }
479
502
  }
480
503
 
@@ -510,7 +533,7 @@ export default (funcs, globals, pages) => {
510
533
  let b = lastInst[1];
511
534
 
512
535
  const val = performWasmOp(inst[0], a, b);
513
- if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
536
+ if (Prefs.optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
514
537
 
515
538
  wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
516
539
  i -= 2;
@@ -522,14 +545,27 @@ export default (funcs, globals, pages) => {
522
545
  for (const x in useCount) {
523
546
  if (useCount[x] === 0) {
524
547
  const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
525
- if (optLog) log('opt', `removed internal local ${x} (${name})`);
548
+ if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
526
549
  delete f.locals[name];
527
550
  }
528
551
  }
529
552
 
530
- if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
553
+ if (Prefs.optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
531
554
  }
532
555
  }
533
556
 
557
+ // for (const x in tagUse) {
558
+ // if (tagUse[x] === 0) {
559
+ // const el = tags.find(y => y.idx === x);
560
+ // tags.splice(tags.indexOf(el), 1);
561
+ // }
562
+ // }
563
+
564
+ // for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
565
+ // if (exceptionUse[x] === 0) {
566
+ // exceptions.splice(+x, 1);
567
+ // }
568
+ // }
569
+
534
570
  // return funcs;
535
571
  };
package/compiler/parse.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { log } from "./log.js";
2
+ import Prefs from './prefs.js';
3
+
4
+ // import { parse } from 'acorn';
2
5
 
3
6
  // deno compat
4
7
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -6,26 +9,54 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
6
9
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
7
10
  }
8
11
 
9
- // import { parse } from 'acorn';
12
+ // should we try to support types (while parsing)
13
+ const types = Prefs.parseTypes;
14
+ globalThis.typedInput = types && Prefs.optTypes;
10
15
 
11
16
  // todo: review which to use by default
12
- const parser = process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? 'acorn';
13
- const { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
14
-
15
17
  // supported parsers:
16
18
  // - acorn
17
19
  // - meriyah
18
20
  // - hermes-parser
19
21
  // - @babel/parser
20
22
 
21
- // should we try to support types (while parsing)
22
- const types = process.argv.includes('-types');
23
+ globalThis.parser = '';
24
+ let parse;
25
+ const loadParser = async (fallbackParser = 'acorn', forceParser) => {
26
+ parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
27
+ 0, { parse } = (await import((globalThis.document || globalThis.Deno ? 'https://esm.sh/' : '') + parser));
28
+ };
29
+ globalThis._porf_loadParser = loadParser;
30
+ await loadParser(types ? '@babel/parser' : undefined);
23
31
 
24
- if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
32
+ if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parse', `passed -parse-types with a parser (${parser}) which does not support`);
25
33
 
26
34
  export default (input, flags) => {
27
- return parse(input, {
28
- ecmaVersion: 'latest',
29
- sourceType: flags.includes('module') ? 'module' : 'script'
30
- });
35
+ try {
36
+ const ast = parse(input, {
37
+ // acorn
38
+ ecmaVersion: 'latest',
39
+
40
+ // meriyah
41
+ next: true,
42
+ module: flags.includes('module'),
43
+ webcompat: true,
44
+
45
+ // babel
46
+ plugins: types ? ['estree', 'typescript'] : ['estree'],
47
+
48
+ // multiple
49
+ sourceType: flags.includes('module') ? 'module' : 'script',
50
+ ranges: false,
51
+ tokens: false,
52
+ comments: false,
53
+ });
54
+
55
+ if (ast.type === 'File') return ast.program;
56
+
57
+ return ast;
58
+ } catch (e) {
59
+ // normalize error class thrown by 3rd party parsers
60
+ throw new SyntaxError(e.message, { cause: e });
61
+ }
31
62
  };
@@ -0,0 +1,123 @@
1
+ import { Opcodes } from './wasmSpec.js';
2
+ import { TYPES } from './types.js';
3
+
4
+ import fs from 'node:fs';
5
+ import { join } from 'node:path';
6
+
7
+ import { fileURLToPath } from 'node:url';
8
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
9
+
10
+ // import porfParse from './parse.js';
11
+ // import porfCodegen from './codeGen.js';
12
+
13
+ const argv = process.argv.slice();
14
+
15
+ const compile = async (file, [ _funcs, _globals ]) => {
16
+ let source = fs.readFileSync(file, 'utf8');
17
+ let first = source.slice(0, source.indexOf('\n'));
18
+
19
+ if (first.startsWith('export default')) {
20
+ source = (await import(file)).default();
21
+ first = source.slice(0, source.indexOf('\n'));
22
+ }
23
+
24
+ let args = ['-bytestring', '-todo-time=compile', '-no-aot-pointer-opt', '-scoped-page-names', '-parse-types', '-opt-types'];
25
+ if (first.startsWith('// @porf')) {
26
+ args = args.concat(first.slice('// @porf '.length).split(' '));
27
+ }
28
+ process.argv = argv.concat(args);
29
+
30
+ // const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
31
+ // const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
32
+
33
+ // let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
34
+
35
+ const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
36
+
37
+ let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
38
+
39
+ const allocated = new Set();
40
+
41
+ const exports = funcs.filter(x => x.export);
42
+ for (const x of exports) {
43
+ if (x.data) {
44
+ x.data = x.data.map(x => data[x]);
45
+ for (const y in x.data) {
46
+ x.data[y].offset -= x.data[0].offset;
47
+ }
48
+ }
49
+ if (x.exceptions) x.exceptions = x.exceptions.map(x => {
50
+ const obj = exceptions[x];
51
+ if (obj) obj.exceptId = x;
52
+ return obj;
53
+ }).filter(x => x);
54
+
55
+ const locals = Object.keys(x.locals).reduce((acc, y) => {
56
+ acc[x.locals[y].idx] = { ...x.locals[y], name: y };
57
+ return acc;
58
+ }, {});
59
+
60
+ for (let i = 0; i < x.wasm.length; i++) {
61
+ const y = x.wasm[i];
62
+ const n = x.wasm[i + 1];
63
+ if (y[0] === Opcodes.call) {
64
+ const f = funcs.find(x => x.index === y[1]);
65
+ if (!f) continue;
66
+
67
+ y[1] = f.name;
68
+ }
69
+
70
+ if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
71
+ const l = locals[n[1]];
72
+ if (!l) continue;
73
+ if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
74
+ if (!x.pages) continue;
75
+
76
+ const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
77
+ if (!pageName || allocated.has(pageName)) continue;
78
+ allocated.add(pageName);
79
+
80
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
81
+ // y.push(x.pages.get(pageName));
82
+ }
83
+ }
84
+ }
85
+
86
+ _funcs.push(...exports);
87
+ _globals.push(...Object.values(globals));
88
+ };
89
+
90
+ const precompile = async () => {
91
+ const dir = join(__dirname, 'builtins');
92
+
93
+ let funcs = [], globals = [];
94
+ for (const file of fs.readdirSync(dir)) {
95
+ if (file.endsWith('.d.ts')) continue;
96
+ await compile(join(dir, file), [ funcs, globals ]);
97
+ }
98
+
99
+ // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
100
+ // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
101
+
102
+ return `// autogenerated by compiler/precompile.js
103
+ import { number } from './embedding.js';
104
+
105
+ export const BuiltinFuncs = function() {
106
+ ${funcs.map(x => ` this.${x.name} = {
107
+ wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)",(.*?)\]/g, (_, reason, type, valtype) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtype})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
108
+ params: ${JSON.stringify(x.params)},
109
+ typedParams: true,
110
+ returns: ${JSON.stringify(x.returns)},
111
+ ${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: true'},
112
+ locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
113
+ localNames: ${JSON.stringify(Object.keys(x.locals))},
114
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
115
+ ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
116
+ };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')).join('\n')}
117
+ };`;
118
+ };
119
+
120
+ const code = await precompile();
121
+ // console.log(code);
122
+
123
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);