porffor 0.50.20 → 0.50.22

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.
package/README.md CHANGED
@@ -175,9 +175,6 @@ No particular order and no guarantees, just what could happen soon™
175
175
  - PGO
176
176
  - Self hosted testing?
177
177
 
178
- ## VSCode extension
179
- There is a vscode extension in `vscode-ext` which tweaks JS syntax highlighting to be nicer with porffor features (eg highlighting wasm inside of inline asm).
180
-
181
178
  ## Wasm proposals used
182
179
  Porffor intentionally does not use Wasm proposals which are not commonly implemented yet (eg GC) so it can be used in as many places as possible.
183
180
 
@@ -1,5 +1,5 @@
1
1
  import { Valtype, FuncType, ExportDesc, Section, Magic, Opcodes, PageSize, Reftype } from './wasmSpec.js';
2
- import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64, ieee754_binary64_into } from './encoding.js';
2
+ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64, ieee754_binary64_into, unsignedLEB128_length } from './encoding.js';
3
3
  import { importedFuncs } from './builtins.js';
4
4
  import { log } from './log.js';
5
5
  import './prefs.js';
@@ -250,9 +250,7 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
250
250
  let codeSection = [];
251
251
  for (let i = 0; i < funcs.length; i++) {
252
252
  const x = funcs[i];
253
- // time(x.name);
254
253
  const locals = Object.values(x.locals).sort((a, b) => a.idx - b.idx);
255
- // time(' locals gen');
256
254
 
257
255
  const paramCount = x.params.length;
258
256
  let localDecl = [], typeCount = 0, lastType, declCount = 0;
@@ -269,77 +267,131 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
269
267
  lastType = local?.type;
270
268
  }
271
269
 
272
- // time(' localDecl gen');
273
-
274
- const makeAssembled = Prefs.d;
275
270
  let wasm = [], wasmNonFlat = [];
