porffor 0.55.26 → 0.55.28

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.
@@ -188,6 +188,7 @@ export const __Porffor_object_hash = (key: any): i32 => {
188
188
 
189
189
  // bytestring or string, fnv-1a hash (custom variant)
190
190
  // todo/opt: custom wasm simd variant?
191
+ // todo/opt: pgo for if no hash collisions?
191
192
 
192
193
  let ptr: i32 = Porffor.wasm`local.get ${key}`;
193
194
  const len: i32 = Porffor.wasm.i32.load(key, 0, 0);
@@ -371,15 +372,9 @@ export const __Porffor_object_lookup = (obj: any, target: any, targetHash: i32):
371
372
  let ptr: i32 = Porffor.wasm`local.get ${obj}` + 8;
372
373
  const endPtr: i32 = ptr + Porffor.wasm.i32.load16_u(obj, 0, 0) * 18;
373
374
 
374
- let out: boolean = false;
375
375
  if (Porffor.wasm`local.get ${target+1}` == Porffor.TYPES.symbol) {
376
376
  for (; ptr < endPtr; ptr += 18) {
377
377
  const key: i32 = Porffor.wasm.i32.load(ptr, 0, 4);
378
- if (key == 0) {
379
- if (out) break; // ran out of keys
380
- out = true;
381
- }
382
-
383
378
  if ((key >>> 30) == 3) { // MSB 1 and 2 set, symbol (unset MSB x2)
384
379
  // todo: remove casts once weird bug which breaks unrelated things is fixed (https://github.com/CanadaHonk/porffor/commit/5747f0c1f3a4af95283ebef175cdacb21e332a52)
385
380
  if ((key & 0x3FFFFFFF) as symbol == target as symbol) return ptr;
@@ -389,6 +384,11 @@ export const __Porffor_object_lookup = (obj: any, target: any, targetHash: i32):
389
384
  for (; ptr < endPtr; ptr += 18) {
390
385
  if (Porffor.wasm.i32.load(ptr, 0, 0) == targetHash) {
391
386
  const key: i32 = Porffor.wasm.i32.load(ptr, 0, 4);
387
+
388
+ // fast path: check if same pointer first
389
+ if (key == Porffor.wasm`local.get ${target}`) return ptr;
390
+
391
+ // slow path: strcmp
392
392
  Porffor.wasm`
393
393
  local.get ${key}
394
394
  i32.const 2147483647
@@ -86,9 +86,9 @@ params:[127,127],typedParams:1,returns:[127],returnType:1,
86
86
  locals:[127,127,127,127,126,126],localNames:["key","key#type","ptr","len","hash","endPtr","x","shift"],
87
87
  }
88
88
  this.__Porffor_object_lookup = {
89
- wasm:(_,{builtin})=>[[32,0],[69],[4,64],[65,127],[15],[26],[11],[32,1],[65,7],[71],[4,64],[32,0],[183],[32,1],[16,builtin('__Porffor_object_underlying')],[33,6],[252,2],[33,0],[32,6],[34,1],[65,7],[71],[4,64],[65,127],[15],[26],[11],[11],[32,0],[65,8],[106],[34,7],[32,0],[47,0,0],[65,18],[108],[106],[33,8],[65,0],[33,9],[32,3],[65,5],[70],[4,64],[3,64],[32,7],[32,8],[72],[4,64],[2,64],[32,7],[40,0,4],[34,10],[69],[4,64],[32,9],[4,64],[12,3],[26],[11],[65,1],[33,9],[11],[32,10],[65,30],[118],[65,3],[70],[4,64],[32,10],[65,255,255,255,255,3],[113],[32,2],[70],[4,64],[32,7],[15],[26],[11],[11],[11],[32,7],[65,18],[106],[33,7],[12,1],[11],[11],[5],[3,64],[32,7],[32,8],[72],[4,64],[32,7],[40,0,0],[32,4],[70],[4,64],[32,7],[40,0,4],[34,10],[65,255,255,255,255,7],[113],[65,195,0],[65,195,1],[32,10],[65,30],[118],[27],[32,2],[32,3],[16,builtin('__Porffor_strcmp')],[4,64],[32,7],[15],[11],[11],[32,7],[65,18],[106],[33,7],[12,1],[11],[11],[11],[65,127],[15]],
89
+ wasm:(_,{builtin})=>[[32,0],[69],[4,64],[65,127],[15],[26],[11],[32,1],[65,7],[71],[4,64],[32,0],[183],[32,1],[16,builtin('__Porffor_object_underlying')],[33,6],[252,2],[33,0],[32,6],[34,1],[65,7],[71],[4,64],[65,127],[15],[26],[11],[11],[32,0],[65,8],[106],[34,7],[32,0],[47,0,0],[65,18],[108],[106],[33,8],[32,3],[65,5],[70],[4,64],[3,64],[32,7],[32,8],[72],[4,64],[32,7],[40,0,4],[34,9],[65,30],[118],[65,3],[70],[4,64],[32,9],[65,255,255,255,255,3],[113],[32,2],[70],[4,64],[32,7],[15],[26],[11],[11],[32,7],[65,18],[106],[33,7],[12,1],[11],[11],[5],[3,64],[32,7],[32,8],[72],[4,64],[32,7],[40,0,0],[32,4],[70],[4,64],[32,7],[40,0,4],[34,9],[32,2],[70],[4,64],[32,7],[15],[26],[11],[32,9],[65,255,255,255,255,7],[113],[65,195,0],[65,195,1],[32,9],[65,30],[118],[27],[32,2],[32,3],[16,builtin('__Porffor_strcmp')],[4,64],[32,7],[15],[11],[11],[32,7],[65,18],[106],[33,7],[12,1],[11],[11],[11],[65,127],[15]],
90
90
  params:[127,127,127,127,127,127],typedParams:1,returns:[127],returnType:1,
91
- locals:[127,127,127,127,127],localNames:["obj","obj#type","target","target#type","targetHash","targetHash#type","#last_type","ptr","endPtr","out","key"],
91
+ locals:[127,127,127,127],localNames:["obj","obj#type","target","target#type","targetHash","targetHash#type","#last_type","ptr","endPtr","key"],
92
92
  usedTypes:[5],
93
93
  }
94
94
  this.__Porffor_object_readValue = {
@@ -1,6 +1,7 @@
1
1
  // cyclone: wasm partial constant evaluator (it is fast and dangerous hence "cyclone")
2
2
  import { number, signedLEB128, ieee754_binary64, read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
3
3
  import { Opcodes, Valtype } from './wasmSpec.js';
4
+ import './prefs.js';
4
5
 
5
6
  const f64ToI32Op = {
6
7
  [Opcodes.f64_eq]: Opcodes.i32_eq,
@@ -15,8 +16,16 @@ const f64ToI32Op = {
15
16
  [Opcodes.f64_div]: Opcodes.i32_div_s,
16
17
  };
17
18
 
18
- export default wasm => {
19
- let stack = []; // """""stack"""""
19
+ export default ({ name, wasm, locals: _locals, params }) => {
20
+ let locals = Object.values(_locals);
21
+ const localValtypes = locals.map(x => x.type);
22
+
23
+ const resetLocals = () => {
24
+ locals = new Array(locals.length).fill(false);
25
+ };
26
+ resetLocals();
27
+
28
+ let stack = []; // "stack"
20
29
  for (let i = 0; i < wasm.length; i++) {
21
30
  let op = wasm[i];
22
31
  if (!op) continue;
@@ -33,8 +42,13 @@ export default wasm => {
33
42
  stack.push({ val, op });
34
43
  };
35
44
 
45
+ const peek = () => stack[stack.length - 1]?.val;
46
+ const popWithoutRemove = () => stack.pop().val;
47
+
48
+ // let debugPops = [];
36
49
  const pop = () => {
37
50
  const popped = stack.pop();
51
+ // debugPops.unshift({ ...popped, index: wasm.indexOf(popped.op) });
38
52
 
39
53
  // remove the op
40
54
  wasm.splice(wasm.indexOf(popped.op), 1);
@@ -42,12 +56,18 @@ export default wasm => {
42
56
 
43
57
  return popped.val;
44
58
  };
45
-
46
59
  const pop2 = () => [ pop(), pop() ];
47
60
 
48
61
  const bool = v => v ? 1 : 0;
49
62
 
50
63
  const replaceOp = newOp => {
64
+ // console.log(`\x1b[4m${name}\x1b[0m`);
65
+ // const oldOps = [ ...debugPops, { op, index: i + debugPops.length } ];
66
+ // for (const x of oldOps) {
67
+ // console.log(`\x1b[90m${x.index.toString().padStart(4)} ▌\x1b[0m ${disassemble([ x.op ], undefined, undefined, _locals).slice(0, -1)}${x.val != null ? ` \x1b[2m(${x.val})\x1b[0m` : ''}`);
68
+ // }
69
+ // process.stdout.write(`\x1b[s\x1b[${oldOps.length}A\x1b[40C\x1b[2m->\x1b[0m ${disassemble([ newOp ]).slice(0, -1)}\x1b[u\n`);
70
+
51
71
  op.splice(0, op.length, ...newOp);
52
72
  };
53
73
  const replaceVal = (val, valtype) => replaceOp(number(val, valtype));
@@ -57,41 +77,41 @@ export default wasm => {
57
77
  };
58
78
 
59
79
  switch (opcode) {
60
- case Opcodes.if: {
61
- if (stack.length < 1) { empty(); break; }
62
- const cond = bool(pop());
63
-
64
- // find else split and end
65
- let j = i + 1;
66
- let depth = 0, elseStart = 0;
67
- for (; j < wasm.length; j++) {
68
- const op = wasm[j][0];
69
- if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) depth++;
70
- if (op === Opcodes.else && depth === 0) elseStart = j;
71
- if (op === Opcodes.end) {
72
- depth--;
73
- if (depth < 0) break;
74
- }
75
- if (op === Opcodes.br || op === Opcodes.br_if) wasm[j][1] -= 1;
76
- }
77
-
78
- if (cond) {
79
- // remove else if it exists, or just remove end
80
- if (elseStart) wasm.splice(elseStart, j - elseStart + 1);
81
- else wasm.splice(j, 1);
82
- } else {
83
- // remove truthy conseq and keep else if it exists, or just remove entire thing
84
- if (elseStart) {
85
- wasm.splice(j, 1); // remove end
86
- wasm.splice(i + 1, elseStart - i + 1); // remove truthy conseq
87
- } else wasm.splice(i + 1, j - i + 0); // no else, remove entire if
88
- }
89
-
90
- // remove this if op
91
- wasm.splice(i, 1);
80
+ // case Opcodes.if: {
81
+ // if (stack.length < 1) { empty(); break; }
82
+ // const cond = bool(pop());
83
+
84
+ // // find else split and end
85
+ // let j = i + 1;
86
+ // let depth = 0, elseStart = 0;
87
+ // for (; j < wasm.length; j++) {
88
+ // const op = wasm[j][0];
89
+ // if (op === Opcodes.if || op === Opcodes.block || op === Opcodes.loop || op === Opcodes.try) depth++;
90
+ // if (op === Opcodes.else && depth === 0) elseStart = j;
91
+ // if (op === Opcodes.end) {
92
+ // depth--;
93
+ // if (depth < 0) break;
94
+ // }
95
+ // if (op === Opcodes.br || op === Opcodes.br_if) wasm[j][1] -= 1;
96
+ // }
97
+
98
+ // if (cond) {
99
+ // // remove else if it exists, or just remove end
100
+ // if (elseStart) wasm.splice(elseStart, j - elseStart + 1);
101
+ // else wasm.splice(j, 1);
102
+ // } else {
103
+ // // remove truthy conseq and keep else if it exists, or just remove entire thing
104
+ // if (elseStart) {
105
+ // wasm.splice(j, 1); // remove end
106
+ // wasm.splice(i + 1, elseStart - i + 1); // remove truthy conseq
107
+ // } else wasm.splice(i + 1, j - i + 0); // no else, remove entire if
108
+ // }
109
+
110
+ // // remove this if op
111
+ // wasm.splice(i, 1);
92
112
 
93
- break;
94
- }
113
+ // break;
114
+ // }
95
115
 
96
116
  case Opcodes.i32_const: {
97
117
  const n = read_signedLEB128(op.slice(1));
@@ -448,8 +468,8 @@ export default wasm => {
448
468
  break;
449
469
  }
450
470
 
451
- case Opcodes.f64_convert_i32_u:
452
- case Opcodes.f64_convert_i32_s: {
471
+ case Opcodes.f64_convert_i32_s:
472
+ case Opcodes.f64_convert_i32_u: {
453
473
  if (stack.length < 1) { empty(); break; };
454
474
  const v = pop();
455
475
 
@@ -458,7 +478,8 @@ export default wasm => {
458
478
  break;
459
479
  }
460
480
 
461
- case 0xfc02: { // i32_trunc_sat_f64_s
481
+ case 0xfc02: // i32.trunc_sat_f64_s
482
+ case 0xfc03: { // i32.trunc_sat_f64_u
462
483
  if (stack.length < 1) { empty(); break; }
463
484
  const v = pop();
464
485
 
@@ -467,20 +488,60 @@ export default wasm => {
467
488
  break;
468
489
  }
469
490
 
470
- case 0xfc03: { // i32_trunc_sat_f64_u
491
+ case Opcodes.drop: {
471
492
  if (stack.length < 1) { empty(); break; }
472
- const v = pop();
493
+ pop();
494
+ break;
495
+ }
473
496
 
474
- replaceVal(v, Valtype.i32);
475
- push(v);
497
+ case Opcodes.local_get: {
498
+ const x = locals[op[1]];
499
+ if (x === false) {
500
+ empty();
501
+ } else {
502
+ replaceVal(x, localValtypes[op[1]]);
503
+ push(x);
504
+ }
476
505
  break;
477
506
  }
478
507
 
479
- // case Opcodes.local_tee: {
480
- // if (stack.length < 1) { empty(); break; }
481
- // push(pop());
482
- // break;
483
- // }
508
+ case Opcodes.local_set: {
509
+ if (stack.length < 1) {
510
+ locals[op[1]] = false;
511
+ empty();
512
+ break;
513
+ }
514
+
515
+ const x = popWithoutRemove();
516
+ locals[op[1]] = x;
517
+ break;
518
+ }
519
+
520
+ case Opcodes.local_tee: {
521
+ if (stack.length < 1) {
522
+ locals[op[1]] = false;
523
+ empty();
524
+ break;
525
+ }
526
+
527
+ const x = pop();
528
+ locals[op[1]] = x;
529
+ replaceVal(x, localValtypes[op[1]]);
530
+ push(x);
531
+ break;
532
+ }
533
+
534
+ case Opcodes.block:
535
+ case Opcodes.loop:
536
+ case Opcodes.try:
537
+ case Opcodes.if:
538
+ case Opcodes.else:
539
+ case Opcodes.catch:
540
+ case Opcodes.end: {
541
+ resetLocals();
542
+ empty();
543
+ break;
544
+ }
484
545
 
485
546
  default: {
486
547
  empty();
@@ -500,69 +561,69 @@ export default wasm => {
500
561
  // i32.const 1
501
562
  // i32.add
502
563
  // local.set 7 ;; $i (i32)
503
- if (i >= 2 &&
504
- ((opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
505
- (opcode >= 0x61 && opcode <= 0x66)) // main f64 eq op
506
- ) {
507
- const o2 = wasm[i - 1][0];
508
- if (o2 === Opcodes.f64_const) { // f64.const
509
- const o3 = wasm[i - 2][0];
510
- if (o3 === Opcodes.f64_convert_i32_s || o3 === Opcodes.f64_convert_i32_u) {
511
- // remove now unneeded i32 -> f64 convert
512
- wasm.splice(i - 2, 1);
513
- i--;
514
-
515
- // convert f64.const -> i32.const
516
- const n = wasm[i - 1][1];
517
- wasm.splice(i - 1, 1, number(n, Valtype.i32));
518
-
519
- // convert math op from f64 to i32
520
- wasm[i][0] = f64ToI32Op[wasm[i][0]];
521
-
522
- const nextOp = wasm[i + 1];
523
- if (nextOp && opcode >= 0xa0 && opcode <= 0xa3) {
524
- if (nextOp[0] === 0xfc && (nextOp[1] === 0x02 || nextOp[1] === 0x03)) {
525
- // remove optional unneeded f64 -> i32 convert after
526
- wasm.splice(i + 1, 1);
527
- } else {
528
- // add now needed i32 -> f64 convert after
529
- wasm.splice(i + 1, Opcodes.i32_trunc_sat_f64_s);
530
- }
531
- }
532
- }
533
- }
534
- }
535
-
536
- if ((opcode === 0xfc02 || opcode === 0xfc03) && i >= 3) { // i32.trunc_sat_f64_s/u
537
- const o2 = wasm[i - 1][0];
538
- if (
539
- (o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
540
- (o2 >= 0x61 && o2 <= 0x66) // main f64 eq op
541
- ) {
542
- const o3 = wasm[i - 2][0];
543
- if (o3 === Opcodes.f64_const) { // f64.const
544
- const o4 = wasm[i - 3][0];
545
- if (o4 === Opcodes.f64_convert_i32_s || o4 === Opcodes.f64_convert_i32_u) {
546
- // remove now unneeded i32 -> f64 convert
547
- wasm.splice(i - 3, 1);
548
- i--;
549
- } else {
550
- // add now needed f64 -> i32 convert prior
551
- wasm.splice(i - 2, 0, Opcodes.i32_trunc_sat_f64_s);
552
- i++;
553
- }
554
-
555
- // convert f64.const -> i32.const
556
- const n = wasm[i - 2][1];
557
- wasm.splice(i - 2, 1, number(n, Valtype.i32));
558
-
559
- // convert math op from f64 to i32
560
- wasm[i - 1][0] = f64ToI32Op[wasm[i - 1][0]];
561
-
562
- // remove this now unneeded f64 -> i32 convert
563
- wasm.splice(i, 1);
564
- }
565
- }
566
- }
564
+ // if (i >= 2 &&
565
+ // ((opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
566
+ // (opcode >= 0x61 && opcode <= 0x66)) // main f64 eq op
567
+ // ) {
568
+ // const o2 = wasm[i - 1][0];
569
+ // if (o2 === Opcodes.f64_const) { // f64.const
570
+ // const o3 = wasm[i - 2][0];
571
+ // if (o3 === Opcodes.f64_convert_i32_s || o3 === Opcodes.f64_convert_i32_u) {
572
+ // // remove now unneeded i32 -> f64 convert
573
+ // wasm.splice(i - 2, 1);
574
+ // i--;
575
+
576
+ // // convert f64.const -> i32.const
577
+ // const n = wasm[i - 1][1];
578
+ // wasm.splice(i - 1, 1, number(n, Valtype.i32));
579
+
580
+ // // convert math op from f64 to i32
581
+ // wasm[i][0] = f64ToI32Op[wasm[i][0]];
582
+
583
+ // const nextOp = wasm[i + 1];
584
+ // if (nextOp && opcode >= 0xa0 && opcode <= 0xa3) {
585
+ // if (nextOp[0] === 0xfc && (nextOp[1] === 0x02 || nextOp[1] === 0x03)) {
586
+ // // remove optional unneeded f64 -> i32 convert after
587
+ // wasm.splice(i + 1, 1);
588
+ // } else {
589
+ // // add now needed i32 -> f64 convert after
590
+ // wasm.splice(i + 1, Opcodes.i32_trunc_sat_f64_s);
591
+ // }
592
+ // }
593
+ // }
594
+ // }
595
+ // }
596
+
597
+ // if ((opcode === 0xfc02 || opcode === 0xfc03) && i >= 3) { // i32.trunc_sat_f64_s/u
598
+ // const o2 = wasm[i - 1][0];
599
+ // if (
600
+ // (o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
601
+ // (o2 >= 0x61 && o2 <= 0x66) // main f64 eq op
602
+ // ) {
603
+ // const o3 = wasm[i - 2][0];
604
+ // if (o3 === Opcodes.f64_const) { // f64.const
605
+ // const o4 = wasm[i - 3][0];
606
+ // if (o4 === Opcodes.f64_convert_i32_s || o4 === Opcodes.f64_convert_i32_u) {
607
+ // // remove now unneeded i32 -> f64 convert
608
+ // wasm.splice(i - 3, 1);
609
+ // i--;
610
+ // } else {
611
+ // // add now needed f64 -> i32 convert prior
612
+ // wasm.splice(i - 2, 0, Opcodes.i32_trunc_sat_f64_s);
613
+ // i++;
614
+ // }
615
+
616
+ // // convert f64.const -> i32.const
617
+ // const n = wasm[i - 2][1];
618
+ // wasm.splice(i - 2, 1, number(n, Valtype.i32));
619
+
620
+ // // convert math op from f64 to i32
621
+ // wasm[i - 1][0] = f64ToI32Op[wasm[i - 1][0]];
622
+
623
+ // // remove this now unneeded f64 -> i32 convert
624
+ // wasm.splice(i, 1);
625
+ // }
626
+ // }
627
+ // }
567
628
  }
568
629
  };
@@ -18,7 +18,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
18
18
  }
19
19
 
20
20
  return `(${params.map((x, i) => invValtype[x]).join(', ')}) -> (${returns.map(x => invValtype[x]).join(', ')})`;
21
- }
21
+ };
22
22
 
23
23
  let out = '', depth = 0;
24
24
  if (name) out += `${name}(${ind}) ${makeSignature(params, returns, locals)}\n`;
package/compiler/index.js CHANGED
@@ -132,9 +132,9 @@ export default (code, module = undefined) => {
132
132
 
133
133
  for (const x of funcs) {
134
134
  const preOps = x.wasm.length;
135
- cyclone(x.wasm);
135
+ cyclone(x);
136
136
 
137
- console.log(`${x.name}: ${preOps} -> ${x.wasm.length} ops`);
137
+ if (preOps !== x.wasm.length) console.log(`${x.name}: ${preOps} -> ${x.wasm.length} ops`);
138
138
  }
139
139
  opt(funcs, globals, pages, tags, exceptions);
140
140
 
@@ -144,7 +144,7 @@ export default (code, module = undefined) => {
144
144
  console.log(`cyclone size diff: ${oldSize - newSize} bytes (${oldSize} -> ${newSize})\n`);
145
145
  } else {
146
146
  for (const x of funcs) {
147
- cyclone(x.wasm);
147
+ cyclone(x);
148
148
  }
149
149
  }
150
150
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "An ahead-of-time JavaScript compiler",
4
- "version": "0.55.26",
4
+ "version": "0.55.28",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
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.55.26';
3
+ globalThis.version = '0.55.28';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {