porffor 0.2.0-5e33105 → 0.2.0-6aff0fa

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,15 @@
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
+ };
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, tags);
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, tags) => {
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, tags) => {
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, tags) => {
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,9 +98,10 @@ export default (funcs, globals, pages, tags) => {
97
98
  }
98
99
  }
99
100
 
100
- if (process.argv.includes('-opt-inline-only')) return;
101
+ if (Prefs.optInlineOnly) return;
101
102
 
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; }, {});
103
105
 
104
106
  // wasm transform pass
105
107
  for (const f of funcs) {
@@ -129,7 +131,12 @@ export default (funcs, globals, pages, tags) => {
129
131
  if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
130
132
  if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
131
133
 
132
- if (inst[0] === Opcodes.throw) tagUse[inst[1]]++;
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
+ }
133
140
 
134
141
  if (inst[0] === Opcodes.block) {
135
142
  // remove unneeded blocks (no brs inside)
@@ -160,7 +167,7 @@ export default (funcs, globals, pages, tags) => {
160
167
 
161
168
  wasm.splice(j - 1, 1); // remove end of this block
162
169
 
163
- if (optLog) log('opt', `removed unneeded block in for loop`);
170
+ if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
164
171
  }
165
172
  }
166
173
 
@@ -192,6 +199,7 @@ export default (funcs, globals, pages, tags) => {
192
199
  let missing = false;
193
200
  if (type === 'Array') missing = !pages.hasArray;
194
201
  if (type === 'String') missing = !pages.hasString;
202
+ if (type === 'ByteString') missing = !pages.hasByteString;
195
203
 
196
204
  if (missing) {
197
205
  let j = i, depth = 0;
@@ -208,7 +216,7 @@ export default (funcs, globals, pages, tags) => {
208
216
  i -= 4;
209
217
  inst = wasm[i];
210
218
 
211
- if (optLog) log('opt', `removed unneeded typeswitch check`);
219
+ if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
212
220
  }
213
221
  }
214
222
 
@@ -231,7 +239,7 @@ export default (funcs, globals, pages, tags) => {
231
239
  wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
232
240
  wasm.splice(i - 1, 1); // remove this inst
233
241
 
234
- if (optLog) log('opt', 'removed unneeded entire typeswitch');
242
+ if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
235
243
 
236
244
  if (i > 0) i--;
237
245
  continue;
@@ -260,7 +268,7 @@ export default (funcs, globals, pages, tags) => {
260
268
 
261
269
  getCount[inst[1]]--;
262
270
  i--;
263
- // if (optLog) log('opt', `consolidated set, get -> tee`);
271
+ // if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
264
272
  continue;
265
273
  }
266
274
 
@@ -328,7 +336,7 @@ export default (funcs, globals, pages, tags) => {
328
336
 
329
337
  wasm.splice(i - 1, 2); // remove this inst and last
330
338
  i -= 2;
331
- // if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
339
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
332
340
  continue;
333
341
  }
334
342
 
@@ -341,7 +349,7 @@ export default (funcs, globals, pages, tags) => {
341
349
 
342
350
  wasm.splice(i - 1, 2); // remove this inst and last
343
351
  i -= 2;
344
- // if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
352
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
345
353
  continue;
346
354
  }
347
355
 
@@ -356,7 +364,7 @@ export default (funcs, globals, pages, tags) => {
356
364
 
357
365
  wasm.splice(i, 1); // remove this inst
358
366
  i--;
359
- if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
367
+ if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
360
368
  continue;
361
369
  }
362
370
 
@@ -371,7 +379,7 @@ export default (funcs, globals, pages, tags) => {
371
379
 
372
380
  wasm.splice(i, 1); // remove this inst
373
381
  i--;
374
- if (optLog) log('opt', `converted i32 const -> convert into const`);
382
+ if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
375
383
  continue;
376
384
  }
377
385
 
@@ -386,7 +394,7 @@ export default (funcs, globals, pages, tags) => {
386
394
 
387
395
  wasm.splice(i, 1); // remove this inst (return)
388
396
  i--;
389
- if (optLog) log('opt', `tail called return, call`);
397
+ if (Prefs.optLog) log('opt', `tail called return, call`);
390
398
  continue;
391
399
  }
392
400
 
@@ -399,7 +407,7 @@ export default (funcs, globals, pages, tags) => {
399
407
 
400
408
  wasm.splice(i, 1); // remove this inst (return)
401
409
  i--;
402
- // if (optLog) log('opt', `removed redundant return at end`);
410
+ // if (Prefs.optLog) log('opt', `removed redundant return at end`);
403
411
  continue;
404
412
  }
405
413
 
@@ -429,7 +437,7 @@ export default (funcs, globals, pages, tags) => {
429
437
  // <nothing>
430
438
 
431
439
  wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
432
- if (optLog) log('opt', `removed redundant inline param local handling`);
440
+ if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
433
441
  i -= 3;
434
442
  continue;
435
443
  }
@@ -437,12 +445,12 @@ export default (funcs, globals, pages, tags) => {
437
445
 
438
446
  if (optLevel < 2) continue;
439
447
 
440
- 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(', ')}`);
441
449
 
442
450
  // remove unneeded var: remove pass
443
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
444
452
  let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
445
- 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})`);
446
454
 
447
455
  // note: disabled for now due to instability
448
456
  if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
@@ -460,7 +468,7 @@ export default (funcs, globals, pages, tags) => {
460
468
  wasm.splice(i - 1, 2); // remove insts
461
469
  i -= 2;
462
470
  delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
463
- if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
471
+ if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
464
472
  }
465
473
 
466
474
  if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
@@ -488,7 +496,7 @@ export default (funcs, globals, pages, tags) => {
488
496
  unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
489
497
  unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
490
498
 
491
- if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
499
+ if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
492
500
  }
493
501
  }
494
502
 
@@ -524,7 +532,7 @@ export default (funcs, globals, pages, tags) => {
524
532
  let b = lastInst[1];
525
533
 
526
534
  const val = performWasmOp(inst[0], a, b);
527
- 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})`);
528
536
 
529
537
  wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
530
538
  i -= 2;
@@ -536,12 +544,12 @@ export default (funcs, globals, pages, tags) => {
536
544
  for (const x in useCount) {
537
545
  if (useCount[x] === 0) {
538
546
  const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
539
- if (optLog) log('opt', `removed internal local ${x} (${name})`);
547
+ if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
540
548
  delete f.locals[name];
541
549
  }
542
550
  }
543
551
 
544
- 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(', ')}`);
545
553
  }
546
554
  }
547
555
 
@@ -552,5 +560,11 @@ export default (funcs, globals, pages, tags) => {
552
560
  }
553
561
  }
554
562
 
563
+ for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
564
+ if (exceptionUse[x] === 0) {
565
+ exceptions.splice(+x, 1);
566
+ }
567
+ }
568
+
555
569
  // return funcs;
556
570
  };
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;