276
- for (let i = 0; i < x.wasm.length; i++) {
277
- let o = x.wasm[i];
278
-
279
- // encode local/global ops as unsigned leb128 from raw number
280
- if (
281
- (o[0] >= Opcodes.local_get && o[0] <= Opcodes.global_set) &&
282
- o[1] > 127
283
- ) {
284
- const n = o[1];
285
- o = [ o[0] ];
286
- unsignedLEB128_into(n, o);
287
- }
271
+ if (Prefs.d) {
272
+ for (let i = 0; i < x.wasm.length; i++) {
273
+ let o = x.wasm[i];
274
+
275
+ // encode local/global ops as unsigned leb128 from raw number
276
+ if (
277
+ (o[0] >= Opcodes.local_get && o[0] <= Opcodes.global_set) &&
278
+ o[1] > 127
279
+ ) {
280
+ const n = o[1];
281
+ o = [ o[0] ];
282
+ unsignedLEB128_into(n, o);
283
+ }
288
284
 
289
- // encode f64.const ops as ieee754 from raw number
290
- if (o[0] === Opcodes.f64_const) {
291
- const n = o[1];
292
- o = ieee754_binary64(n);
293
- if (o.length === 8) o.unshift(Opcodes.f64_const);
294
- }
285
+ // encode f64.const ops as ieee754 from raw number
286
+ if (o[0] === Opcodes.f64_const) {
287
+ const n = o[1];
288
+ o = ieee754_binary64(n);
289
+ if (o.length === 8) o.unshift(Opcodes.f64_const);
290
+ }
295
291
 
296
- // encode call ops as unsigned leb128 from raw number
297
- if ((o[0] === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
298
- const n = o[1] - importDelta;
299
- o = [ Opcodes.call ];
300
- unsignedLEB128_into(n, o);
301
- }
292
+ // encode call ops as unsigned leb128 from raw number
293
+ if ((o[0] === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
294
+ const n = o[1] - importDelta;
295
+ o = [ Opcodes.call ];
296
+ unsignedLEB128_into(n, o);
297
+ }
302
298
 
303
- // encode call indirect ops as types from info
304
- if (o[0] === Opcodes.call_indirect) {
305
- o = [...o];
306
- const params = [];
307
- for (let i = 0; i < o[1]; i++) {
308
- params.push(valtypeBinary, Valtype.i32);
299
+ // encode call indirect ops as types from info
300
+ if (o[0] === Opcodes.call_indirect) {
301
+ o = [...o];
302
+ const params = [];
303
+ for (let i = 0; i < o[1]; i++) {
304
+ params.push(valtypeBinary, Valtype.i32);
305
+ }
306
+
307
+ let returns = [ valtypeBinary, Valtype.i32 ];
308
+ if (o.at(-1) === 'no_type_return') {
309
+ o.pop();
310
+ returns = [ valtypeBinary ];
311
+ }
312
+
313
+ o[1] = getType(params, returns);
309
314
  }
310
315
 
311
- let returns = [ valtypeBinary, Valtype.i32 ];
312
- if (o.at(-1) === 'no_type_return') {
313
- o.pop();
314
- returns = [ valtypeBinary ];
316
+ for (let j = 0; j < o.length; j++) {
317
+ const x = o[j];
318
+ if (x == null || !(x <= 0xff)) continue;
319
+ wasm.push(x);
315
320
  }
316
321
 
317
- o[1] = getType(params, returns);
322
+ wasmNonFlat.push(o);
318
323
  }
319
324
 
320
- for (let j = 0; j < o.length; j++) {
321
- const x = o[j];
322
- if (x == null || !(x <= 0xff)) continue;
323
- wasm.push(x);
324
- }
325
+ x.assembled = { localDecl, wasm, wasmNonFlat };
326
+ } else {
327
+ for (let i = 0; i < x.wasm.length; i++) {
328
+ let o = x.wasm[i];
329
+ const op = o[0];
330
+
331
+ // encode local/global ops as unsigned leb128 from raw number
332
+ if (
333
+ (op >= Opcodes.local_get && op <= Opcodes.global_set) &&
334
+ o[1] > 127
335
+ ) {
336
+ wasm.push(op);
337
+ unsignedLEB128_into(o[1], wasm);
338
+ continue;
339
+ }
325
340
 
326
- if (makeAssembled) wasmNonFlat.push(o);
327
- }
328
- // time(' wasm transform');
341
+ // encode f64.const ops as ieee754 from raw number
342
+ if (op === Opcodes.f64_const) {
343
+ wasm.push(op);
344
+ ieee754_binary64_into(o[1], wasm);
345
+ continue;
346
+ }
329
347
 
330
- if (makeAssembled) {
331
- x.assembled = { localDecl, wasm, wasmNonFlat };
332
- }
348
+ // encode call ops as unsigned leb128 from raw number
349
+ if ((op === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
350
+ wasm.push(op);
351
+ unsignedLEB128_into(o[1] - importDelta, wasm);
352
+ continue;
353
+ }
354
+
355
+ // encode call indirect ops as types from info
356
+ if (op === Opcodes.call_indirect) {
357
+ const params = [];
358
+ for (let i = 0; i < o[1]; i++) {
359
+ params.push(valtypeBinary, Valtype.i32);
360
+ }
333
361
 
334
- let out = unsignedLEB128(declCount)
335
- .concat(localDecl, wasm, Opcodes.end);
362
+ let returns = [ valtypeBinary, Valtype.i32 ];
363
+ if (o.at(-1) === 'no_type_return') {
364
+ returns = [ valtypeBinary ];
365
+ }
366
+
367
+ wasm.push(op, getType(params, returns), o[2]);
368
+ continue;
369
+ }
336
370
 
337
- codeSection.push(
338
- ...unsignedLEB128(out.length),
339
- ...out
340
- );
371
+ for (let j = 0; j < o.length; j++) {
372
+ const x = o[j];
373
+ if (x == null || !(x <= 0xff)) continue;
374
+ wasm.push(x);
375
+ }
376
+ }
377
+ }
341
378
 
342
- // time(' finish');
379
+ if (wasm.length > 100000) {
380
+ // slow path for handling large arrays which break v8 due to running out of stack size
381
+ const out = unsignedLEB128(declCount)
382
+ .concat(localDecl, wasm, Opcodes.end);
383
+ codeSection = codeSection.concat(unsignedLEB128(out.length), out);
384
+ } else {
385
+ codeSection.push(
386
+ ...unsignedLEB128(unsignedLEB128_length(declCount) + localDecl.length + wasm.length + 1),
387
+ ...unsignedLEB128(declCount),
388
+ ...localDecl,
389
+ ...wasm,
390
+ Opcodes.end
391
+ );
392
+ }
393
+
394
+ // globalThis.progress?.(`${i}/${funcs.length}`);
343
395
  }
344
396
 
345
397
  codeSection.unshift(...unsignedLEB128(funcs.length, codeSection));
@@ -368,10 +420,14 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
368
420
  dataSection.push(0x01);
369
421
  }
370
422
 
371
- dataSection.push(
372
- ...unsignedLEB128(x.bytes.length),
373
- ...x.bytes
374
- );
423
+ if (x.bytes.length > 100000) {
424
+ codeSection = codeSection.concat(unsignedLEB128(x.bytes.length), x.bytes);
425
+ } else {
426
+ dataSection.push(
427
+ ...unsignedLEB128(x.bytes.length),
428
+ ...x.bytes
429
+ );
430
+ }
375
431
  }
376
432
 
377
433
  dataSection.unshift(...unsignedLEB128(data.length, dataSection));
@@ -6457,8 +6457,6 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6457
6457
  return [ func, out ];
6458
6458
  }
6459
6459
 
6460
- globalThis.progress?.(null, ' ' + name);
6461
-
6462
6460
  const params = decl.params ?? [];
6463
6461
 
6464
6462
  // TODO: share scope/locals between !!!
@@ -6762,15 +6760,18 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6762
6760
  return [ func, out ];
6763
6761
  };
6764
6762
 
6763
+ let largestBlockBody = 0;
6765
6764
  const generateBlock = (scope, decl) => {
6766
6765
  let out = [];
6767
6766
 
6768
6767
  scope.inferTree ??= [];
6769
6768
  scope.inferTree.push(decl);
6770
6769
 
6771
- let j = 0;
6772
- for (let i = 0; i < decl.body.length; i++) {
6770
+ let len = decl.body.length, j = 0;
6771
+ if (len > largestBlockBody) largestBlockBody = len;
6772
+ for (let i = 0; i < len; i++) {
6773
6773
  const x = decl.body[i];
6774
+ if (len >= largestBlockBody) globalThis.progress?.(`${i}/${len}`);
6774
6775
  if (isEmptyNode(x)) continue;
6775
6776
 
6776
6777
  if (j++ > 0) out.push([ Opcodes.drop ]);
@@ -6946,6 +6947,7 @@ export default program => {
6946
6947
  data = [];
6947
6948
  currentFuncIndex = importedFuncs.length;
6948
6949
  typeswitchDepth = 0;
6950
+ largestBlockBody = 0;
6949
6951
  usedTypes = new Set([ TYPES.empty, TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ]);
6950
6952
  coctc = new Map();
6951
6953
  globalInfer = new Map();
@@ -8,17 +8,4 @@ export const number = (n, valtype = valtypeBinary) => {
8
8
  signedLEB128_into(n, out);
9
9
 
10
10
  return out;
11
- };
12
-
13
- export const enforceOneByte = arr => [ arr[0] ?? 0 ];
14
- export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
15
- export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
16
- export const enforceEightBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0, arr[4] ?? 0, arr[5] ?? 0, arr[6] ?? 0, arr[7] ?? 0 ];
17
-
18
- export const i32x4 = (a, b, c, d) => [ [
19
- ...Opcodes.v128_const,
20
- ...enforceFourBytes(signedLEB128(a)),
21
- ...enforceFourBytes(signedLEB128(b)),
22
- ...enforceFourBytes(signedLEB128(c)),
23
- ...enforceFourBytes(signedLEB128(d))
24
- ] ];
11
+ };
@@ -64,6 +64,22 @@ export const unsignedLEB128 = n => {
64
64
  return buffer;
65
65
  };
66
66
 
67
+ export const unsignedLEB128_length = n => {
68
+ if (n < 0) n = n >>> 0;
69
+ if (n <= 127) return 1;
70
+ if (n <= 16383) return 2;
71
+ if (n <= 2097151) return 3;
72
+ if (n <= 268435455) return 4;
73
+ if (n <= 34359738367) return 5;
74
+
75
+ let length = 0;
76
+ do {
77
+ length++;
78
+ n >>>= 7;
79
+ } while (n > 0);
80
+ return length;
81
+ };
82
+
67
83
  export const big_signedLEB128 = n => {
68
84
  // just input for small numbers (for perf as common)
69
85
  if (n >= 0n && n <= 63n) return [ Number(n) ];
package/compiler/index.js CHANGED
@@ -35,7 +35,7 @@ const progressStart = msg => {
35
35
 
36
36
  const log = (extra, after) => {
37
37
  const pre = extra ? `${extra}` : spinner[spin++ % 4];
38
- process.stdout.write(`\r\u001b[90m${' '.repeat(10 - pre.length)}${pre} ${msg}${after ?? ''}\u001b[0m`);
38
+ process.stdout.write(`\r\u001b[90m${' '.repeat(120)}\r${' '.repeat(12 - pre.length)}${pre} ${msg}${after ?? ''}\u001b[0m`);
39
39
  };
40
40
  log();
41
41
 
@@ -48,7 +48,7 @@ const progressDone = (msg, start) => {
48
48
  clearInterval(progressInterval);
49
49
 
50
50
  const timeStr = (performance.now() - start).toFixed(0);
51
- console.log(`\r${' '.repeat(50)}\r\u001b[90m${' '.repeat(8 - timeStr.length)}${timeStr}ms\u001b[0m \u001b[92m${msg}\u001b[0m`);
51
+ console.log(`\r${' '.repeat(120)}\r\u001b[90m${' '.repeat(10 - timeStr.length)}${timeStr}ms\u001b[0m \u001b[92m${msg}\u001b[0m`);
52
52
  progressLines++;
53
53
  };
54
54
  const progressClear = () => {
@@ -66,9 +66,8 @@ export default (code, module = undefined) => {
66
66
  let target = Prefs.target ?? 'wasm';
67
67
  if (Prefs.native) target = 'native';
68
68
 
69
- const logProgress = Prefs.profileCompiler || (target === 'native' && !Prefs.native && globalThis.file);
70
-
71
- let outFile = Prefs.o;
69
+ const outFile = Prefs.o;
70
+ const logProgress = Prefs.profileCompiler || (Prefs.target && outFile && !Prefs.native && globalThis.file);
72
71
 
73
72
  globalThis.valtype = Prefs.valtype ?? 'f64';
74
73
  globalThis.valtypeBinary = Valtype[valtype];
@@ -214,6 +213,12 @@ export default (code, module = undefined) => {
214
213
  if (target === 'wasm' && outFile) {
215
214
  fs.writeFileSync(outFile, Buffer.from(wasm));
216
215
 
216
+ if (logProgress) {
217
+ const total = performance.now();
218
+ progressClear();
219
+ console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
220
+ }
221
+
217
222
  if (process.version) process.exit();
218
223
  }
219
224
 
@@ -227,6 +232,12 @@ export default (code, module = undefined) => {
227
232
  console.log(c);
228
233
  }
229
234
 
235
+ if (logProgress) {
236
+ const total = performance.now();
237
+ progressClear();
238
+ console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
239
+ }
240
+
230
241
  if (process.version) process.exit();
231
242
  }
232
243
 
@@ -260,35 +271,33 @@ export default (code, module = undefined) => {
260
271
 
261
272
  if (logProgress) progressStart(`compiled C to native (using ${compiler})`, t5);
262
273
 
263
- if (process.version) {
264
- if (Prefs.native) {
265
- const cleanup = () => {
266
- try {
267
- fs.unlinkSync(outFile);
268
- } catch {}
269
- };
270
-
271
- process.on('exit', cleanup);
272
- process.on('beforeExit', cleanup);
273
- process.on('SIGINT', () => {
274
- cleanup();
275
- process.exit();
276
- });
277
-
278
- const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
274
+ if (Prefs.native) {
275
+ const cleanup = () => {
279
276
  try {
280
- execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
277
+ fs.unlinkSync(outFile);
281
278
  } catch {}
282
- }
279
+ };
283
280
 
284
- if (logProgress) {
285
- const total = performance.now();
286
- progressClear();
287
- console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
288
- }
281
+ process.on('exit', cleanup);
282
+ process.on('beforeExit', cleanup);
283
+ process.on('SIGINT', () => {
284
+ cleanup();
285
+ process.exit();
286
+ });
287
+
288
+ const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
289
+ try {
290
+ execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
291
+ } catch {}
292
+ }
289
293
 
290
- process.exit();
294
+ if (logProgress) {
295
+ const total = performance.now();
296
+ progressClear();
297
+ console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
291
298
  }
299
+
300
+ process.exit();
292
301
  }
293
302
 
294
303
  return out;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.50.20",
4
+ "version": "0.50.22",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runner/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.50.20';
3
+ globalThis.version = '0.50.22';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -94,8 +94,6 @@ if (['precompile', 'run', 'wasm', 'native', 'c', 'flamegraph', 'hotlines', 'debu
94
94
  }
95
95
  }
96
96
 
97
- globalThis.file = file;
98
-
99
97
  let source = '', printOutput = false;
100
98
  if (process.argv.length >= 4) {
101
99
  let evalIndex = process.argv.indexOf('-e');
@@ -127,6 +125,20 @@ if (process.argv.length >= 4) {
127
125
  }
128
126
  }
129
127
 
128
+ if (file.startsWith('https://')) { // https only :)
129
+ // rce warning, make user confirm (disabled)
130
+ // const rl = (await import('readline')).createInterface({ input: process.stdin, output: process.stdout });
131
+ // const ans = await new Promise(resolve => rl.question(`\u001b[1mAre you sure you want to download this URL:\u001b[0m ${file} (y/n)? `, ans => {
132
+ // rl.close();
133
+ // resolve(ans);
134
+ // }));
135
+ // if (ans.toLowerCase()[0] !== 'y') process.exit();
136
+
137
+ source = await (await fetch(file)).text();
138
+ }
139
+
140
+ globalThis.file = file;
141
+
130
142
  if (!file && !source) {
131
143
  if (process.argv.includes('-v') || process.argv.includes('--version')) {
132
144
  // just print version