porffor 0.2.0-50b82f8 → 0.2.0-5ac7ea0

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.
@@ -15,7 +15,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
15
15
  if (name) out += `${makeSignature(params, returns)} ;; $${name} (${ind})\n`;
16
16
 
17
17
  const justLocals = Object.values(locals).sort((a, b) => a.idx - b.idx).slice(params.length);
18
- if (justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
18
+ if (name && justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
19
19
 
20
20
  let i = -1, lastInst;
21
21
  let byte = 0;
@@ -32,7 +32,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
32
32
  inst = [ [ inst[0], inst[1] ], ...inst.slice(2) ];
33
33
  }
34
34
 
35
- if (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all) depth--;
35
+ if (depth > 0 && (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all)) depth--;
36
36
 
37
37
  out += ' '.repeat(Math.max(0, depth * 2));
38
38
 
@@ -119,7 +119,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
119
119
  export const highlightAsm = asm => asm
120
120
  .replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
121
121
  .replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
122
- .replace(/[^m](i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `${_[0]}\x1B[36m${_.slice(1)}\x1B[0m`)
122
+ .replace(/(i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `\x1B[36m${_}\x1B[0m`)
123
123
  .replace(/(return_call|call|br_if|br|return|rethrow|throw)/g, _ => `\x1B[35m${_}\x1B[0m`)
124
124
  .replace(/(block|loop|if|end|else|try|catch_all|catch|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
125
125
  .replace(/unreachable/g, _ => `\x1B[91m${_}\x1B[0m`)
@@ -0,0 +1,25 @@
1
+ // autogenerated by compiler/precompile.js
2
+ import { number } from './embedding.js';
3
+
4
+ export const BuiltinFuncs = function() {
5
+ this.btoa = {
6
+ wasm: (scope, { allocPage, builtin }) => [...number(allocPage(scope, 'bytestring: keyStr', 'i8') * pageSize, 127),[34,2],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,3],[32,0],[40,1,0],[33,4],...number(allocPage(scope, 'bytestring: output', 'i8') * pageSize, 127),[33,5],[65,0],[65,4],[32,4],[65,3],[109],[32,4],[65,3],[111],[69],[69],[106],[108],[34,6],[54,1,128,128,4],[32,0],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,7],[32,5],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,8],[32,7],[32,4],[106],[33,9],[65,0],[33,10],[3,64],[32,7],[32,9],[72],[4,64],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[33,11],[32,7],[32,9],[72],[4,127],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[65,0],[33,13],[5],[65,127],[65,0],[33,13],[11],[33,12],[32,7],[32,9],[72],[4,127],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[65,0],[33,13],[5],[65,127],[65,0],[33,13],[11],[33,14],[32,11],[65,2],[117],[33,15],[32,11],[65,3],[113],[65,4],[116],[32,12],[65,127],[70],[4,127],[65,0],[65,0],[33,13],[5],[32,12],[65,4],[117],[65,0],[33,13],[11],[114],[33,16],[32,12],[65,15],[113],[65,2],[116],[32,14],[65,127],[70],[4,127],[65,0],[65,0],[33,13],[5],[32,14],[65,6],[117],[65,0],[33,13],[11],[114],[33,17],[32,14],[65,63],[113],[33,18],[32,12],[65,127],[70],[4,64],[65,192,0],[33,17],[65,192,0],[33,18],[5],[32,14],[65,127],[70],[4,64],[65,192,0],[33,18],[11],[11],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,15],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,16],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,17],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,18],[106],[45,0,4],[58,0,4],[12,1],[11],[11],[32,5],[65,18],[15]],
7
+ params: [127,127],
8
+ typedParams: true,
9
+ returns: [127,127],
10
+ typedReturns: true,
11
+ locals: [127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127],
12
+ localNames: ["input","input#type","keyStr","keyStrPtr","len","output","__length_setter_tmp","i","j","endPtr","endPtr#type","chr1","chr2","#last_type","chr3","enc1","enc2","enc3","enc4"],
13
+ data: [{"offset":0,"bytes":[65,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,43,47,61]}],
14
+ };
15
+ this.__crypto_randomUUID = {
16
+ wasm: (scope, { allocPage, builtin }) => [...number(allocPage(scope, 'bytestring: bytes', 'i8') * pageSize, 127),[34,0],[16, builtin('__Porffor_i32_ptrUnsafe')],[34,1],[34,2],[65,16],[106],[33,3],[3,64],[32,2],[32,3],[72],[4,64],[32,2],[32,2],[65,1],[106],[33,2],[16, builtin('__Porffor_i32_randomByte')],[58,0,4],[12,1],[11],[11],[32,1],[32,1],[45,0,10],[65,15],[113],[65,192,0],[114],[58,0,10],[32,1],[32,1],[45,0,12],[65,63],[113],[65,128,1],[114],[58,0,12],...number(allocPage(scope, 'bytestring: output', 'i8') * pageSize, 127),[34,4],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,5],[32,1],[33,6],[32,5],[65,8],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,12],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[33,5],[32,4],[65,18],[15]],
17
+ params: [],
18
+ typedParams: true,
19
+ returns: [127,127],
20
+ typedReturns: true,
21
+ locals: [127,127,127,127,127,127,127,127,127,127,127,127,127,127],
22
+ localNames: ["bytes","bytesPtr","a","aEndPtr","output","i","j","endPtr","byte","byte#type","lower","lower#type","upper","upper#type"],
23
+ data: [{"offset":0,"bytes":[16,0,0,0,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46]},{"offset":65536,"bytes":[36,0,0,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45]}],
24
+ };
25
+ };
package/compiler/index.js CHANGED
@@ -4,8 +4,8 @@ import codeGen from './codeGen.js';
4
4
  import opt from './opt.js';
