porffor 0.2.0-c7b7423 → 0.2.0-dcc06c8

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`)
@@ -105,119 +105,5 @@ export const read_unsignedLEB128 = _input => {
105
105
  };
106
106
 
107
107
  // ieee 754 binary64
108
-
109
- // from https://github.com/feross/ieee754
110
- // BSD 3-Clause. Copyright 2008 Fair Oaks Labs, Inc. (https://github.com/feross/ieee754/blob/master/LICENSE)
111
- export const ieee754_binary64 = value => {
112
- return [...new Uint8Array(new Float64Array([ value ]).buffer)];
113
-
114
- let isLE = true, mLen = 52, nBytes = 8, offset = 0;
115
- let buffer = new Array(nBytes).fill(0);
116
-
117
- let e, m, c
118
- let eLen = (nBytes * 8) - mLen - 1
119
- const eMax = (1 << eLen) - 1
120
- const eBias = eMax >> 1
121
- const rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
122
- let i = isLE ? 0 : (nBytes - 1)
123
- const d = isLE ? 1 : -1
124
- const s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
125
-
126
- value = Math.abs(value)
127
-
128
- if (isNaN(value) || value === Infinity) {
129
- m = isNaN(value) ? 1 : 0
130
- e = eMax
131
- } else {
132
- e = Math.floor(Math.log(value) / Math.LN2)
133
- if (value * (c = Math.pow(2, -e)) < 1) {
134
- e--
135
- c *= 2
136
- }
137
- if (e + eBias >= 1) {
138
- value += rt / c
139
- } else {
140
- value += rt * Math.pow(2, 1 - eBias)
141
- }
142
- if (value * c >= 2) {
143
- e++
144
- c /= 2
145
- }
146
-
147
- if (e + eBias >= eMax) {
148
- m = 0
149
- e = eMax
150
- } else if (e + eBias >= 1) {
151
- m = ((value * c) - 1) * Math.pow(2, mLen)
152
- e = e + eBias
153
- } else {
154
- m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
155
- e = 0
156
- }
157
- }
158
-
159
- while (mLen >= 8) {
160
- buffer[offset + i] = m & 0xff
161
- i += d
162
- m /= 256
163
- mLen -= 8
164
- }
165
-
166
- e = (e << mLen) | m
167
- eLen += mLen
168
- while (eLen > 0) {
169
- buffer[offset + i] = e & 0xff
170
- i += d
171
- e /= 256
172
- eLen -= 8
173
- }
174
-
175
- buffer[offset + i - d] |= s * 128
176
-
177
- return buffer;
178
- };
179
-
180
- export const read_ieee754_binary64 = buffer => {
181
- return new Float64Array(new Uint8Array(buffer).buffer)[0];
182
-
183
- let isLE = true, mLen = 52, nBytes = 8, offset = 0;
184
-
185
- let e, m
186
- const eLen = (nBytes * 8) - mLen - 1
187
- const eMax = (1 << eLen) - 1
188
- const eBias = eMax >> 1
189
- let nBits = -7
190
- let i = isLE ? (nBytes - 1) : 0
191
- const d = isLE ? -1 : 1
192
- let s = buffer[offset + i]
193
-
194
- i += d
195
-
196
- e = s & ((1 << (-nBits)) - 1)
197
- s >>= (-nBits)
198
- nBits += eLen
199
- while (nBits > 0) {
200
- e = (e * 256) + buffer[offset + i]
201
- i += d
202
- nBits -= 8
203
- }
204
-
205
- m = e & ((1 << (-nBits)) - 1)
206
- e >>= (-nBits)
207
- nBits += mLen
208
- while (nBits > 0) {
209
- m = (m * 256) + buffer[offset + i]
210
- i += d
211
- nBits -= 8
212
- }
213
-
214
- if (e === 0) {
215
- e = 1 - eBias
216
- } else if (e === eMax) {
217
- return m ? NaN : ((s ? -1 : 1) * Infinity)
218
- } else {
219
- m = m + Math.pow(2, mLen)
220
- e = e - eBias
221
- }
222
- return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
223
- };
108
+ export const ieee754_binary64 = value => [...new Uint8Array(new Float64Array([ value ]).buffer)];
109
+ export const read_ieee754_binary64 = buffer => new Float64Array(new Uint8Array(buffer).buffer)[0];
@@ -0,0 +1,3 @@
1
+
2
+ export const BuiltinFuncs = function() {
3
+ };
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/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,12 +98,17 @@ 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) {
104
108
  const wasm = f.wasm;
105
109
 
110
+ const lastType = f.locals['#last_type'];
111
+
106
112
  let runs = 2; // how many by default? add arg?
107
113
  while (runs > 0) {
108
114
  runs--;
@@ -125,6 +131,13 @@ export default (funcs, globals, pages) => {
125
131
  if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
126
132
  if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
127
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
+
128
141
  if (inst[0] === Opcodes.block) {
129
142
  // remove unneeded blocks (no brs inside)
130
143
  // block
@@ -141,7 +154,7 @@ export default (funcs, globals, pages) => {
141
154
  depth--;
142
155
  if (depth <= 0) break;
143
156
  }
144
- if (op === Opcodes.br || op === Opcodes.br_if) {
157
+ if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
145
158
  hasBranch = true;
146
159
  break;
147
160
  }
@@ -154,7 +167,7 @@ export default (funcs, globals, pages) => {
154
167
 
155
168
  wasm.splice(j - 1, 1); // remove end of this block
156
169
 
157
- if (optLog) log('opt', `removed unneeded block in for loop`);
170
+ if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
158
171
  }
159
172
  }
160
173
 
@@ -186,6 +199,7 @@ export default (funcs, globals, pages) => {
186
199
  let missing = false;
187
200
  if (type === 'Array') missing = !pages.hasArray;
188
201
  if (type === 'String') missing = !pages.hasString;
202
+ if (type === 'ByteString') missing = !pages.hasByteString;
189
203
 
190
204
  if (missing) {
191
205
  let j = i, depth = 0;
@@ -202,7 +216,7 @@ export default (funcs, globals, pages) => {
202
216
  i -= 4;
203
217
  inst = wasm[i];
204
218
 
205
- if (optLog) log('opt', `removed unneeded typeswitch check`);
219
+ if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
206
220
  }
207
221
  }
208
222
 
@@ -221,16 +235,24 @@ export default (funcs, globals, pages) => {
221
235
  }
222
236
 
223
237
  if (checks === 0) {
238
+ // todo: review indexes below
224
239
  wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
225
240
  wasm.splice(i - 1, 1); // remove this inst
226
241
 
227
- if (optLog) log('opt', 'removed unneeded entire typeswitch');
242
+ if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
228
243
 
229
244
  if (i > 0) i--;
230
245
  continue;
231
246
  }
232
247
  }
233
248
 
249
+ // remove setting last type if it is never gotten
250
+ if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
251
+ // replace this inst with drop
252
+ wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
253
+ if (i > 0) i--;
254
+ }
255
+
234
256
  if (i < 1) continue;
235
257
  let lastInst = wasm[i - 1];
236
258
 
@@ -246,7 +268,7 @@ export default (funcs, globals, pages) => {
246
268
 
247
269
  getCount[inst[1]]--;
248
270
  i--;
249
- // if (optLog) log('opt', `consolidated set, get -> tee`);
271
+ // if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
250
272
  continue;
251
273
  }
252
274
 
@@ -314,7 +336,7 @@ export default (funcs, globals, pages) => {
314
336
 
315
337
  wasm.splice(i - 1, 2); // remove this inst and last
316
338
  i -= 2;
317
- // if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
339
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
318
340
  continue;
319
341
  }
320
342
 
@@ -327,7 +349,7 @@ export default (funcs, globals, pages) => {
327
349
 
328
350
  wasm.splice(i - 1, 2); // remove this inst and last
329
351
  i -= 2;
330
- // if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
352
+ // if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
331
353
  continue;
332
354
  }
333
355
 
@@ -342,7 +364,7 @@ export default (funcs, globals, pages) => {
342
364
 
343
365
  wasm.splice(i, 1); // remove this inst
344
366
  i--;
345
- if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
367
+ if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
346
368
  continue;
347
369
  }
348
370
 
@@ -357,7 +379,7 @@ export default (funcs, globals, pages) => {
357
379
 
358
380
  wasm.splice(i, 1); // remove this inst
359
381
  i--;
360
- if (optLog) log('opt', `converted i32 const -> convert into const`);
382
+ if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
361
383
  continue;
362
384
  }
363
385
 
@@ -372,7 +394,7 @@ export default (funcs, globals, pages) => {
372
394
 
373
395
  wasm.splice(i, 1); // remove this inst (return)
374
396
  i--;
375
- if (optLog) log('opt', `tail called return, call`);
397
+ if (Prefs.optLog) log('opt', `tail called return, call`);
376
398
  continue;
377
399
  }
378
400
 
@@ -385,7 +407,7 @@ export default (funcs, globals, pages) => {
385
407
 
386
408
  wasm.splice(i, 1); // remove this inst (return)
387
409
  i--;
388
- // if (optLog) log('opt', `removed redundant return at end`);
410
+ // if (Prefs.optLog) log('opt', `removed redundant return at end`);
389
411
  continue;
390
412
  }
391
413
 
@@ -415,7 +437,7 @@ export default (funcs, globals, pages) => {
415
437
  // <nothing>
416
438
 
417
439
  wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
418
- if (optLog) log('opt', `removed redundant inline param local handling`);
440
+ if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
419
441
  i -= 3;
420
442
  continue;
421
443
  }
@@ -423,12 +445,12 @@ export default (funcs, globals, pages) => {
423
445
 
424
446
  if (optLevel < 2) continue;
425
447
 
426
- 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(', ')}`);
427
449
 
