porffor 0.2.0-9ca9aed → 0.2.0-a759814

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
@@ -1,5 +1,5 @@
1
1
  # Porffor &nbsp;<sup><sub>/ˈpɔrfɔr/ &nbsp;*(poor-for)*</sup></sub>
2
- A from-scratch experimental **AOT** optimizing JS -> Wasm/C engine/compiler/runtime in JS. Not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
2
+ A from-scratch experimental **AOT** optimizing JS/TS -> Wasm/C engine/compiler/runtime in JS. Not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
3
3
  Age: ~6 months (very on and off)
4
4
 
5
5
  ## Design
@@ -131,6 +131,14 @@ No particular order and no guarentees, just what could happen soon™
131
131
  - Smarter inline selection (snapshots?)
132
132
  - Remove const ifs (`if (true)`, etc)
133
133
  - Use type(script) information to remove unneeded typechecker code
134
+ - Cool proposals
135
+ - [Optional Chaining Assignment](https://github.com/tc39/proposal-optional-chaining-assignment)
136
+ - [Modulus and Additional Integer Math](https://github.com/tc39/proposal-integer-and-modulus-math)
137
+ - [Array Equality](https://github.com/tc39/proposal-array-equality)
138
+ - [Declarations in Conditionals](https://github.com/tc39/proposal-Declarations-in-Conditionals)
139
+ - [Seeded Pseudo-Random Numbers](https://github.com/tc39/proposal-seeded-random)
140
+ - [`do` expressions](https://github.com/tc39/proposal-do-expressions)
141
+ - [String Trim Characters](https://github.com/Kingwl/proposal-string-trim-characters)
134
142
 
135
143
  ## Performance
136
144
  *For the things it supports most of the time*, Porffor is blazingly fast compared to most interpreters, and common engines running without JIT. For those with JIT, it is not that much slower like a traditional interpreter would be; mostly the same or a bit faster/slower depending on what.
@@ -212,11 +220,14 @@ You can also use Deno (`deno run -A ...` instead of `node ...`), or Bun (`bun ..
212
220
  - `-target=native` only:
213
221
  - `-compiler=clang` to set compiler binary (path/name) to use to compile
214
222
  - `-cO=O3` to set compiler opt argument
223
+ - `-parser=acorn|@babel/parser|meriyah|hermes-parser` (default: `acorn`) to set which parser to use
224
+ - `-parse-types` to enable parsing type annotations/typescript. if `-parser` is unset, changes default to `@babel/parser`. does not type check
225
+ - `-opt-types` to perform optimizations using type annotations as compiler hints. does not type check
215
226
  - `-valtype=i32|i64|f64` (default: `f64`) to set valtype
216
227
  - `-O0` to disable opt
217
228
  - `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
218
- - `-O2` to enable advanced opt (inlining)
219
- - `-O3` to enable advanceder opt (precompute const math)
229
+ - `-O2` to enable advanced opt (inlining). unstable
230
+ - `-O3` to enable advanceder opt (precompute const math). unstable
220
231
  - `-no-run` to not run wasm output, just compile
221
232
  - `-opt-log` to log some opts
222
233
  - `-code-log` to log some codegen (you probably want `-funcs`)
@@ -214,6 +214,11 @@ const generate = (scope, decl, global = false, name = undefined) => {
214
214
  }
215
215
 
216
216
  default:
217
+ if (decl.type.startsWith('TS')) {
218
+ // ignore typescript nodes
219
+ return [];
220
+ }
221
+
217
222
  return todo(`no generation for ${decl.type}!`);
218
223
  }
219
224
  };
@@ -360,12 +365,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
360
365
  ...right,
361
366
  // note type
362
367
  ...rightType,
363
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
368
+ setLastType(scope),
364
369
  [ Opcodes.else ],
365
370
  [ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
366
371
  // note type
367
372
  ...leftType,
368
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
373
+ setLastType(scope),
369
374
  [ Opcodes.end ],
370
375
  Opcodes.i32_from
371
376
  ];
@@ -379,12 +384,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
379
384
  ...right,
380
385
  // note type
381
386
  ...rightType,
382
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
387
+ setLastType(scope),
383
388
  [ Opcodes.else ],
384
389
  [ Opcodes.local_get, localTmp(scope, 'logictmp') ],
385
390
  // note type
386
391
  ...leftType,
387
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
392
+ setLastType(scope),
388
393
  [ Opcodes.end ]
389
394
  ];
390
395
  };
@@ -852,7 +857,16 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
852
857
 
853
858
  let tmpLeft, tmpRight;
854
859
  // if equal op, check if strings for compareStrings
855
- if (op === '===' || op === '==' || op === '!==' || op === '!=') {
860
+ if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
861
+ const knownLeft = knownType(scope, leftType);
862
+ const knownRight = knownType(scope, rightType);
863
+
864
+ // todo: intelligent partial skip later
865
+ // if neither known are string, stop this madness
866
+ if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
867
+ return;
868
+ }
869
+
856
870
  tmpLeft = localTmp(scope, '__tmpop_left');
857
871
  tmpRight = localTmp(scope, '__tmpop_right');
858
872
 
@@ -902,7 +916,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
902
916
  // endOut.push(stringOnly([ Opcodes.end ]));
903
917
  endOut.unshift(stringOnly([ Opcodes.end ]));
904
918
  // }
905
- }
919
+ })();
906
920
 
907
921
  return finalise([
908
922
  ...left,
@@ -1013,18 +1027,7 @@ const TYPES = {
1013
1027
 
1014
1028
  // these are not "typeof" types but tracked internally
1015
1029
  _array: 0x10,
1016
- _regexp: 0x11,
1017
-
1018
- // typed arrays
1019
- _int8array: 0x20,
1020
- _uint8array: 0x21,
1021
- _uint8clampedarray: 0x22,
1022
- _int16array: 0x23,
1023
- _uint16array: 0x24,
1024
- _int32array: 0x25,
1025
- _uint32array: 0x26,
1026
- _float32array: 0x27,
1027
- _float64array: 0x28,
1030
+ _regexp: 0x11
1028
1031
  };
1029
1032
 
1030
1033
  const TYPE_NAMES = {
@@ -1062,11 +1065,13 @@ const setType = (scope, _name, type) => {
1062
1065
 
1063
1066
  const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
1064
1067
 
1068
+ if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
1065
1069
  if (scope.locals[name]) return [
1066
1070
  ...out,
1067
1071
  [ Opcodes.local_set, scope.locals[name + '#type'].idx ]
1068
1072
  ];
1069
1073
 
1074
+ if (typedInput && globals[name]?.metadata?.type != null) return [];
1070
1075
  if (globals[name]) return [
1071
1076
  ...out,
1072
1077
  [ Opcodes.global_set, globals[name + '#type'].idx ]
@@ -1075,6 +1080,15 @@ const setType = (scope, _name, type) => {
1075
1080
  // throw new Error('could not find var');
1076
1081
  };
1077
1082
 
1083
+ const getLastType = scope => {
1084
+ scope.gotLastType = true;
1085
+ return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
1086
+ };
1087
+
1088
+ const setLastType = scope => {
1089
+ return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
1090
+ };
1091
+
1078
1092
  const getNodeType = (scope, node) => {
1079
1093
  const inner = () => {
1080
1094
  if (node.type === 'Literal') {
@@ -1103,7 +1117,19 @@ const getNodeType = (scope, node) => {
1103
1117
  if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
1104
1118
  if (internalConstrs[name]) return internalConstrs[name].type;
1105
1119
 
1106
- if (scope.locals['#last_type']) return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
1120
+ // check if this is a prototype function
1121
+ // if so and there is only one impl (eg charCodeAt)
1122
+ // use that return type as that is the only possibility
1123
+ // (if non-matching type it would error out)
1124
+ if (name.startsWith('__')) {
1125
+ const spl = name.slice(2).split('_');
1126
+
1127
+ const func = spl[spl.length - 1];
1128
+ const protoFuncs = Object.values(prototypeFuncs).filter(x => x[func] != null);
1129
+ if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
1130
+ }
1131
+
1132
+ if (scope.locals['#last_type']) return [ getLastType(scope) ];
1107
1133
 
1108
1134
  // presume
1109
1135
  // todo: warn here?
@@ -1188,7 +1214,7 @@ const getNodeType = (scope, node) => {
1188
1214
  return TYPES.number;
1189
1215
  }
1190
1216
 
1191
- if (scope.locals['#last_type']) return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
1217
+ if (scope.locals['#last_type']) return [ getLastType(scope) ];
1192
1218
 
1193
1219
  // presume
1194
1220
  // todo: warn here?
@@ -1367,13 +1393,13 @@ const generateCall = (scope, decl, _global, _name) => {
1367
1393
  const finalStatement = parsed.body[parsed.body.length - 1];
1368
1394
  out.push(
1369
1395
  ...getNodeType(scope, finalStatement),
1370
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1396
+ setLastType(scope)
1371
1397
  );
1372
1398
  } else if (countLeftover(out) === 0) {
1373
1399
  out.push(...number(UNDEFINED));
1374
1400
  out.push(
1375
1401
  ...number(TYPES.undefined, Valtype.i32),
1376
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1402
+ setLastType(scope)
1377
1403
  );
1378
1404
  }
1379
1405
 
@@ -1391,8 +1417,7 @@ const generateCall = (scope, decl, _global, _name) => {
1391
1417
  if (name && name.startsWith('__')) {
1392
1418
  const spl = name.slice(2).split('_');
1393
1419
 
1394
- const func = spl[spl.length - 1];
1395
- protoName = func;
1420
+ protoName = spl[spl.length - 1];
1396
1421
 
1397
1422
  target = { ...decl.callee };
1398
1423
  target.name = spl.slice(0, -1).join('_');
@@ -1418,12 +1443,11 @@ const generateCall = (scope, decl, _global, _name) => {
1418
1443
  Opcodes.i32_from_u,
1419
1444
 
1420
1445
  ...number(TYPES.boolean, Valtype.i32),
1421
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1446
+ setLastType(scope)
1422
1447
  ];
1423
1448
  }
1424
1449
 
1425
- const func = decl.callee.property.name;
1426
- protoName = func;
1450
+ protoName = decl.callee.property.name;
1427
1451
 
1428
1452
  target = decl.callee.object;
1429
1453
  }
@@ -1468,7 +1492,7 @@ const generateCall = (scope, decl, _global, _name) => {
1468
1492
  ...RTArrayUtil.getLength(getPointer),
1469
1493
 
1470
1494
  ...number(TYPES.number, Valtype.i32),
1471
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
1495
+ setLastType(scope)
1472
1496
  ];
1473
1497
  continue;
1474
1498
  }
@@ -1499,7 +1523,7 @@ const generateCall = (scope, decl, _global, _name) => {
1499
1523
  ...protoOut,
1500
1524
 
1501
1525
  ...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
1502
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ],
1526
+ setLastType(scope),
1503
1527
  [ Opcodes.end ]
1504
1528
  ];
1505
1529
  }
@@ -1600,7 +1624,7 @@ const generateCall = (scope, decl, _global, _name) => {
1600
1624
  // ...number(type, Valtype.i32),
1601
1625
  // [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1602
1626
  // );
1603
- } else out.push([ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]);
1627
+ } else out.push(setLastType(scope));
1604
1628
 
1605
1629
  return out;
1606
1630
  };
@@ -1625,7 +1649,28 @@ const unhackName = name => {
1625
1649
  return name;
1626
1650
  };
1627
1651
 
1652
+ const knownType = (scope, type) => {
1653
+ if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
1654
+ return type[0][1];
1655
+ }
1656
+
1657
+ if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
1658
+ const idx = type[0][1];
1659
+
1660
+ // type idx = var idx + 1
1661
+ const v = Object.values(scope.locals).find(x => x.idx === idx - 1);
1662
+ if (v.metadata?.type != null) return v.metadata.type;
1663
+ }
1664
+
1665
+ return null;
1666
+ };
1667
+
1628
1668
  const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
1669
+ const known = knownType(scope, type);
1670
+ if (known != null) {
1671
+ return bc[known] ?? bc.default;
1672
+ }
1673
+
1629
1674
  const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
1630
1675
 
1631
1676
  const out = [
@@ -1679,6 +1724,49 @@ const allocVar = (scope, name, global = false) => {
1679
1724
  return idx;
1680
1725
  };
1681
1726
 
1727
+ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
1728
+ const target = global ? globals : scope.locals;
1729
+
1730
+ target[name].metadata ??= {};
1731
+ for (const x in metadata) {
1732
+ if (metadata[x] != null) target[name].metadata[x] = metadata[x];
1733
+ }
1734
+ };
1735
+
1736
+ const typeAnnoToPorfType = x => {
1737
+ if (TYPES[x]) return TYPES[x];
1738
+ if (TYPES['_' + x]) return TYPES['_' + x];
1739
+
1740
+ switch (x) {
1741
+ case 'i32':
1742
+ return TYPES.number;
1743
+ }
1744
+
1745
+ return null;
1746
+ };
1747
+
1748
+ const extractTypeAnnotation = decl => {
1749
+ let a = decl;
1750
+ while (a.typeAnnotation) a = a.typeAnnotation;
1751
+
1752
+ let type, elementType;
1753
+ if (a.typeName) {
1754
+ type = a.typeName.name;
1755
+ } else if (a.type.endsWith('Keyword')) {
1756
+ type = a.type.slice(2, -7).toLowerCase();
1757
+ } else if (a.type === 'TSArrayType') {
1758
+ type = 'array';
1759
+ elementType = extractTypeAnnotation(a.elementType).type;
1760
+ }
1761
+
1762
+ const typeName = type;
1763
+ type = typeAnnoToPorfType(type);
1764
+
1765
+ // if (decl.name) console.log(decl.name, { type, elementType });
1766
+
1767
+ return { type, typeName, elementType };
1768
+ };
1769
+
1682
1770
  const generateVar = (scope, decl) => {
1683
1771
  let out = [];
1684
1772
 
@@ -1715,6 +1803,10 @@ const generateVar = (scope, decl) => {
1715
1803
 
1716
1804
  // hack: this follows spec properly but is mostly unneeded 😅
1717
1805
  // out.push(...setType(scope, name, x.init ? getNodeType(scope, x.init) : TYPES.undefined));
1806
+
1807
+ if (typedInput && x.id.typeAnnotation) {
1808
+ addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
1809
+ }
1718
1810
  }
1719
1811
 
1720
1812
  return out;
@@ -1869,7 +1961,7 @@ const generateAssign = (scope, decl) => {
1869
1961
  ], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
1870
1962
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
1871
1963
 
1872
- [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ],
1964
+ getLastType(scope),
1873
1965
  // hack: type is idx+1
1874
1966
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
1875
1967
  ];
@@ -2036,7 +2128,7 @@ const generateConditional = (scope, decl) => {
2036
2128
  // note type
2037
2129
  out.push(
2038
2130
  ...getNodeType(scope, decl.consequent),
2039
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
2131
+ setLastType(scope)
2040
2132
  );
2041
2133
 
2042
2134
  out.push([ Opcodes.else ]);
@@ -2045,7 +2137,7 @@ const generateConditional = (scope, decl) => {
2045
2137
  // note type
2046
2138
  out.push(
2047
2139
  ...getNodeType(scope, decl.alternate),
2048
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
2140
+ setLastType(scope)
2049
2141
  );
2050
2142
 
2051
2143
  out.push([ Opcodes.end ]);
@@ -2516,7 +2608,7 @@ export const generateMember = (scope, decl, _global, _name) => {
2516
2608
  [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
2517
2609
 
2518
2610
  ...number(TYPES.number, Valtype.i32),
2519
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
2611
+ setLastType(scope)
2520
2612
  ],
2521
2613
 
2522
2614
  [TYPES.string]: [
@@ -2548,7 +2640,7 @@ export const generateMember = (scope, decl, _global, _name) => {
2548
2640
  ...number(newPointer),
2549
2641
 
2550
2642
  ...number(TYPES.string, Valtype.i32),
2551
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
2643
+ setLastType(scope)
2552
2644
  ],
2553
2645
 
2554
2646
  default: [ [ Opcodes.unreachable ] ]
@@ -2597,7 +2689,7 @@ const generateFunc = (scope, decl) => {
2597
2689
  if (decl.generator) return todo('generator functions are not supported');
2598
2690
 
2599
2691
  const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
2600
- const params = decl.params?.map(x => x.name) ?? [];
2692
+ const params = decl.params ?? [];
2601
2693
 
2602
2694
  // const innerScope = { ...scope };
2603
2695
  // TODO: share scope/locals between !!!
@@ -2611,7 +2703,11 @@ const generateFunc = (scope, decl) => {
2611
2703
  };
2612
2704
 
2613
2705
  for (let i = 0; i < params.length; i++) {
2614
- allocVar(innerScope, params[i], false);
2706
+ allocVar(innerScope, params[i].name, false);
2707
+
2708
+ if (typedInput && params[i].typeAnnotation) {
2709
+ addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
2710
+ }
2615
2711
  }
2616
2712
 
2617
2713
  let body = objectHack(decl.body);
@@ -2650,117 +2746,6 @@ const generateFunc = (scope, decl) => {
2650
2746
  );
2651
2747
  }
2652
2748
 
2653
- // change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
2654
- let offset = 0, vecParams = 0;
2655
- for (let i = 0; i < params.length; i++) {
2656
- const name = params[i];
2657
- const local = func.locals[name];
2658
- if (local.type === Valtype.v128) {
2659
- vecParams++;
2660
-
2661
- /* wasm.unshift( // add v128 load for param
2662
- [ Opcodes.i32_const, 0 ],
2663
- [ ...Opcodes.v128_load, 0, i * 16 ],
2664
- [ Opcodes.local_set, local.idx ]
2665
- ); */
2666
-
2667
- // using params and replace_lane is noticably faster than just loading from memory (above) somehow
2668
-
2669
- // extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
2670
- const { vecType } = local;
2671
- let [ type, lanes ] = vecType.split('x');
2672
- if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
2673
-
2674
- lanes = parseInt(lanes);
2675
- type = Valtype[type];
2676
-
2677
- const name = params[i]; // get original param name
2678
-
2679
- func.params.splice(offset, 1, ...new Array(lanes).fill(type)); // add new params of {type}, {lanes} times
2680
-
2681
- // update index of original local
2682
- // delete func.locals[name];
2683
-
2684
- // add new locals for params
2685
- for (let j = 0; j < lanes; j++) {
2686
- func.locals[name + j] = { idx: offset + j, type, vecParamAutogen: true };
2687
- }
2688
-
2689
- // prepend wasm to generate expected v128 locals
2690
- wasm.splice(i * 2 + offset * 2, 0,
2691
- ...i32x4(0, 0, 0, 0),
2692
- ...new Array(lanes).fill(0).flatMap((_, j) => [
2693
- [ Opcodes.local_get, offset + j ],
2694
- [ ...Opcodes[vecType + '_replace_lane'], j ]
2695
- ]),
2696
- [ Opcodes.local_set, i ]
2697
- );
2698
-
2699
- offset += lanes;
2700
-
2701
- // note: wrapping is disabled for now due to perf/dx concerns (so this will never run)
2702
- /* if (!func.name.startsWith('#')) func.name = '##' + func.name;
2703
-
2704
- // add vec type index to hash name prefix for wrapper to know how to wrap
2705
- const vecTypeIdx = [ 'i8x16', 'i16x8', 'i32x4', 'i64x2', 'f32x4', 'f64x2' ].indexOf(local.vecType);
2706
- const secondHash = func.name.slice(1).indexOf('#');
2707
- func.name = '#' + func.name.slice(1, secondHash) + vecTypeIdx + func.name.slice(secondHash); */
2708
- }
2709
- }
2710
-
2711
- if (offset !== 0) {
2712
- // bump local indexes for all other locals after
2713
- for (const x in func.locals) {
2714
- const local = func.locals[x];
2715
- if (!local.vecParamAutogen) local.idx += offset;
2716
- }
2717
-
2718
- // bump local indexes in wasm local.get/set
2719
- for (let j = 0; j < wasm.length; j++) {
2720
- const inst = wasm[j];
2721
- if (j < offset * 2 + vecParams * 2) {
2722
- if (inst[0] === Opcodes.local_set) inst[1] += offset;
2723
- continue;
2724
- }
2725
-
2726
- if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) inst[1] += offset;
2727
- }
2728
- }
2729
-
2730
- // change v128 return into many <type> instead as unsupported return valtype
2731
- const lastReturnLocal = wasm.length > 2 && wasm[wasm.length - 1][0] === Opcodes.return && Object.values(func.locals).find(x => x.idx === wasm[wasm.length - 2][1]);
2732
- if (lastReturnLocal && lastReturnLocal.type === Valtype.v128) {
2733
- const name = Object.keys(func.locals)[Object.values(func.locals).indexOf(lastReturnLocal)];
2734
- // extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
2735
- const { vecType } = lastReturnLocal;
2736
- let [ type, lanes ] = vecType.split('x');
2737
- if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
2738
-
2739
- lanes = parseInt(lanes);
2740
- type = Valtype[type];
2741
-
2742
- const vecIdx = lastReturnLocal.idx;
2743
-
2744
- const lastIdx = Math.max(0, ...Object.values(func.locals).map(x => x.idx));
2745
- const tmpIdx = [];
2746
- for (let i = 0; i < lanes; i++) {
2747
- const idx = lastIdx + i + 1;
2748
- tmpIdx.push(idx);
2749
- func.locals[name + i] = { idx, type, vecReturnAutogen: true };
2750
- }
2751
-
2752
- wasm.splice(wasm.length - 1, 1,
2753
- ...new Array(lanes).fill(0).flatMap((_, i) => [
2754
- i === 0 ? null : [ Opcodes.local_get, vecIdx ],
2755
- [ ...Opcodes[vecType + '_extract_lane'], i ],
2756
- [ Opcodes.local_set, tmpIdx[i] ],
2757
- ].filter(x => x !== null)),
2758
- ...new Array(lanes).fill(0).map((_, i) => [ Opcodes.local_get, tmpIdx[i]])
2759
- );
2760
-
2761
- func.returns = new Array(lanes).fill(type);
2762
- }
2763
-
2764
2749
  func.wasm = wasm;
2765
2750
 
2766
2751
  funcs.push(func);
package/compiler/opt.js CHANGED
@@ -103,6 +103,8 @@ export default (funcs, globals, pages) => {
103
103
  for (const f of funcs) {
104
104
  const wasm = f.wasm;
105
105
 
106
+ const lastType = f.locals['#last_type'];
107
+
106
108
  let runs = 2; // how many by default? add arg?
107
109
  while (runs > 0) {
108
110
  runs--;
@@ -221,6 +223,7 @@ export default (funcs, globals, pages) => {
221
223
  }
222
224
 
223
225
  if (checks === 0) {
226
+ // todo: review indexes below
224
227
  wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
225
228
  wasm.splice(i - 1, 1); // remove this inst
226
229
 
@@ -231,6 +234,13 @@ export default (funcs, globals, pages) => {
231
234
  }
232
235
  }
233
236
 
237
+ // remove setting last type if it is never gotten
238
+ if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType.idx) {
239
+ // replace this inst with drop
240
+ wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
241
+ if (i > 0) i--;
242
+ }
243
+
234
244
  if (i < 1) continue;
235
245
  let lastInst = wasm[i - 1];
236
246
 
package/compiler/parse.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { log } from "./log.js";
2
+ // import { parse } from 'acorn';
2
3
 
3
4
  // deno compat
4
5
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
@@ -6,16 +7,9 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
6
7
  globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
7
8
  }
8
9
 
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();
10
+ // 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');
19
13
 
20
14
  // todo: review which to use by default
21
15
  // supported parsers:
@@ -24,8 +18,13 @@ await loadParser();
24
18
  // - hermes-parser
25
19
  // - @babel/parser
26
20
 
27
- // should we try to support types (while parsing)
28
- const types = process.argv.includes('-types');
21
+ let parser, parse;
22
+ const loadParser = async (fallbackParser = 'acorn', forceParser) => {
23
+ parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
24
+ 0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
25
+ };
26
+ globalThis._porf_loadParser = loadParser;
27
+ await loadParser(types ? '@babel/parser' : undefined);
29
28
 
30
29
  if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parser', `passed -types with a parser (${parser}) which does not support`);
31
30
 
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.2.0-9ca9aed",
4
+ "version": "0.2.0-a759814",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "dependencies": {
package/runner/repl.js CHANGED
@@ -45,9 +45,9 @@ let prev = '';
45
45
  const run = async (source, _context, _filename, callback, run = true) => {
46
46
  // hack: print "secret" before latest code ran to only enable printing for new code
47
47
 
48
- let toRun = prev + `;\nprint(-0x1337);\n` + source.trim();
48
+ let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source.trim();
49
49
 
50
- let shouldPrint = false;
50
+ let shouldPrint = !prev;
51
51
  const { exports, wasm, pages } = await compile(toRun, [], {}, str => {
52
52
  if (shouldPrint) process.stdout.write(str);
53
53
  if (str === '-4919') shouldPrint = true;
package/tmp.c DELETED
@@ -1,71 +0,0 @@
1
-
2
- #include <stdio.h>
3
-
4
- struct ReturnValue {
5
- double value;
6
- long type;
7
- };
8
-
9
- double sum = 0;
10
- long sumdtype = 0;
11
- double counter = 0;
12
- long counterdtype = 0;
13
-
14
- double inline f64_f(double x, double y) {
15
- return (x - ((int)(x / y) * y));
16
- }
17
-
18
- struct ReturnValue isPrime(double number, long numberdtype) {
19
- double i = 0;
20
- long idtype = 0;
21
- double __tmpop_left = 0;
22
- double __tmpop_right = 0;
23
- long compare_left_pointer = 0;
24
- long compare_left_length = 0;
25
- long compare_right_pointer = 0;
26
- long compare_right_length = 0;
27
- long compare_index = 0;
28
- long compare_index_end = 0;
29
-
30
- if (number < 2e+0) {
31
- return (struct ReturnValue){ 1, 0e+0 };
32
- }
33
- i = 2e+0;
34
- idtype = 0;
35
- while (i < number) {
36
- if (f64_f(number, i) == 0e+0) {
37
- return (struct ReturnValue){ 1, 0e+0 };
38
- }
39
- i = i + 1e+0;
40
- }
41
- return (struct ReturnValue){ 1, 1e+0 };
42
- }
43
-
44
- double inline __console_log(double x) {
45
- printf("%f\n", x);
46
- printf("%c", (int)(1e+1));
47
- }
48
-
49
- int main() {
50
- long dlast_type = 0;
51
- double elogicinner_tmp = 0;
52
- long dtypeswitch_tmp = 0;
53
-
54
- sum = 0e+0;
55
- sumdtype = 0;
56
- counter = 0e+0;
57
- counterdtype = 0;
58
- while (counter <= 1e+5) {
59
- const struct ReturnValue _ = isPrime(counter, counterdtype);
60
- dlast_type = _.type;
61
- if ((unsigned long)(elogicinner_tmp = _.value) == 1e+0) {
62
- sum = sum + counter;
63
- sumdtype = 0;
64
- }
65
- counter = counter + 1e+0;
66
- }
67
- __console_log(sum);
68
-
69
- return 0;
70
- }
71
-