porffor 0.2.0-fdf0fc5 → 0.14.0-0ad2c8a7c

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 (48) hide show
  1. package/CONTRIBUTING.md +12 -7
  2. package/README.md +18 -15
  3. package/asur/index.js +1 -1
  4. package/byg/index.js +3 -24
  5. package/compiler/2c.js +69 -55
  6. package/compiler/assemble.js +51 -11
  7. package/compiler/builtins/annexb_string.js +10 -10
  8. package/compiler/builtins/annexb_string.ts +4 -3
  9. package/compiler/builtins/array.ts +85 -9
  10. package/compiler/builtins/base64.ts +2 -1
  11. package/compiler/builtins/boolean.ts +2 -2
  12. package/compiler/builtins/console.ts +4 -0
  13. package/compiler/builtins/crypto.ts +2 -1
  14. package/compiler/builtins/date.ts +2 -3
  15. package/compiler/builtins/error.js +22 -0
  16. package/compiler/builtins/escape.ts +2 -3
  17. package/compiler/builtins/function.ts +1 -1
  18. package/compiler/builtins/int.ts +1 -1
  19. package/compiler/builtins/math.ts +410 -0
  20. package/compiler/builtins/number.ts +4 -7
  21. package/compiler/builtins/object.ts +1 -1
  22. package/compiler/builtins/porffor.d.ts +9 -8
  23. package/compiler/builtins/set.ts +197 -3
  24. package/compiler/builtins/string.ts +2 -1
  25. package/compiler/builtins/symbol.ts +62 -0
  26. package/compiler/builtins.js +30 -15
  27. package/compiler/codegen.js +641 -365
  28. package/compiler/decompile.js +7 -3
  29. package/compiler/embedding.js +2 -2
  30. package/compiler/encoding.js +0 -14
  31. package/compiler/expression.js +1 -1
  32. package/compiler/generated_builtins.js +781 -187
  33. package/compiler/index.js +5 -11
  34. package/compiler/opt.js +7 -7
  35. package/compiler/parse.js +2 -4
  36. package/compiler/precompile.js +18 -25
  37. package/compiler/prefs.js +6 -2
  38. package/compiler/prototype.js +185 -162
  39. package/compiler/wasmSpec.js +5 -0
  40. package/compiler/wrap.js +150 -90
  41. package/package.json +1 -1
  42. package/runner/compare.js +0 -1
  43. package/runner/debug.js +1 -6
  44. package/runner/index.js +5 -4
  45. package/runner/profiler.js +15 -42
  46. package/runner/repl.js +20 -10
  47. package/runner/sizes.js +2 -2
  48. package/runner/version.js +10 -8
