porffor 0.2.0-964b4c2 → 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 +14 -3
- package/compiler/2c.js +6 -1
- package/compiler/codeGen.js +132 -136
- package/compiler/opt.js +10 -0
- package/compiler/parse.js +11 -12
- package/package.json +1 -1
- package/runner/repl.js +2 -2
- package/tmp.c +0 -69
package/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Porffor <sup><sub>/ˈpɔrfɔr/ *(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`)
|
package/compiler/2c.js
CHANGED
@@ -124,7 +124,7 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
124
124
|
const returns = f.returns.length > 0;
|
125
125
|
|
126
126
|
const shouldInline = f.internal;
|
127
|
-
out += `${f.name === 'main' ? 'int' : (f.internal ? 'double' : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
127
|
+
out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? 'double' : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
128
128
|
|
129
129
|
const localKeys = Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).slice(f.params.length).sort((a, b) => f.locals[a].idx - f.locals[b].idx);
|
130
130
|
for (const x of localKeys) {
|
@@ -367,6 +367,11 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
367
367
|
line(`return ${vals.pop()}`);
|
368
368
|
}
|
369
369
|
|
370
|
+
if (f.name === 'main') {
|
371
|
+
out += '\n';
|
372
|
+
line(`return 0`);
|
373
|
+
}
|
374
|
+
|
370
375
|
out += '}\n\n';
|
371
376
|
}
|
372
377
|
|
package/compiler/codeGen.js
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
387
|
+
setLastType(scope),
|
383
388
|
[ Opcodes.else ],
|
384
389
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
385
390
|
// note type
|
386
391
|
...leftType,
|
387
|
-
|
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,
|
@@ -1051,11 +1065,13 @@ const setType = (scope, _name, type) => {
|
|
1051
1065
|
|
1052
1066
|
const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
|
1053
1067
|
|
1068
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
|
1054
1069
|
if (scope.locals[name]) return [
|
1055
1070
|
...out,
|
1056
1071
|
[ Opcodes.local_set, scope.locals[name + '#type'].idx ]
|
1057
1072
|
];
|
1058
1073
|
|
1074
|
+
if (typedInput && globals[name]?.metadata?.type != null) return [];
|
1059
1075
|
if (globals[name]) return [
|
1060
1076
|
...out,
|
1061
1077
|
[ Opcodes.global_set, globals[name + '#type'].idx ]
|
@@ -1064,6 +1080,15 @@ const setType = (scope, _name, type) => {
|
|
1064
1080
|
// throw new Error('could not find var');
|
1065
1081
|
};
|
1066
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
|
+
|
1067
1092
|
const getNodeType = (scope, node) => {
|
1068
1093
|
const inner = () => {
|
1069
1094
|
if (node.type === 'Literal') {
|
@@ -1092,7 +1117,19 @@ const getNodeType = (scope, node) => {
|
|
1092
1117
|
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1093
1118
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1094
1119
|
|
1095
|
-
|
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) ];
|
1096
1133
|
|
1097
1134
|
// presume
|
1098
1135
|
// todo: warn here?
|
@@ -1177,7 +1214,7 @@ const getNodeType = (scope, node) => {
|
|
1177
1214
|
return TYPES.number;
|
1178
1215
|
}
|
1179
1216
|
|
1180
|
-
if (scope.locals['#last_type']) return [
|
1217
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1181
1218
|
|
1182
1219
|
// presume
|
1183
1220
|
// todo: warn here?
|
@@ -1356,13 +1393,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1356
1393
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1357
1394
|
out.push(
|
1358
1395
|
...getNodeType(scope, finalStatement),
|
1359
|
-
|
1396
|
+
setLastType(scope)
|
1360
1397
|
);
|
1361
1398
|
} else if (countLeftover(out) === 0) {
|
1362
1399
|
out.push(...number(UNDEFINED));
|
1363
1400
|
out.push(
|
1364
1401
|
...number(TYPES.undefined, Valtype.i32),
|
1365
|
-
|
1402
|
+
setLastType(scope)
|
1366
1403
|
);
|
1367
1404
|
}
|
1368
1405
|
|
@@ -1380,8 +1417,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1380
1417
|
if (name && name.startsWith('__')) {
|
1381
1418
|
const spl = name.slice(2).split('_');
|
1382
1419
|
|
1383
|
-
|
1384
|
-
protoName = func;
|
1420
|
+
protoName = spl[spl.length - 1];
|
1385
1421
|
|
1386
1422
|
target = { ...decl.callee };
|
1387
1423
|
target.name = spl.slice(0, -1).join('_');
|
@@ -1407,12 +1443,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1407
1443
|
Opcodes.i32_from_u,
|
1408
1444
|
|
1409
1445
|
...number(TYPES.boolean, Valtype.i32),
|
1410
|
-
|
1446
|
+
setLastType(scope)
|
1411
1447
|
];
|
1412
1448
|
}
|
1413
1449
|
|
1414
|
-
|
1415
|
-
protoName = func;
|
1450
|
+
protoName = decl.callee.property.name;
|
1416
1451
|
|
1417
1452
|
target = decl.callee.object;
|
1418
1453
|
}
|
@@ -1457,7 +1492,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1457
1492
|
...RTArrayUtil.getLength(getPointer),
|
1458
1493
|
|
1459
1494
|
...number(TYPES.number, Valtype.i32),
|
1460
|
-
|
1495
|
+
setLastType(scope)
|
1461
1496
|
];
|
1462
1497
|
continue;
|
1463
1498
|
}
|
@@ -1488,7 +1523,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1488
1523
|
...protoOut,
|
1489
1524
|
|
1490
1525
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1491
|
-
|
1526
|
+
setLastType(scope),
|
1492
1527
|
[ Opcodes.end ]
|
1493
1528
|
];
|
1494
1529
|
}
|
@@ -1589,7 +1624,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1589
1624
|
// ...number(type, Valtype.i32),
|
1590
1625
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1591
1626
|
// );
|
1592
|
-
} else out.push(
|
1627
|
+
} else out.push(setLastType(scope));
|
1593
1628
|
|
1594
1629
|
return out;
|
1595
1630
|
};
|
@@ -1614,7 +1649,28 @@ const unhackName = name => {
|
|
1614
1649
|
return name;
|
1615
1650
|
};
|
1616
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
|
+
|
1617
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
|
+
|
1618
1674
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
1619
1675
|
|
1620
1676
|
const out = [
|
@@ -1668,6 +1724,49 @@ const allocVar = (scope, name, global = false) => {
|
|
1668
1724
|
return idx;
|
1669
1725
|
};
|
1670
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
|
+
|
1671
1770
|
const generateVar = (scope, decl) => {
|
1672
1771
|
let out = [];
|
1673
1772
|
|
@@ -1704,6 +1803,10 @@ const generateVar = (scope, decl) => {
|
|
1704
1803
|
|
1705
1804
|
// hack: this follows spec properly but is mostly unneeded 😅
|
1706
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
|
+
}
|
1707
1810
|
}
|
1708
1811
|
|
1709
1812
|
return out;
|
@@ -1858,7 +1961,7 @@ const generateAssign = (scope, decl) => {
|
|
1858
1961
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1859
1962
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1860
1963
|
|
1861
|
-
|
1964
|
+
getLastType(scope),
|
1862
1965
|
// hack: type is idx+1
|
1863
1966
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
1864
1967
|
];
|
@@ -2025,7 +2128,7 @@ const generateConditional = (scope, decl) => {
|
|
2025
2128
|
// note type
|
2026
2129
|
out.push(
|
2027
2130
|
...getNodeType(scope, decl.consequent),
|
2028
|
-
|
2131
|
+
setLastType(scope)
|
2029
2132
|
);
|
2030
2133
|
|
2031
2134
|
out.push([ Opcodes.else ]);
|
@@ -2034,7 +2137,7 @@ const generateConditional = (scope, decl) => {
|
|
2034
2137
|
// note type
|
2035
2138
|
out.push(
|
2036
2139
|
...getNodeType(scope, decl.alternate),
|
2037
|
-
|
2140
|
+
setLastType(scope)
|
2038
2141
|
);
|
2039
2142
|
|
2040
2143
|
out.push([ Opcodes.end ]);
|
@@ -2505,7 +2608,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2505
2608
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2506
2609
|
|
2507
2610
|
...number(TYPES.number, Valtype.i32),
|
2508
|
-
|
2611
|
+
setLastType(scope)
|
2509
2612
|
],
|
2510
2613
|
|
2511
2614
|
[TYPES.string]: [
|
@@ -2537,7 +2640,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2537
2640
|
...number(newPointer),
|
2538
2641
|
|
2539
2642
|
...number(TYPES.string, Valtype.i32),
|
2540
|
-
|
2643
|
+
setLastType(scope)
|
2541
2644
|
],
|
2542
2645
|
|
2543
2646
|
default: [ [ Opcodes.unreachable ] ]
|
@@ -2586,7 +2689,7 @@ const generateFunc = (scope, decl) => {
|
|
2586
2689
|
if (decl.generator) return todo('generator functions are not supported');
|
2587
2690
|
|
2588
2691
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2589
|
-
const params = decl.params
|
2692
|
+
const params = decl.params ?? [];
|
2590
2693
|
|
2591
2694
|
// const innerScope = { ...scope };
|
2592
2695
|
// TODO: share scope/locals between !!!
|
@@ -2600,7 +2703,11 @@ const generateFunc = (scope, decl) => {
|
|
2600
2703
|
};
|
2601
2704
|
|
2602
2705
|
for (let i = 0; i < params.length; i++) {
|
2603
|
-
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
|
+
}
|
2604
2711
|
}
|
2605
2712
|
|
2606
2713
|
let body = objectHack(decl.body);
|
@@ -2639,117 +2746,6 @@ const generateFunc = (scope, decl) => {
|
|
2639
2746
|
);
|
2640
2747
|
}
|
2641
2748
|
|
2642
|
-
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
2643
|
-
let offset = 0, vecParams = 0;
|
2644
|
-
for (let i = 0; i < params.length; i++) {
|
2645
|
-
const name = params[i];
|
2646
|
-
const local = func.locals[name];
|
2647
|
-
if (local.type === Valtype.v128) {
|
2648
|
-
vecParams++;
|
2649
|
-
|
2650
|
-
/* wasm.unshift( // add v128 load for param
|
2651
|
-
[ Opcodes.i32_const, 0 ],
|
2652
|
-
[ ...Opcodes.v128_load, 0, i * 16 ],
|
2653
|
-
[ Opcodes.local_set, local.idx ]
|
2654
|
-
); */
|
2655
|
-
|
2656
|
-
// using params and replace_lane is noticably faster than just loading from memory (above) somehow
|
2657
|
-
|
2658
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2659
|
-
const { vecType } = local;
|
2660
|
-
let [ type, lanes ] = vecType.split('x');
|
2661
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2662
|
-
|
2663
|
-
lanes = parseInt(lanes);
|
2664
|
-
type = Valtype[type];
|
2665
|
-
|
2666
|
-
const name = params[i]; // get original param name
|
2667
|
-
|
2668
|
-
func.params.splice(offset, 1, ...new Array(lanes).fill(type)); // add new params of {type}, {lanes} times
|
2669
|
-
|
2670
|
-
// update index of original local
|
2671
|
-
// delete func.locals[name];
|
2672
|
-
|
2673
|
-
// add new locals for params
|
2674
|
-
for (let j = 0; j < lanes; j++) {
|
2675
|
-
func.locals[name + j] = { idx: offset + j, type, vecParamAutogen: true };
|
2676
|
-
}
|
2677
|
-
|
2678
|
-
// prepend wasm to generate expected v128 locals
|
2679
|
-
wasm.splice(i * 2 + offset * 2, 0,
|
2680
|
-
...i32x4(0, 0, 0, 0),
|
2681
|
-
...new Array(lanes).fill(0).flatMap((_, j) => [
|
2682
|
-
[ Opcodes.local_get, offset + j ],
|
2683
|
-
[ ...Opcodes[vecType + '_replace_lane'], j ]
|
2684
|
-
]),
|
2685
|
-
[ Opcodes.local_set, i ]
|
2686
|
-
);
|
2687
|
-
|
2688
|
-
offset += lanes;
|
2689
|
-
|
2690
|
-
// note: wrapping is disabled for now due to perf/dx concerns (so this will never run)
|
2691
|
-
/* if (!func.name.startsWith('#')) func.name = '##' + func.name;
|
2692
|
-
|
2693
|
-
// add vec type index to hash name prefix for wrapper to know how to wrap
|
2694
|
-
const vecTypeIdx = [ 'i8x16', 'i16x8', 'i32x4', 'i64x2', 'f32x4', 'f64x2' ].indexOf(local.vecType);
|
2695
|
-
const secondHash = func.name.slice(1).indexOf('#');
|
2696
|
-
func.name = '#' + func.name.slice(1, secondHash) + vecTypeIdx + func.name.slice(secondHash); */
|
2697
|
-
}
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
if (offset !== 0) {
|
2701
|
-
// bump local indexes for all other locals after
|
2702
|
-
for (const x in func.locals) {
|
2703
|
-
const local = func.locals[x];
|
2704
|
-
if (!local.vecParamAutogen) local.idx += offset;
|
2705
|
-
}
|
2706
|
-
|
2707
|
-
// bump local indexes in wasm local.get/set
|
2708
|
-
for (let j = 0; j < wasm.length; j++) {
|
2709
|
-
const inst = wasm[j];
|
2710
|
-
if (j < offset * 2 + vecParams * 2) {
|
2711
|
-
if (inst[0] === Opcodes.local_set) inst[1] += offset;
|
2712
|
-
continue;
|
2713
|
-
}
|
2714
|
-
|
2715
|
-
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) inst[1] += offset;
|
2716
|
-
}
|
2717
|
-
}
|
2718
|
-
|
2719
|
-
// change v128 return into many <type> instead as unsupported return valtype
|
2720
|
-
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]);
|
2721
|
-
if (lastReturnLocal && lastReturnLocal.type === Valtype.v128) {
|
2722
|
-
const name = Object.keys(func.locals)[Object.values(func.locals).indexOf(lastReturnLocal)];
|
2723
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2724
|
-
const { vecType } = lastReturnLocal;
|
2725
|
-
let [ type, lanes ] = vecType.split('x');
|
2726
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2727
|
-
|
2728
|
-
lanes = parseInt(lanes);
|
2729
|
-
type = Valtype[type];
|
2730
|
-
|
2731
|
-
const vecIdx = lastReturnLocal.idx;
|
2732
|
-
|
2733
|
-
const lastIdx = Math.max(0, ...Object.values(func.locals).map(x => x.idx));
|
2734
|
-
const tmpIdx = [];
|
2735
|
-
for (let i = 0; i < lanes; i++) {
|
2736
|
-
const idx = lastIdx + i + 1;
|
2737
|
-
tmpIdx.push(idx);
|
2738
|
-
func.locals[name + i] = { idx, type, vecReturnAutogen: true };
|
2739
|
-
}
|
2740
|
-
|
2741
|
-
wasm.splice(wasm.length - 1, 1,
|
2742
|
-
...new Array(lanes).fill(0).flatMap((_, i) => [
|
2743
|
-
i === 0 ? null : [ Opcodes.local_get, vecIdx ],
|
2744
|
-
[ ...Opcodes[vecType + '_extract_lane'], i ],
|
2745
|
-
[ Opcodes.local_set, tmpIdx[i] ],
|
2746
|
-
].filter(x => x !== null)),
|
2747
|
-
...new Array(lanes).fill(0).map((_, i) => [ Opcodes.local_get, tmpIdx[i]])
|
2748
|
-
);
|
2749
|
-
|
2750
|
-
func.returns = new Array(lanes).fill(type);
|
2751
|
-
}
|
2752
|
-
|
2753
2749
|
func.wasm = wasm;
|
2754
2750
|
|
2755
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
|
-
//
|
10
|
-
|
11
|
-
|
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
|
-
|
28
|
-
const
|
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
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 =
|
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,69 +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
|
-
|