428
450
  // remove unneeded var: remove pass
429
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
430
452
  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})`);
453
+ if (Prefs.optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
432
454
 
433
455
  // note: disabled for now due to instability
434
456
  if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
@@ -446,7 +468,7 @@ export default (funcs, globals, pages) => {
446
468
  wasm.splice(i - 1, 2); // remove insts
447
469
  i -= 2;
448
470
  delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
449
- if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
471
+ if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
450
472
  }
451
473
 
452
474
  if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
@@ -474,7 +496,7 @@ export default (funcs, globals, pages) => {
474
496
  unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
475
497
  unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
476
498
 
477
- if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
499
+ if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
478
500
  }
479
501
  }
480
502
 
@@ -510,7 +532,7 @@ export default (funcs, globals, pages) => {
510
532
  let b = lastInst[1];
511
533
 
512
534
  const val = performWasmOp(inst[0], a, b);
513
- 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})`);
514
536
 
515
537
  wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
516
538
  i -= 2;
@@ -522,12 +544,25 @@ export default (funcs, globals, pages) => {
522
544
  for (const x in useCount) {
523
545
  if (useCount[x] === 0) {
524
546
  const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
525
- if (optLog) log('opt', `removed internal local ${x} (${name})`);
547
+ if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
526
548
  delete f.locals[name];
527
549
  }
528
550
  }
529
551
 
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(', ')}`);
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);
531
566
  }
532
567
  }
533
568
 
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,16 +9,9 @@ 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';
10
-
11
- let parser, parse;
12
-
13
- const loadParser = async () => {
14
- parser = process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? 'acorn';
15
- 0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
16
- };
17
- globalThis._porf_loadParser = loadParser;
18
- await loadParser();
12
+ // should we try to support types (while parsing)
13
+ const types = Prefs.parseTypes;
14
+ globalThis.typedInput = types && Prefs.optTypes;
19
15
 
20
16
  // todo: review which to use by default
21
17
  // supported parsers:
@@ -24,8 +20,13 @@ await loadParser();
24
20
  // - hermes-parser
25
21
  // - @babel/parser
26
22
 
27
- // should we try to support types (while parsing)
28
- const types = process.argv.includes('-types');
23
+ let parser, parse;
24
+ const loadParser = async (fallbackParser = 'acorn', forceParser) => {
25
+ parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
26
+ 0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
27
+ };
28
+ globalThis._porf_loadParser = loadParser;
29
+ await loadParser(types ? '@babel/parser' : undefined);
29
30
 
30
31
  if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
31
32
 
@@ -0,0 +1,79 @@
1
+ import fs from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import { fileURLToPath } from 'node:url';
5
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
+
7
+ // import porfParse from './parse.js';
8
+ // import porfCodegen from './codeGen.js';
9
+
10
+ const argv = process.argv.slice();
11
+
12
+ const compile = async (file, [ _funcs, _globals ]) => {
13
+ const source = fs.readFileSync(file, 'utf8');
14
+ const first = source.slice(0, source.indexOf('\n'));
15
+
16
+ let args = ['-bytestring'];
17
+ if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
18
+ if (first.startsWith('// @porf')) {
19
+ args = args.concat(first.slice('// @porf '.length).split(' '));
20
+ }
21
+ process.argv = argv.concat(args);
22
+
23
+ // globalThis.optLog = process.argv.includes('-opt-log');
24
+ // globalThis.codeLog = process.argv.includes('-code-log');
25
+ // globalThis.allocLog = process.argv.includes('-alloc-log');
26
+ // globalThis.regexLog = process.argv.includes('-regex-log');
27
+
28
+ // const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
29
+ // const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
30
+
31
+ // let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
32
+
33
+ const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
34
+
35
+ let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
36
+
37
+ funcs = funcs.filter(x => x.export);
38
+ for (const x of funcs) {
39
+ if (x.data) x.data = x.data.map(x => data[x]);
40
+ if (x.exceptions) x.exceptions = x.exceptions.map(x => {
41
+ const obj = exceptions[x];
42
+ if (obj) obj.exceptId = x;
43
+ return obj;
44
+ }).filter(x => x);
45
+ }
46
+
47
+ _funcs.push(...funcs);
48
+ _globals.push(...Object.values(globals));
49
+ };
50
+
51
+ const precompile = async () => {
52
+ const dir = join(__dirname, 'builtins');
53
+
54
+ let funcs = [], globals = [];
55
+ for (const file of fs.readdirSync(dir)) {
56
+ if (file.endsWith('.d.ts')) continue;
57
+ await compile(join(dir, file), [ funcs, globals ]);
58
+ }
59
+
60
+ // todo: globals, exceptions, pages per func
61
+
62
+ return `// autogenerated by precompile.js
63
+
64
+ export const BuiltinFuncs = function() {
65
+ ${funcs.map(x => ` this.${x.name} = {
66
+ wasm: ${JSON.stringify(x.wasm)},
67
+ params: ${JSON.stringify(x.params)},
68
+ typedParams: true,
69
+ returns: ${JSON.stringify(x.returns)},
70
+ typedReturns: true,
71
+ locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length * 2).map(x => x.type))},
72
+ ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
73
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
74
+ ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
75
+ };`.replaceAll('\n\n', '\n')).join('\n')}
76
+ }`;
77
+ };
78
+
79
+ console.log(await precompile());
@@ -0,0 +1,22 @@
1
+ const cache = {};
2
+ const obj = new Proxy({}, {
3
+ get(_, p) {
4
+ // intentionally misses with undefined values cached
5
+ if (cache[p]) return cache[p];
6
+
7
+ return cache[p] = (() => {
8
+ // fooBar -> foo-bar
9
+ const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
10
+ if (process.argv.includes('-' + name)) return true;
11
+
12
+ const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
13
+ if (valArg) return valArg.slice(name.length + 2);
14
+
15
+ return undefined;
16
+ })();
17
+ }
18
+ });
19
+
20
+ obj.uncache = () => cache = {};
21
+
22
+ export default obj;