package/compiler/index.js CHANGED
@@ -12,14 +12,7 @@ globalThis.decompile = decompile;
12
12
  const logFuncs = (funcs, globals, exceptions) => {
13
13
  console.log('\n' + underline(bold('funcs')));
14
14
 
15
- const startIndex = funcs.sort((a, b) => a.index - b.index)[0].index;
16
15
  for (const f of funcs) {
17
- console.log(`${underline(f.name)} (${f.index - startIndex})`);
18
-
19
- console.log(`params: ${f.params.map((_, i) => Object.keys(f.locals)[Object.values(f.locals).indexOf(Object.values(f.locals).find(x => x.idx === i))]).join(', ')}`);
20
- console.log(`returns: ${f.returns.length > 0 ? true : false}`);
21
- console.log(`locals: ${Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).map(x => `${x} (${f.locals[x].idx})`).join(', ')}`);
22
- console.log();
23
16
  console.log(decompile(f.wasm, f.name, f.index, f.locals, f.params, f.returns, funcs, globals, exceptions));
24
17
  }
25
18
 
@@ -44,12 +37,14 @@ export default (code, flags) => {
44
37
  opt(funcs, globals, pages, tags, exceptions);
45
38
  if (Prefs.profileCompiler) console.log(`3. optimized in ${(performance.now() - t2).toFixed(2)}ms`);
46
39
 
47
- if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
40
+ // if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
48
41
 
49
42
  const t3 = performance.now();
50
43
  const wasm = assemble(funcs, globals, tags, pages, data, flags);
51
44
  if (Prefs.profileCompiler) console.log(`4. assembled in ${(performance.now() - t3).toFixed(2)}ms`);
52
45
 
46
+ if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
47
+
53
48
  if (Prefs.allocLog) {
54
49
  const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
55
50
  const bytes = wasmPages * 65536;
@@ -89,9 +84,8 @@ export default (code, flags) => {
89
84
  else compiler = [ compiler ];
90
85
 
91
86
  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' ];
87
+ const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO ];
88
+ if (!Prefs.compiler) args.push('-flto=thin', '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-fno-ident', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl,--gc-sections');
95
89
 
96
90
  const c = toc(out);
97
91
  fs.writeFileSync(tmpfile, c);
package/compiler/opt.js CHANGED
@@ -1,7 +1,7 @@
1
- import { Opcodes, Valtype } from "./wasmSpec.js";
2
- import { number } from "./embedding.js";
3
- import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
4
- import { log } from "./log.js";
1
+ import { Opcodes, Valtype } from './wasmSpec.js';
2
+ import { number } from './embedding.js';
3
+ import { read_signedLEB128, read_ieee754_binary64 } from './encoding.js';
4
+ import { log } from './log.js';
5
5
  import Prefs from './prefs.js';
6
6
 
7
7
  const performWasmOp = (op, a, b) => {
@@ -172,14 +172,14 @@ export default (funcs, globals, pages, tags, exceptions) => {
172
172
  }
173
173
  }
174
174
 
175
- if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString) {
175
+ if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString && !Prefs.noRmUnusedTypes) {
176
176
  // remove this inst
177
177
  wasm.splice(i, 1);
178
178
  if (i > 0) i--;
179
179
  inst = wasm[i];
180
180
  }
181
181
 
182
- if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString) {
182
+ if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString&& !Prefs.noRmUnusedTypes) {
183
183
  let j = i;
184
184
  for (; j < wasm.length; j++) {
185
185
  const op = wasm[j];
@@ -193,7 +193,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
193
193
  inst = wasm[i];
194
194
  }
195
195
 
196
- if (inst[0] === Opcodes.if && typeof inst[2] === 'string') {
196
+ if (inst[0] === Opcodes.if && typeof inst[2] === 'string' && !Prefs.noRmUnusedTypes) {
197
197
  // remove unneeded typeswitch checks
198
198
 
199
199
  const type = inst[2].split('|')[1];
package/compiler/parse.js CHANGED
@@ -1,15 +1,13 @@
1
- import { log } from "./log.js";
1
+ import { log } from './log.js';
2
2
  import Prefs from './prefs.js';
3
3
 
4
- // import { parse } from 'acorn';
5
-
6
4
  // deno compat
7
5
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
8
6
  const textEncoder = new TextEncoder();
9
7
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
10
8
  }
11
9
 
12
- const file = process.argv.slice(2).find(x => x[0] !== '-');
10
+ const file = process.argv.slice(2).find(x => x[0] !== '-' && !['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(x));
13
11
 
14
12
  // should we try to support types (while parsing)
15
13
  const types = Prefs.parseTypes || file?.endsWith('.ts');
@@ -7,9 +7,6 @@ import { join } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
9
9
 
10
- // import porfParse from './parse.js';
11
- // import porfCodegen from './codeGen.js';
12
-
13
10
  const argv = process.argv.slice();
14
11
 
15
12
  const compile = async (file, [ _funcs, _globals ]) => {
@@ -21,17 +18,12 @@ const compile = async (file, [ _funcs, _globals ]) => {
21
18
  first = source.slice(0, source.indexOf('\n'));
22
19
  }
23
20
 
24
- let args = ['--bytestring', '--todo-time=compile', '--no-aot-pointer-opt', '--no-treeshake-wasm-imports', '--scoped-page-names', '--parse-types', '--opt-types'];
21
+ let args = ['--bytestring', '--todo-time=compile', '--truthy=no_nan_negative', '--no-treeshake-wasm-imports', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--fast-length', '--parse-types', '--opt-types'];
25
22
  if (first.startsWith('// @porf')) {
26
- args = args.concat(first.slice('// @porf '.length).split(' '));
23
+ args = first.slice('// @porf '.length).split(' ').concat(args);
27
24
  }
28
25
  process.argv = argv.concat(args);
29
26
 
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
27
  const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
36
28
 
37
29
  let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
@@ -78,7 +70,14 @@ const compile = async (file, [ _funcs, _globals ]) => {
78
70
  allocated.add(pageName);
79
71
 
80
72
  y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
81
- // y.push(x.pages.get(pageName));
73
+ }
74
+
75
+ if (y[0] === Opcodes.i32_const && n[0] === Opcodes.throw) {
76
+ const id = y[1];
77
+ y.splice(0, 10, 'throw', exceptions[id].constructor, exceptions[id].message);
78
+
79
+ // remove throw inst
80
+ x.wasm.splice(i + 1, 1);
82
81
  }
83
82
  }
84
83
  }
@@ -98,18 +97,14 @@ const precompile = async () => {
98
97
  await compile(join(dir, file), [ funcs, globals ]);
99
98
  }
100
99
 
101
- // const a = funcs.find(x => x.name === '__ecma262_ToUTCDTSF');
102
- // console.log(Object.values(a.locals).slice(a.params.length));
103
-
104
- // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
105
- // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
106
-
107
100
  return `// autogenerated by compiler/precompile.js
108
101
  import { number } from './embedding.js';
109
102
 
110
103
  export const BuiltinFuncs = function() {
111
- ${funcs.map(x => ` this.${x.name} = {
112
- 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}')]`)},
104
+ ${funcs.map(x => {
105
+ const wasm = 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}')]`).replace(/\["throw","(.*?)","(.*?)"\]/g, (_, constructor, message) => `...internalThrow(scope, '${constructor}', \`${message}\`)`);
106
+ return ` this.${x.name} = {
107
+ wasm: (scope, {${wasm.includes('allocPage(') ? 'allocPage,' : ''}${wasm.includes('builtin(') ? 'builtin,' : ''}${wasm.includes('internalThrow(') ? 'internalThrow,' : ''}}) => ${wasm},
113
108
  params: ${JSON.stringify(x.params)},
114
109
  typedParams: true,
115
110
  returns: ${JSON.stringify(x.returns)},
@@ -117,12 +112,10 @@ ${funcs.map(x => ` this.${x.name} = {
117
112
  locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
118
113
  localNames: ${JSON.stringify(Object.keys(x.locals))},
119
114
  ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
120
- ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
121
- };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')).join('\n')}
115
+ ${x.table ? ` table: true` : ''}
116
+ };`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n').replaceAll('\n\n', '\n');
117
+ }).join('\n')}
122
118
  };`;
123
119
  };
124
120
 
125
- const code = await precompile();
126
- // console.log(code);
127
-
128
- fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
121
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), await precompile());
package/compiler/prefs.js CHANGED
@@ -1,4 +1,4 @@
1
- const onByDefault = [ 'bytestring', 'aotPointerOpt', 'treeshakeWasmImports', 'alwaysMemory' ];
1
+ const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused' ];
2
2
 
3
3
  let cache = {};
4
4
  const obj = new Proxy({}, {
@@ -6,7 +6,7 @@ const obj = new Proxy({}, {
6
6
  // intentionally misses with undefined values cached
7
7
  if (cache[p]) return cache[p];
8
8
 
9
- return cache[p] = (() => {
9
+ const ret = (() => {
10
10
  // fooBar -> foo-bar
11
11
  const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
12
  const prefix = name.length === 1 ? '-' : '--';
@@ -19,6 +19,10 @@ const obj = new Proxy({}, {
19
19
  if (onByDefault.includes(p)) return true;
20
20
  return undefined;
21
21
  })();
22
+
23
+ // do not cache in web demo as args are changed live
24
+ if (!globalThis.document) cache[p] = ret;
25
+ return ret;
22
26
  }
23
27
  });
24
28