porffor 0.16.0-97bb4f33b → 0.16.0-a7bc359af
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/CONTRIBUTING.md +1 -1
- package/compiler/codegen.js +6 -9
- package/compiler/cyclone.js +12 -12
- package/compiler/havoc.js +1 -1
- package/compiler/index.js +29 -4
- package/compiler/parse.js +1 -1
- package/compiler/pgo.js +11 -10
- package/compiler/precompile.js +3 -1
- package/compiler/wrap.js +1 -1
- package/package.json +3 -5
- package/runner/index.js +10 -3
package/CONTRIBUTING.md
CHANGED
@@ -26,7 +26,7 @@ You can also swap out `node` in the alias to use another runtime like Deno (`den
|
|
26
26
|
|
27
27
|
### Precompile
|
28
28
|
|
29
|
-
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run
|
29
|
+
**If you update any file inside `compiler/builtins` you will need to do this for it to update inside Porffor otherwise your changes will have no effect.** Run `./porf precompile` to precompile. It may error during this, if so, you might have an error in your code or there could be a compiler error with Porffor (feel free to ask for help as soon as you encounter any errors with it).
|
30
30
|
|
31
31
|
<br>
|
32
32
|
|
package/compiler/codegen.js
CHANGED
@@ -2115,7 +2115,6 @@ const brTable = (input, bc, returns) => {
|
|
2115
2115
|
}
|
2116
2116
|
|
2117
2117
|
for (let i = 0; i < count; i++) {
|
2118
|
-
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2119
2118
|
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2120
2119
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2121
2120
|
}
|
@@ -2149,10 +2148,8 @@ const brTable = (input, bc, returns) => {
|
|
2149
2148
|
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
2150
2149
|
);
|
2151
2150
|
|
2152
|
-
//
|
2153
|
-
// (
|
2154
|
-
// dm me and if you are correct and the first person
|
2155
|
-
// I will somehow shout you out or something
|
2151
|
+
// sort the wrong way and then reverse
|
2152
|
+
// so strings ('default') are at the start before any numbers
|
2156
2153
|
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
2157
2154
|
|
2158
2155
|
br = count - 1;
|
@@ -2178,7 +2175,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2178
2175
|
return bc[known] ?? bc.default;
|
2179
2176
|
}
|
2180
2177
|
|
2181
|
-
if (Prefs.
|
2178
|
+
if (Prefs.typeswitchBrtable)
|
2182
2179
|
return brTable(type, bc, returns);
|
2183
2180
|
|
2184
2181
|
const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
|
@@ -3270,7 +3267,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3270
3267
|
firstAssign = true;
|
3271
3268
|
|
3272
3269
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3273
|
-
const uniqueName = name === '$undeclared' ? name +
|
3270
|
+
const uniqueName = name === '$undeclared' ? name + randId() : name;
|
3274
3271
|
|
3275
3272
|
let page;
|
3276
3273
|
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
@@ -3658,7 +3655,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3658
3655
|
});
|
3659
3656
|
};
|
3660
3657
|
|
3661
|
-
const randId = () => Math.random().toString(16).slice(
|
3658
|
+
const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
|
3662
3659
|
|
3663
3660
|
const objectHack = node => {
|
3664
3661
|
if (!node) return node;
|
@@ -3710,7 +3707,7 @@ const generateFunc = (scope, decl) => {
|
|
3710
3707
|
if (decl.async) return todo(scope, 'async functions are not supported');
|
3711
3708
|
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3712
3709
|
|
3713
|
-
const name = decl.id ? decl.id.name : `
|
3710
|
+
const name = decl.id ? decl.id.name : `anonymous${randId()}`;
|
3714
3711
|
const params = decl.params ?? [];
|
3715
3712
|
|
3716
3713
|
// TODO: share scope/locals between !!!
|
package/compiler/cyclone.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
// cyclone: wasm partial constant evaluator
|
1
|
+
// cyclone: wasm partial constant evaluator (it is fast and dangerous hence "cyclone")
|
2
2
|
import { signedLEB128, ieee754_binary64, read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
|
3
3
|
import { Opcodes, Valtype } from './wasmSpec.js';
|
4
4
|
|
@@ -246,7 +246,7 @@ export default wasm => {
|
|
246
246
|
const [ b, a ] = pop2();
|
247
247
|
const v = bool(a === b);
|
248
248
|
|
249
|
-
replaceVal(v, Valtype.
|
249
|
+
replaceVal(v, Valtype.i32);
|
250
250
|
push(v);
|
251
251
|
break;
|
252
252
|
}
|
@@ -255,7 +255,7 @@ export default wasm => {
|
|
255
255
|
const [ b, a ] = pop2();
|
256
256
|
const v = bool(a !== b);
|
257
257
|
|
258
|
-
replaceVal(v, Valtype.
|
258
|
+
replaceVal(v, Valtype.i32);
|
259
259
|
push(v);
|
260
260
|
break;
|
261
261
|
}
|
@@ -264,7 +264,7 @@ export default wasm => {
|
|
264
264
|
const [ b, a ] = pop2();
|
265
265
|
const v = bool(a < b);
|
266
266
|
|
267
|
-
replaceVal(v, Valtype.
|
267
|
+
replaceVal(v, Valtype.i32);
|
268
268
|
push(v);
|
269
269
|
break;
|
270
270
|
}
|
@@ -273,7 +273,7 @@ export default wasm => {
|
|
273
273
|
const [ b, a ] = pop2();
|
274
274
|
const v = bool(a <= b);
|
275
275
|
|
276
|
-
replaceVal(v, Valtype.
|
276
|
+
replaceVal(v, Valtype.i32);
|
277
277
|
push(v);
|
278
278
|
break;
|
279
279
|
}
|
@@ -282,7 +282,7 @@ export default wasm => {
|
|
282
282
|
const [ b, a ] = pop2();
|
283
283
|
const v = bool(a > b);
|
284
284
|
|
285
|
-
replaceVal(v, Valtype.
|
285
|
+
replaceVal(v, Valtype.i32);
|
286
286
|
push(v);
|
287
287
|
break;
|
288
288
|
}
|
@@ -291,7 +291,7 @@ export default wasm => {
|
|
291
291
|
const [ b, a ] = pop2();
|
292
292
|
const v = bool(a >= b);
|
293
293
|
|
294
|
-
replaceVal(v, Valtype.
|
294
|
+
replaceVal(v, Valtype.i32);
|
295
295
|
push(v);
|
296
296
|
break;
|
297
297
|
}
|
@@ -404,7 +404,7 @@ export default wasm => {
|
|
404
404
|
case Opcodes.f64_max: {
|
405
405
|
if (stack.length < 2) { empty(); break; };
|
406
406
|
const [ b, a ] = pop2();
|
407
|
-
const v = a
|
407
|
+
const v = Math.max(a, b);
|
408
408
|
|
409
409
|
replaceVal(v, Valtype.f64);
|
410
410
|
push(v);
|
@@ -467,9 +467,9 @@ export default wasm => {
|
|
467
467
|
// i32.const 1
|
468
468
|
// i32.add
|
469
469
|
// local.set 7 ;; $i (i32)
|
470
|
-
if (
|
471
|
-
(opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
|
472
|
-
(opcode >= 0x61 && opcode <= 0x66) // main f64 eq op
|
470
|
+
if (i >= 2 &&
|
471
|
+
((opcode >= 0xa0 && opcode <= 0xa3) || // main f64 math op
|
472
|
+
(opcode >= 0x61 && opcode <= 0x66)) // main f64 eq op
|
473
473
|
) {
|
474
474
|
const o2 = wasm[i - 1][0];
|
475
475
|
if (o2 === Opcodes.f64_const) { // f64.const
|
@@ -500,7 +500,7 @@ export default wasm => {
|
|
500
500
|
}
|
501
501
|
}
|
502
502
|
|
503
|
-
if ((opcode === 0xfc02 || opcode === 0xfc03) && i
|
503
|
+
if ((opcode === 0xfc02 || opcode === 0xfc03) && i >= 3) { // i32.trunc_sat_f64_s/u
|
504
504
|
const o2 = wasm[i - 1][0];
|
505
505
|
if (
|
506
506
|
(o2 >= 0xa0 && o2 <= 0xa3) || // main f64 math op
|
package/compiler/havoc.js
CHANGED
package/compiler/index.js
CHANGED
@@ -27,7 +27,10 @@ const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs'))
|
|
27
27
|
const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
28
28
|
|
29
29
|
export default (code, flags) => {
|
30
|
-
|
30
|
+
let target = Prefs.target ?? 'wasm';
|
31
|
+
if (Prefs.native) target = 'native';
|
32
|
+
|
33
|
+
let outFile = Prefs.o;
|
31
34
|
|
32
35
|
globalThis.valtype = 'f64';
|
33
36
|
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
@@ -113,8 +116,6 @@ export default (code, flags) => {
|
|
113
116
|
|
114
117
|
const out = { wasm, funcs, globals, tags, exceptions, pages, data };
|
115
118
|
|
116
|
-
const outFile = Prefs.o;
|
117
|
-
|
118
119
|
if (target === 'wasm' && outFile) {
|
119
120
|
fs.writeFileSync(outFile, Buffer.from(wasm));
|
120
121
|
|
@@ -135,6 +136,8 @@ export default (code, flags) => {
|
|
135
136
|
}
|
136
137
|
|
137
138
|
if (target === 'native') {
|
139
|
+
outFile ??= Prefs.native ? './porffor_tmp' : file.split('/').at(-1).split('.').at(0, -1).join('.');
|
140
|
+
|
138
141
|
let compiler = Prefs.compiler ?? 'clang';
|
139
142
|
const cO = Prefs._cO ?? 'Ofast';
|
140
143
|
|
@@ -153,7 +156,29 @@ export default (code, flags) => {
|
|
153
156
|
|
154
157
|
fs.unlinkSync(tmpfile);
|
155
158
|
|
156
|
-
if (process.version)
|
159
|
+
if (process.version) {
|
160
|
+
if (Prefs.native) {
|
161
|
+
const cleanup = () => {
|
162
|
+
try {
|
163
|
+
fs.unlinkSync(outFile);
|
164
|
+
} catch {}
|
165
|
+
};
|
166
|
+
|
167
|
+
process.on('exit', cleanup);
|
168
|
+
process.on('beforeExit', cleanup);
|
169
|
+
process.on('SIGINT', () => {
|
170
|
+
cleanup();
|
171
|
+
process.exit();
|
172
|
+
});
|
173
|
+
|
174
|
+
const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
|
175
|
+
try {
|
176
|
+
execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
|
177
|
+
} catch {}
|
178
|
+
}
|
179
|
+
|
180
|
+
process.exit();
|
181
|
+
}
|
157
182
|
}
|
158
183
|
|
159
184
|
return out;
|
package/compiler/parse.js
CHANGED
@@ -43,7 +43,7 @@ export default (input, flags) => {
|
|
43
43
|
webcompat: true,
|
44
44
|
|
45
45
|
// babel
|
46
|
-
plugins: types ? ['estree', 'typescript'] : ['estree'],
|
46
|
+
plugins: types || flags.includes('typed') ? ['estree', 'typescript'] : ['estree'],
|
47
47
|
|
48
48
|
// multiple
|
49
49
|
sourceType: flags.includes('module') ? 'module' : 'script',
|
package/compiler/pgo.js
CHANGED
@@ -11,7 +11,7 @@ export const setup = () => {
|
|
11
11
|
|
12
12
|
// enable these prefs by default for pgo
|
13
13
|
Prefs.typeswitchUniqueTmp = Prefs.typeswitchUniqueTmp === false ? false : true;
|
14
|
-
Prefs.cyclone = Prefs.cyclone === false ? false : true
|
14
|
+
Prefs.cyclone = Prefs.cyclone === false ? false : true;
|
15
15
|
};
|
16
16
|
|
17
17
|
export const run = obj => {
|
@@ -87,9 +87,10 @@ export const run = obj => {
|
|
87
87
|
localData[activeFunc][i].push(n);
|
88
88
|
},
|
89
89
|
w: (ind, outPtr) => { // readArgv
|
90
|
-
const
|
90
|
+
const pgoInd = process.argv.indexOf('--pgo');
|
91
|
+
const args = process.argv.slice(pgoInd).filter(x => !x.startsWith('-'));
|
91
92
|
const str = args[ind - 1];
|
92
|
-
if (!str) {
|
93
|
+
if (pgoInd === -1 || !str) {
|
93
94
|
if (Prefs.pgoLog) console.log('\nPGO warning: script was expecting arguments, please specify args to use for PGO after --pgo arg');
|
94
95
|
return -1;
|
95
96
|
}
|
@@ -130,16 +131,18 @@ export const run = obj => {
|
|
130
131
|
func.localValues = localValues;
|
131
132
|
|
132
133
|
let counts = new Array(10).fill(0);
|
133
|
-
const consistents = localData[i].map(x => {
|
134
|
-
if (
|
134
|
+
const consistents = localData[i].map((x, j) => {
|
135
|
+
if (j < func.params.length) return false; // param
|
136
|
+
if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false; // not consistent
|
135
137
|
|
136
138
|
counts[0]++;
|
137
139
|
return x[0];
|
138
140
|
});
|
139
141
|
|
140
142
|
const integerOnlyF64s = localData[i].map((x, j) => {
|
143
|
+
if (j < func.params.length) return false; // param
|
141
144
|
if (localValues[j].type === Valtype.i32) return false; // already i32
|
142
|
-
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false;
|
145
|
+
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false; // not all integer values
|
143
146
|
|
144
147
|
counts[1]++;
|
145
148
|
return true;
|
@@ -150,14 +153,14 @@ export const run = obj => {
|
|
150
153
|
|
151
154
|
log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
|
152
155
|
if (Prefs.verbosePgo) {
|
153
|
-
for (let j =
|
156
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
154
157
|
log += ` ${consistents[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${new Set(localData[i][j]).size} unique values set\n`;
|
155
158
|
}
|
156
159
|
}
|
157
160
|
|
158
161
|
log += ` ${func.name}: identified ${counts[1]}/${localValues.reduce((acc, x) => acc + (x.type === Valtype.f64 ? 1 : 0), 0)} f64 locals as integer usage only${Prefs.verbosePgo ? ':' : ''}\n`;
|
159
162
|
if (Prefs.verbosePgo) {
|
160
|
-
for (let j =
|
163
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
161
164
|
if (localValues[j].type !== Valtype.f64) continue;
|
162
165
|
log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
|
163
166
|
}
|
@@ -177,7 +180,6 @@ export const run = obj => {
|
|
177
180
|
for (let i = 0; i < x.integerOnlyF64s.length; i++) {
|
178
181
|
const c = x.integerOnlyF64s[i];
|
179
182
|
if (c === false) continue;
|
180
|
-
if (i < x.params.length) continue;
|
181
183
|
|
182
184
|
targets.push(i);
|
183
185
|
}
|
@@ -190,7 +192,6 @@ export const run = obj => {
|
|
190
192
|
for (let i = 0; i < x.consistents.length; i++) {
|
191
193
|
const c = x.consistents[i];
|
192
194
|
if (c === false) continue;
|
193
|
-
if (i < x.params.length) continue;
|
194
195
|
|
195
196
|
targets.push(i);
|
196
197
|
|
package/compiler/precompile.js
CHANGED
@@ -26,7 +26,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
26
26
|
|
27
27
|
const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
|
28
28
|
|
29
|
-
let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
|
29
|
+
let { funcs, globals, data, exceptions } = porfCompile(source, ['module', 'typed']);
|
30
30
|
|
31
31
|
const allocated = new Set();
|
32
32
|
|
@@ -87,6 +87,8 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
87
87
|
};
|
88
88
|
|
89
89
|
const precompile = async () => {
|
90
|
+
if (globalThis._porf_loadParser) await globalThis._porf_loadParser('@babel/parser');
|
91
|
+
|
90
92
|
const dir = join(__dirname, 'builtins');
|
91
93
|
|
92
94
|
let funcs = [], globals = [];
|
package/compiler/wrap.js
CHANGED
@@ -128,7 +128,7 @@ export default (source, flags = [ 'module' ], customImports = {}, print = str =>
|
|
128
128
|
|
129
129
|
if (source.includes?.('export ')) flags.push('module');
|
130
130
|
|
131
|
-
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
131
|
+
// fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
132
132
|
|
133
133
|
times.push(performance.now() - t1);
|
134
134
|
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
package/package.json
CHANGED
@@ -1,12 +1,10 @@
|
|
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.16.0-
|
4
|
+
"version": "0.16.0-a7bc359af",
|
5
5
|
"author": "CanadaHonk",
|
6
6
|
"license": "MIT",
|
7
|
-
"scripts": {
|
8
|
-
"precompile": "node ./compiler/precompile.js"
|
9
|
-
},
|
7
|
+
"scripts": {},
|
10
8
|
"dependencies": {
|
11
9
|
"acorn": "^8.11.3",
|
12
10
|
"node-repl-polyfill": "^0.1.1"
|
@@ -28,5 +26,5 @@
|
|
28
26
|
"bugs": {
|
29
27
|
"url": "https://github.com/CanadaHonk/porffor/issues"
|
30
28
|
},
|
31
|
-
"homepage": "https://porffor.
|
29
|
+
"homepage": "https://porffor.dev"
|
32
30
|
}
|
package/runner/index.js
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
|
3
|
-
import compile from '../compiler/wrap.js';
|
4
2
|
import fs from 'node:fs';
|
5
3
|
|
6
4
|
const start = performance.now();
|
@@ -59,10 +57,15 @@ if (process.argv.includes('--help')) {
|
|
59
57
|
}
|
60
58
|
|
61
59
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
62
|
-
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
60
|
+
if (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
63
61
|
// remove this arg
|
64
62
|
process.argv.splice(process.argv.indexOf(file), 1);
|
65
63
|
|
64
|
+
if (file === 'precompile') {
|
65
|
+
await import('../compiler/precompile.js');
|
66
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
67
|
+
}
|
68
|
+
|
66
69
|
if (file === 'profile') {
|
67
70
|
await import('./profile.js');
|
68
71
|
await new Promise(() => {}); // do nothing for the rest of this file
|
@@ -89,6 +92,8 @@ if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(fi
|
|
89
92
|
}
|
90
93
|
}
|
91
94
|
|
95
|
+
globalThis.file = file;
|
96
|
+
|
92
97
|
if (!file) {
|
93
98
|
if (process.argv.includes('-v') || process.argv.includes('--version')) {
|
94
99
|
// just print version
|
@@ -103,6 +108,8 @@ if (!file) {
|
|
103
108
|
|
104
109
|
const source = fs.readFileSync(file, 'utf8');
|
105
110
|
|
111
|
+
const compile = (await import('../compiler/wrap.js')).default;
|
112
|
+
|
106
113
|
let cache = '';
|
107
114
|
const print = str => {
|
108
115
|
/* cache += str;
|