5
5
  import produceSections from './sections.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,21 +26,10 @@ 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
29
  const writeFileSync = (typeof process?.version !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
32
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
35
  if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
@@ -49,29 +38,35 @@ export default (code, flags) => {
49
38
  const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
50
39
  if (flags.includes('info')) 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);
44
+ opt(funcs, globals, pages, tags, exceptions);
56
45
  if (flags.includes('info')) console.log(`3. optimized code 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
50
  const sections = produceSections(funcs, globals, tags, pages, data, flags);
62
51
  if (flags.includes('info')) console.log(`4. produced sections 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: sections, 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
+ writeFileSync(outFile, Buffer.from(sections));
67
+
68
+ if (process.version) process.exit();
69
+ }
75
70
 
76
71
  if (target === 'c') {
77
72
  const c = toc(out);
@@ -87,11 +82,16 @@ export default (code, flags) => {
87
82
  }
88
83
 
89
84
  if (target === 'native') {
90
- const compiler = getArg('compiler') ?? 'clang';
91
- const cO = getArg('cO') ?? 'Ofast';
85
+ let compiler = Prefs.compiler ?? 'clang';
86
+ const cO = Prefs._cO ?? 'Ofast';
87
+
88
+ if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
89
+ else compiler = [ compiler ];
92
90
 
93
91
  const tmpfile = 'tmp.c';
94
- const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
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' ];
95
95
 
96
96
  const c = toc(out);
97
97
  writeFileSync(tmpfile, c);
package/compiler/log.js CHANGED
@@ -8,8 +8,11 @@ const areaColors = {
8
8
  sections: [ 20, 250, 80 ],
9
9
  alloc: [ 250, 250, 20 ],
10
10
  parser: [ 240, 240, 240 ],
11
- '2c': [ 20, 250, 250 ]
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,7 +98,10 @@ 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
+ const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
104
+ const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
101
105
 
102
106
  // wasm transform pass
103
107
  for (const f of funcs) {
@@ -127,6 +131,13 @@ export default (funcs, globals, pages) => {
127
131
  if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
128
132
  if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
129
133
 
134
+ if (inst[0] === Opcodes.throw) {
135
+ tagUse[inst[1]]++;
136
+
137
+ const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
138
+ exceptionUse[exceptId]++;
139
+ }
140
+
130
141
  if (inst[0] === Opcodes.block) {
131
142
  // remove unneeded blocks (no brs inside)
132
143
  // block
@@ -143,7 +154,7 @@ export default (funcs, globals, pages) => {
143
154
  depth--;
144
155
  if (depth <= 0) break;
145
156
  }
146
- if (op === Opcodes.br || op === Opcodes.br_if) {
157
+ if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
147
158
  hasBranch = true;
148
159
  break;
149
160
  }
@@ -156,7 +167,7 @@ export default (funcs, globals, pages) => {
156
167
 
157
168
  wasm.splice(j - 1, 1); // remove end of this block
158
169
 
159
- if (optLog) log('opt', `removed unneeded block in for loop`);
170
+ if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
160
171
  }
161
172
  }
162
173
 
@@ -188,6 +199,7 @@ export default (funcs, globals, pages) => {
188
199
  let missing = false;
189
200
  if (type === 'Array') missing = !pages.hasArray;
190
201
  if (type === 'String') missing = !pages.hasString;
202
+ if (type === 'ByteString') missing = !pages.hasByteString;
191
203
 
192
204
  if (missing) {
193
205
  let j = i, depth = 0;
@@ -204,7 +216,7 @@ export default (funcs, globals, pages) => {
204
216
  i -= 4;
205
217
  inst = wasm[i];
206
218
 
207
- if (optLog) log('opt', `removed unneeded typeswitch check`);
219
+ if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
208
220
  }
209
221
  }
210
222
 
@@ -227,7 +239,7 @@ export default (funcs, globals, pages) => {
227
239
  wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
228
240
  wasm.splice(i - 1, 1); // remove this inst
229
241
 
230
- if (optLog) log('opt', 'removed unneeded entire typeswitch');
242
+ if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
231
243
 
232
244
  if (i > 0) i--;
233
245
  continue;
@@ -235,7 +247,7 @@ export default (funcs, globals, pages) => {
235
247
  }
236
248
 
237
249
  // remove setting last type if it is never gotten
238
- if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType.idx) {
250
+ if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
239
251
  // replace this inst with drop
240
252
  wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
241
253
  if (i > 0) i--;
@@ -256,7 +268,7 @@ export default (funcs, globals, pages) => {
256
268
 
257
269
  getCount[inst[1]]--;
258
270
  i--;
259
- // if (optLog) log('opt', `consolidated set, get -> tee`);
271
+ // if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
260
272
  continue;
261
273
  }
262
274
 
@@ -324,7 +336,7 @@ export default (funcs, globals, pages) => {
324
336
 
325
337
  wasm.splice(i - 1, 2); // remove this inst and last
326
338
  i -= 2;
327
- // if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
339
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
328
340
  continue;
329
341
  }
330
342
 
@@ -337,7 +349,7 @@ export default (funcs, globals, pages) => {
337
349
 
338
350
  wasm.splice(i - 1, 2); // remove this inst and last
339
351
  i -= 2;
340
- // if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
352
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
341
353
  continue;
342
354
  }
343
355
 
@@ -352,7 +364,7 @@ export default (funcs, globals, pages) => {
352
364
 
353
365
  wasm.splice(i, 1); // remove this inst
354
366
  i--;
355
- if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
367
+ if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
356
368
  continue;
357
369
  }
358
370
 
@@ -367,7 +379,7 @@ export default (funcs, globals, pages) => {
367
379
 
368
380
  wasm.splice(i, 1); // remove this inst
369
381
  i--;
370
- if (optLog) log('opt', `converted i32 const -> convert into const`);
382
+ if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
371
383
  continue;
372
384
  }
373
385
 
@@ -382,7 +394,7 @@ export default (funcs, globals, pages) => {
382
394
 
383
395
  wasm.splice(i, 1); // remove this inst (return)
384
396
  i--;
385
- if (optLog) log('opt', `tail called return, call`);
397
+ if (Prefs.optLog) log('opt', `tail called return, call`);
386
398
  continue;
387
399
  }
388
400
 
@@ -395,7 +407,7 @@ export default (funcs, globals, pages) => {
395
407
 
396
408
  wasm.splice(i, 1); // remove this inst (return)
397
409
  i--;
398
- // if (optLog) log('opt', `removed redundant return at end`);
410
+ // if (Prefs.optLog) log('opt', `removed redundant return at end`);
399
411
  continue;
400
412
  }
401
413
 
@@ -425,7 +437,7 @@ export default (funcs, globals, pages) => {
425
437
  // <nothing>
426
438
 
427
439
  wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
428
- if (optLog) log('opt', `removed redundant inline param local handling`);
440
+ if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
429
441
  i -= 3;
430
442
  continue;
431
443
  }
@@ -433,12 +445,12 @@ export default (funcs, globals, pages) => {
433
445
 
434
446
  if (optLevel < 2) continue;
435
447
 
436
- if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
448
+ if (Prefs.optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
437
449
 
438
450
  // remove unneeded var: remove pass
439
451
  // 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
440
452
  let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
441
- if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
453
+ if (Prefs.optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
442
454
 
443
455
  // note: disabled for now due to instability
444
456
  if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
@@ -456,7 +468,7 @@ export default (funcs, globals, pages) => {
456
468
  wasm.splice(i - 1, 2); // remove insts
457
469
  i -= 2;
458
470
  delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
459
- if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
471
+ if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
460
472
  }
461
473
 
462
474
  if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
@@ -484,7 +496,7 @@ export default (funcs, globals, pages) => {
484
496
  unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
485
497
  unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
486
498
 
487
- if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
499
+ if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
488
500
  }
489
501
  }
490
502
 
@@ -520,7 +532,7 @@ export default (funcs, globals, pages) => {
520
532
  let b = lastInst[1];
521
533
 
522
534
  const val = performWasmOp(inst[0], a, b);
523
- if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
535
+ if (Prefs.optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
524
536
 
525
537
  wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
526
538
  i -= 2;
@@ -532,12 +544,25 @@ export default (funcs, globals, pages) => {
532
544
  for (const x in useCount) {
533
545
  if (useCount[x] === 0) {
534
546
  const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
535
- if (optLog) log('opt', `removed internal local ${x} (${name})`);
547
+ if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
536
548
  delete f.locals[name];
537
549
  }
538
550
  }
539
551
 
540
- if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
552
+ 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(', ')}`);
553
+ }
554
+ }
555
+
556
+ for (const x in tagUse) {
557
+ if (tagUse[x] === 0) {
558
+ const el = tags.find(y => y.idx === x);
559
+ tags.splice(tags.indexOf(el), 1);
560
+ }
561
+ }
562
+
563
+ for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
564
+ if (exceptionUse[x] === 0) {
565
+ exceptions.splice(+x, 1);
541
566
  }
542
567
  }
543
568
 
package/compiler/parse.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { log } from "./log.js";
2
+ import Prefs from './prefs.js';
3
+
2
4
  // import { parse } from 'acorn';
3
5
 
4
6
  // deno compat
@@ -8,8 +10,8 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
8
10
  }
9
11
 
10
12
  // should we try to support types (while parsing)
11
- const types = process.argv.includes('-parse-types');
12
- globalThis.typedInput = types && process.argv.includes('-opt-types');
13
+ const types = Prefs.parseTypes;
14
+ globalThis.typedInput = types && Prefs.optTypes;
13
15
 
14
16
  // todo: review which to use by default
15
17
  // supported parsers:
@@ -0,0 +1,129 @@
1
+ import { Opcodes } from './wasmSpec.js';
2
+
3
+ import fs from 'node:fs';
4
+ import { join } from 'node:path';
5
+
6
+ import { fileURLToPath } from 'node:url';
7
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
+
9
+ const TYPES = {
10
+ number: 0x00,
11
+ boolean: 0x01,
12
+ string: 0x02,
13
+ undefined: 0x03,
14
+ object: 0x04,
15
+ function: 0x05,
16
+ symbol: 0x06,
17
+ bigint: 0x07,
18
+
19
+ // these are not "typeof" types but tracked internally
20
+ _array: 0x10,
21
+ _regexp: 0x11,
22
+ _bytestring: 0x12
23
+ };
24
+
25
+ // import porfParse from './parse.js';
26
+ // import porfCodegen from './codeGen.js';
27
+
28
+ const argv = process.argv.slice();
29
+
30
+ const compile = async (file, [ _funcs, _globals ]) => {
31
+ const source = fs.readFileSync(file, 'utf8');
32
+ const first = source.slice(0, source.indexOf('\n'));
33
+
34
+ let args = ['-bytestring'];
35
+ if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
36
+ if (first.startsWith('// @porf')) {
37
+ args = args.concat(first.slice('// @porf '.length).split(' '));
38
+ }
39
+ process.argv = argv.concat(args);
40
+
41
+ // const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
42
+ // const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
43
+
44
+ // let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
45
+
46
+ const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
47
+
48
+ let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
49
+
50
+ const allocated = new Set();
51
+
52
+ const exports = funcs.filter(x => x.export);
53
+ for (const x of exports) {
54
+ if (x.data) x.data = x.data.map(x => data[x]);
55
+ if (x.exceptions) x.exceptions = x.exceptions.map(x => {
56
+ const obj = exceptions[x];
57
+ if (obj) obj.exceptId = x;
58
+ return obj;
59
+ }).filter(x => x);
60
+
61
+ const locals = Object.keys(x.locals).reduce((acc, y) => {
62
+ acc[x.locals[y].idx] = { ...x.locals[y], name: y };
63
+ return acc;
64
+ }, {});
65
+
66
+ for (let i = 0; i < x.wasm.length; i++) {
67
+ const y = x.wasm[i];
68
+ const n = x.wasm[i + 1];
69
+ if (y[0] === Opcodes.call) {
70
+ const f = funcs.find(x => x.index === y[1]);
71
+ if (!f) continue;
72
+
73
+ y[1] = f.name;
74
+ }
75
+
76
+ if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
77
+ const l = locals[n[1]];
78
+ if (!l) continue;
79
+ if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
80
+ if (!x.pages) continue;
81
+
82
+ const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
83
+ if (!pageName || allocated.has(pageName)) continue;
84
+ allocated.add(pageName);
85
+
86
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
87
+ // y.push(x.pages.get(pageName));
88
+ }
89
+ }
90
+ }
91
+
92
+ _funcs.push(...exports);
93
+ _globals.push(...Object.values(globals));
94
+ };
95
+
96
+ const precompile = async () => {
97
+ const dir = join(__dirname, 'builtins');
98
+
99
+ let funcs = [], globals = [];
100
+ for (const file of fs.readdirSync(dir)) {
101
+ if (file.endsWith('.d.ts')) continue;
102
+ await compile(join(dir, file), [ funcs, globals ]);
103
+ }
104
+
105
+ // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
106
+ // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
107
+
108
+ return `// autogenerated by compiler/precompile.js
109
+ import { number } from './embedding.js';
110
+
111
+ export const BuiltinFuncs = function() {
112
+ ${funcs.map(x => ` this.${x.name} = {
113
+ wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtypeBinary})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
114
+ params: ${JSON.stringify(x.params)},
115
+ typedParams: true,
116
+ returns: ${JSON.stringify(x.returns)},
117
+ typedReturns: true,
118
+ locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
119
+ localNames: ${JSON.stringify(Object.keys(x.locals))},
120
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
121
+ ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
122
+ };`.replaceAll('\n\n', '\n')).join('\n')}
123
+ };`;
124
+ };
125
+
126
+ const code = await precompile();
127
+ // console.log(code);
128
+
129
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
@@ -0,0 +1,26 @@
1
+ const onByDefault = [ 'bytestring' ];
2
+
3
+ const cache = {};
4
+ const obj = new Proxy({}, {
5
+ get(_, p) {
6
+ // intentionally misses with undefined values cached
7
+ if (cache[p]) return cache[p];
8
+
9
+ return cache[p] = (() => {
10
+ // fooBar -> foo-bar
11
+ const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
+ if (process.argv.includes('-' + name)) return true;
13
+ if (process.argv.includes('-no-' + name)) return false;
14
+
15
+ const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
16
+ if (valArg) return valArg.slice(name.length + 2);
17
+
18
+ if (onByDefault.includes(p)) return true;
19
+ return undefined;
20
+ })();
21
+ }
22
+ });
23
+
24
+ obj.uncache = () => cache = {};
25
+
26
+ export default obj;