porffor 0.2.0-5e33105 → 0.2.0-6aff0fa
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 +75 -47
- package/asur/README.md +2 -0
- package/asur/index.js +978 -0
- package/compiler/2c.js +316 -71
- package/compiler/builtins/base64.ts +153 -0
- package/compiler/builtins/porffor.d.ts +23 -0
- package/compiler/builtins.js +230 -205
- package/compiler/codeGen.js +420 -189
- package/compiler/decompile.js +3 -3
- package/compiler/generated_builtins.js +15 -0
- package/compiler/index.js +22 -22
- package/compiler/log.js +4 -1
- package/compiler/opt.js +40 -26
- package/compiler/parse.js +4 -2
- package/compiler/precompile.js +129 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +177 -21
- package/compiler/sections.js +7 -6
- package/compiler/wasmSpec.js +16 -6
- package/compiler/wrap.js +112 -11
- package/package.json +1 -1
- package/porf +2 -0
- package/rhemyn/compile.js +2 -1
- package/runner/index.js +26 -3
- package/runner/profiler.js +83 -0
- package/compiler/builtins/base64.js +0 -92
- package/node_trace.1.log +0 -1
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
package/compiler/decompile.js
CHANGED
@@ -15,7 +15,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
15
15
|
if (name) out += `${makeSignature(params, returns)} ;; $${name} (${ind})\n`;
|
16
16
|
|
17
17
|
const justLocals = Object.values(locals).sort((a, b) => a.idx - b.idx).slice(params.length);
|
18
|
-
if (justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
|
18
|
+
if (name && justLocals.length > 0) out += ` local ${justLocals.map(x => invValtype[x.type]).join(' ')}\n`;
|
19
19
|
|
20
20
|
let i = -1, lastInst;
|
21
21
|
let byte = 0;
|
@@ -32,7 +32,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
32
32
|
inst = [ [ inst[0], inst[1] ], ...inst.slice(2) ];
|
33
33
|
}
|
34
34
|
|
35
|
-
if (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all) depth--;
|
35
|
+
if (depth > 0 && (inst[0] === Opcodes.end || inst[0] === Opcodes.else || inst[0] === Opcodes.catch_all)) depth--;
|
36
36
|
|
37
37
|
out += ' '.repeat(Math.max(0, depth * 2));
|
38
38
|
|
@@ -119,7 +119,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
119
119
|
export const highlightAsm = asm => asm
|
120
120
|
.replace(/(local|global|memory)\.[^\s]*/g, _ => `\x1B[31m${_}\x1B[0m`)
|
121
121
|
.replace(/(i(8|16|32|64)x[0-9]+|v128)(\.[^\s]*)?/g, _ => `\x1B[34m${_}\x1B[0m`)
|
122
|
-
.replace(/
|
122
|
+
.replace(/(i32|i64|f32|f64|drop)(\.[^\s]*)?/g, _ => `\x1B[36m${_}\x1B[0m`)
|
123
123
|
.replace(/(return_call|call|br_if|br|return|rethrow|throw)/g, _ => `\x1B[35m${_}\x1B[0m`)
|
124
124
|
.replace(/(block|loop|if|end|else|try|catch_all|catch|delegate)/g, _ => `\x1B[95m${_}\x1B[0m`)
|
125
125
|
.replace(/unreachable/g, _ => `\x1B[91m${_}\x1B[0m`)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// autogenerated by compiler/precompile.js
|
2
|
+
import { number } from './embedding.js';
|
3
|
+
|
4
|
+
export const BuiltinFuncs = function() {
|
5
|
+
this.btoa = {
|
6
|
+
wasm: (scope, { allocPage, builtin }) => [...number(allocPage(scope, 'bytestring: keyStr', 'i8') * pageSize, 127),[34,2],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,3],[32,0],[40,1,0],[33,4],...number(allocPage(scope, 'bytestring: output', 'i8') * pageSize, 127),[33,5],[65,0],[65,4],[32,4],[65,3],[109],[32,4],[65,3],[111],[69],[69],[106],[108],[34,6],[54,1,128,128,4],[32,0],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,7],[32,5],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,8],[32,7],[32,4],[106],[33,9],[65,0],[33,10],[3,64],[32,7],[32,9],[72],[4,64],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[33,11],[32,7],[32,9],[72],[4,127],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[65,0],[33,13],[5],[65,127],[65,0],[33,13],[11],[33,12],[32,7],[32,9],[72],[4,127],[32,7],[32,7],[65,1],[106],[33,7],[45,0,4],[65,0],[33,13],[5],[65,127],[65,0],[33,13],[11],[33,14],[32,11],[65,2],[117],[33,15],[32,11],[65,3],[113],[65,4],[116],[32,12],[65,127],[70],[4,127],[65,0],[65,0],[33,13],[5],[32,12],[65,4],[117],[65,0],[33,13],[11],[114],[33,16],[32,12],[65,15],[113],[65,2],[116],[32,14],[65,127],[70],[4,127],[65,0],[65,0],[33,13],[5],[32,14],[65,6],[117],[65,0],[33,13],[11],[114],[33,17],[32,14],[65,63],[113],[33,18],[32,12],[65,127],[70],[4,64],[65,192,0],[33,17],[65,192,0],[33,18],[5],[32,14],[65,127],[70],[4,64],[65,192,0],[33,18],[11],[11],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,15],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,16],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,17],[106],[45,0,4],[58,0,4],[32,8],[32,8],[65,1],[106],[33,8],[32,3],[32,18],[106],[45,0,4],[58,0,4],[12,1],[11],[11],[32,5],[65,18],[15]],
|
7
|
+
params: [127,127],
|
8
|
+
typedParams: true,
|
9
|
+
returns: [127,127],
|
10
|
+
typedReturns: true,
|
11
|
+
locals: [127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127],
|
12
|
+
localNames: ["input","input#type","keyStr","keyStrPtr","len","output","__length_setter_tmp","i","j","endPtr","endPtr#type","chr1","chr2","#last_type","chr3","enc1","enc2","enc3","enc4"],
|
13
|
+
data: [{"offset":0,"bytes":[65,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,43,47,61]}],
|
14
|
+
};
|
15
|
+
};
|
package/compiler/index.js
CHANGED
@@ -4,8 +4,8 @@ import codeGen from './codeGen.js';
|
|
4
4
|
import opt from './opt.js';
|
5
5
|
import produceSections from './sections.js';
|
6
6
|
import decompile from './decompile.js';
|
7
|
-
import { BuiltinPreludes } from './builtins.js';
|
8
7
|
import toc from './2c.js';
|
8
|
+
import Prefs from './prefs.js';
|
9
9
|
|
10
10
|
globalThis.decompile = decompile;
|
11
11
|
|
@@ -26,21 +26,10 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
26
26
|
console.log();
|
27
27
|
};
|
28
28
|
|
29
|
-
const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
|
30
|
-
|
31
29
|
const writeFileSync = (typeof process?.version !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
|
32
30
|
const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
33
31
|
|
34
32
|
export default (code, flags) => {
|
35
|
-
globalThis.optLog = process.argv.includes('-opt-log');
|
36
|
-
globalThis.codeLog = process.argv.includes('-code-log');
|
37
|
-
globalThis.allocLog = process.argv.includes('-alloc-log');
|
38
|
-
globalThis.regexLog = process.argv.includes('-regex-log');
|
39
|
-
|
40
|
-
for (const x in BuiltinPreludes) {
|
41
|
-
if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
|
42
|
-
}
|
43
|
-
|
44
33
|
const t0 = performance.now();
|
45
34
|
const program = parse(code, flags);
|
46
35
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
@@ -49,29 +38,35 @@ export default (code, flags) => {
|
|
49
38
|
const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
|
50
39
|
if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
51
40
|
|
52
|
-
if (
|
41
|
+
if (Prefs.funcs) logFuncs(funcs, globals, exceptions);
|
53
42
|
|
54
43
|
const t2 = performance.now();
|
55
|
-
opt(funcs, globals, pages, tags);
|
44
|
+
opt(funcs, globals, pages, tags, exceptions);
|
56
45
|
if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
|
57
46
|
|
58
|
-
if (
|
47
|
+
if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
|
59
48
|
|
60
49
|
const t3 = performance.now();
|
61
50
|
const sections = produceSections(funcs, globals, tags, pages, data, flags);
|
62
51
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
63
52
|
|
64
|
-
if (allocLog) {
|
53
|
+
if (Prefs.allocLog) {
|
65
54
|
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
66
55
|
const bytes = wasmPages * 65536;
|
67
56
|
log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
|
68
57
|
console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
|
69
58
|
}
|
70
59
|
|
71
|
-
const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
|
60
|
+
const out = { wasm: sections, funcs, globals, tags, exceptions, pages, data };
|
61
|
+
|
62
|
+
const target = Prefs.target ?? 'wasm';
|
63
|
+
const outFile = Prefs.o;
|
72
64
|
|
73
|
-
|
74
|
-
|
65
|
+
if (target === 'wasm' && outFile) {
|
66
|
+
writeFileSync(outFile, Buffer.from(sections));
|
67
|
+
|
68
|
+
if (process.version) process.exit();
|
69
|
+
}
|
75
70
|
|
76
71
|
if (target === 'c') {
|
77
72
|
const c = toc(out);
|
@@ -87,11 +82,16 @@ export default (code, flags) => {
|
|
87
82
|
}
|
88
83
|
|
89
84
|
if (target === 'native') {
|
90
|
-
|
91
|
-
const cO =
|
85
|
+
let compiler = Prefs.compiler ?? 'clang';
|
86
|
+
const cO = Prefs._cO ?? 'Ofast';
|
87
|
+
|
88
|
+
if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
|
89
|
+
else compiler = [ compiler ];
|
92
90
|
|
93
91
|
const tmpfile = 'tmp.c';
|
94
|
-
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
|
92
|
+
// const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-fno-unwind-tables', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl', '-fno-ident', '-fno-exceptions', '-ffast-math' ];
|
93
|
+
// const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-target', 'x86_64-linux' ];
|
94
|
+
const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
|
95
95
|
|
96
96
|
const c = toc(out);
|
97
97
|
writeFileSync(tmpfile, c);
|
package/compiler/log.js
CHANGED
@@ -8,8 +8,11 @@ const areaColors = {
|
|
8
8
|
sections: [ 20, 250, 80 ],
|
9
9
|
alloc: [ 250, 250, 20 ],
|
10
10
|
parser: [ 240, 240, 240 ],
|
11
|
-
'2c': [ 20, 250, 250 ]
|
11
|
+
'2c': [ 20, 250, 250 ],
|
12
|
+
wrap: [ 250, 100, 20 ]
|
12
13
|
};
|
13
14
|
|
15
|
+
// for (const x in areaColors) console.log(rgb(areaColors[x][0], areaColors[x][1], areaColors[x][2], x));
|
16
|
+
|
14
17
|
export const log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
|
15
18
|
log.warning = (area, ...args) => log(area, '\u001b[93m' + args[0], ...args.slice(1), '\u001b[0m');
|
package/compiler/opt.js
CHANGED
@@ -2,6 +2,7 @@ import { Opcodes, Valtype } from "./wasmSpec.js";
|
|
2
2
|
import { number } from "./embedding.js";
|
3
3
|
import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
|
4
4
|
import { log } from "./log.js";
|
5
|
+
import Prefs from './prefs.js';
|
5
6
|
|
6
7
|
const performWasmOp = (op, a, b) => {
|
7
8
|
switch (op) {
|
@@ -11,21 +12,21 @@ const performWasmOp = (op, a, b) => {
|
|
11
12
|
}
|
12
13
|
};
|
13
14
|
|
14
|
-
export default (funcs, globals, pages, tags) => {
|
15
|
+
export default (funcs, globals, pages, tags, exceptions) => {
|
15
16
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
16
17
|
if (optLevel === 0) return;
|
17
18
|
|
18
|
-
const tailCall =
|
19
|
+
const tailCall = Prefs.tailCall;
|
19
20
|
if (tailCall) log.warning('opt', 'tail call proposal is not widely implemented! (you used -tail-call)');
|
20
21
|
|
21
|
-
if (optLevel >= 2 && !
|
22
|
+
if (optLevel >= 2 && !Prefs.optNoInline) {
|
22
23
|
// inline pass (very WIP)
|
23
24
|
// get candidates for inlining
|
24
25
|
// todo: pick smart in future (if func is used <N times? or?)
|
25
26
|
const callsSelf = f => f.wasm.some(x => x[0] === Opcodes.call && x[1] === f.index);
|
26
27
|
const suitableReturns = wasm => wasm.reduce((acc, x) => acc + (x[0] === Opcodes.return), 0) <= 1;
|
27
28
|
const candidates = funcs.filter(x => x.name !== 'main' && Object.keys(x.locals).length === x.params.length && (x.returns.length === 0 || suitableReturns(x.wasm)) && !callsSelf(x) && !x.throws).reverse();
|
28
|
-
if (optLog) {
|
29
|
+
if (Prefs.optLog) {
|
29
30
|
log('opt', `found inline candidates: ${candidates.map(x => x.name).join(', ')} (${candidates.length}/${funcs.length - 1})`);
|
30
31
|
|
31
32
|
let reasons = {};
|
@@ -53,7 +54,7 @@ export default (funcs, globals, pages, tags) => {
|
|
53
54
|
for (let i = 0; i < tWasm.length; i++) {
|
54
55
|
const inst = tWasm[i];
|
55
56
|
if (inst[0] === Opcodes.call && inst[1] === c.index) {
|
56
|
-
if (optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
|
57
|
+
if (Prefs.optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
|
57
58
|
tWasm.splice(i, 1); // remove this call
|
58
59
|
|
59
60
|
// add params as locals and set in reverse order
|
@@ -80,7 +81,7 @@ export default (funcs, globals, pages, tags) => {
|
|
80
81
|
// adjust local operands to go to correct param index
|
81
82
|
for (const inst of iWasm) {
|
82
83
|
if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) && inst[1] < c.params.length) {
|
83
|
-
if (optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
|
84
|
+
if (Prefs.optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
|
84
85
|
inst[1] = paramIdx[inst[1]];
|
85
86
|
}
|
86
87
|
}
|
@@ -97,9 +98,10 @@ export default (funcs, globals, pages, tags) => {
|
|
97
98
|
}
|
98
99
|
}
|
99
100
|
|
100
|
-
if (
|
101
|
+
if (Prefs.optInlineOnly) return;
|
101
102
|
|
102
103
|
const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
104
|
+
const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
|
103
105
|
|
104
106
|
// wasm transform pass
|
105
107
|
for (const f of funcs) {
|
@@ -129,7 +131,12 @@ export default (funcs, globals, pages, tags) => {
|
|
129
131
|
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
130
132
|
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
131
133
|
|
132
|
-
if (inst[0] === Opcodes.throw)
|
134
|
+
if (inst[0] === Opcodes.throw) {
|
135
|
+
tagUse[inst[1]]++;
|
136
|
+
|
137
|
+
const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
|
138
|
+
exceptionUse[exceptId]++;
|
139
|
+
}
|
133
140
|
|
134
141
|
if (inst[0] === Opcodes.block) {
|
135
142
|
// remove unneeded blocks (no brs inside)
|
@@ -160,7 +167,7 @@ export default (funcs, globals, pages, tags) => {
|
|
160
167
|
|
161
168
|
wasm.splice(j - 1, 1); // remove end of this block
|
162
169
|
|
163
|
-
if (optLog) log('opt', `removed unneeded block in for loop`);
|
170
|
+
if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
|
164
171
|
}
|
165
172
|
}
|
166
173
|
|
@@ -192,6 +199,7 @@ export default (funcs, globals, pages, tags) => {
|
|
192
199
|
let missing = false;
|
193
200
|
if (type === 'Array') missing = !pages.hasArray;
|
194
201
|
if (type === 'String') missing = !pages.hasString;
|
202
|
+
if (type === 'ByteString') missing = !pages.hasByteString;
|
195
203
|
|
196
204
|
if (missing) {
|
197
205
|
let j = i, depth = 0;
|
@@ -208,7 +216,7 @@ export default (funcs, globals, pages, tags) => {
|
|
208
216
|
i -= 4;
|
209
217
|
inst = wasm[i];
|
210
218
|
|
211
|
-
if (optLog) log('opt', `removed unneeded typeswitch check`);
|
219
|
+
if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
|
212
220
|
}
|
213
221
|
}
|
214
222
|
|
@@ -231,7 +239,7 @@ export default (funcs, globals, pages, tags) => {
|
|
231
239
|
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
232
240
|
wasm.splice(i - 1, 1); // remove this inst
|
233
241
|
|
234
|
-
if (optLog) log('opt', 'removed unneeded entire typeswitch');
|
242
|
+
if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
|
235
243
|
|
236
244
|
if (i > 0) i--;
|
237
245
|
continue;
|
@@ -260,7 +268,7 @@ export default (funcs, globals, pages, tags) => {
|
|
260
268
|
|
261
269
|
getCount[inst[1]]--;
|
262
270
|
i--;
|
263
|
-
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
271
|
+
// if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
|
264
272
|
continue;
|
265
273
|
}
|
266
274
|
|
@@ -328,7 +336,7 @@ export default (funcs, globals, pages, tags) => {
|
|
328
336
|
|
329
337
|
wasm.splice(i - 1, 2); // remove this inst and last
|
330
338
|
i -= 2;
|
331
|
-
// if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
339
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
332
340
|
continue;
|
333
341
|
}
|
334
342
|
|
@@ -341,7 +349,7 @@ export default (funcs, globals, pages, tags) => {
|
|
341
349
|
|
342
350
|
wasm.splice(i - 1, 2); // remove this inst and last
|
343
351
|
i -= 2;
|
344
|
-
// if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
352
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
345
353
|
continue;
|
346
354
|
}
|
347
355
|
|
@@ -356,7 +364,7 @@ export default (funcs, globals, pages, tags) => {
|
|
356
364
|
|
357
365
|
wasm.splice(i, 1); // remove this inst
|
358
366
|
i--;
|
359
|
-
if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
367
|
+
if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
360
368
|
continue;
|
361
369
|
}
|
362
370
|
|
@@ -371,7 +379,7 @@ export default (funcs, globals, pages, tags) => {
|
|
371
379
|
|
372
380
|
wasm.splice(i, 1); // remove this inst
|
373
381
|
i--;
|
374
|
-
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
382
|
+
if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
|
375
383
|
continue;
|
376
384
|
}
|
377
385
|
|
@@ -386,7 +394,7 @@ export default (funcs, globals, pages, tags) => {
|
|
386
394
|
|
387
395
|
wasm.splice(i, 1); // remove this inst (return)
|
388
396
|
i--;
|
389
|
-
if (optLog) log('opt', `tail called return, call`);
|
397
|
+
if (Prefs.optLog) log('opt', `tail called return, call`);
|
390
398
|
continue;
|
391
399
|
}
|
392
400
|
|
@@ -399,7 +407,7 @@ export default (funcs, globals, pages, tags) => {
|
|
399
407
|
|
400
408
|
wasm.splice(i, 1); // remove this inst (return)
|
401
409
|
i--;
|
402
|
-
// if (optLog) log('opt', `removed redundant return at end`);
|
410
|
+
// if (Prefs.optLog) log('opt', `removed redundant return at end`);
|
403
411
|
continue;
|
404
412
|
}
|
405
413
|
|
@@ -429,7 +437,7 @@ export default (funcs, globals, pages, tags) => {
|
|
429
437
|
// <nothing>
|
430
438
|
|
431
439
|
wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
|
432
|
-
if (optLog) log('opt', `removed redundant inline param local handling`);
|
440
|
+
if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
|
433
441
|
i -= 3;
|
434
442
|
continue;
|
435
443
|
}
|
@@ -437,12 +445,12 @@ export default (funcs, globals, pages, tags) => {
|
|
437
445
|
|
438
446
|
if (optLevel < 2) continue;
|
439
447
|
|
440
|
-
if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
448
|
+
if (Prefs.optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
441
449
|
|
442
450
|
// remove unneeded var: remove pass
|
443
451
|
// locals only got once. we don't need to worry about sets/else as these are only candidates and we will check for matching set + get insts in wasm
|
444
452
|
let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
|
445
|
-
if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
|
453
|
+
if (Prefs.optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
|
446
454
|
|
447
455
|
// note: disabled for now due to instability
|
448
456
|
if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
|
@@ -460,7 +468,7 @@ export default (funcs, globals, pages, tags) => {
|
|
460
468
|
wasm.splice(i - 1, 2); // remove insts
|
461
469
|
i -= 2;
|
462
470
|
delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
|
463
|
-
if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
471
|
+
if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
464
472
|
}
|
465
473
|
|
466
474
|
if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
|
@@ -488,7 +496,7 @@ export default (funcs, globals, pages, tags) => {
|
|
488
496
|
unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
|
489
497
|
unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
|
490
498
|
|
491
|
-
if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
499
|
+
if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
492
500
|
}
|
493
501
|
}
|
494
502
|
|
@@ -524,7 +532,7 @@ export default (funcs, globals, pages, tags) => {
|
|
524
532
|
let b = lastInst[1];
|
525
533
|
|
526
534
|
const val = performWasmOp(inst[0], a, b);
|
527
|
-
if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
535
|
+
if (Prefs.optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
528
536
|
|
529
537
|
wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
|
530
538
|
i -= 2;
|
@@ -536,12 +544,12 @@ export default (funcs, globals, pages, tags) => {
|
|
536
544
|
for (const x in useCount) {
|
537
545
|
if (useCount[x] === 0) {
|
538
546
|
const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
|
539
|
-
if (optLog) log('opt', `removed internal local ${x} (${name})`);
|
547
|
+
if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
|
540
548
|
delete f.locals[name];
|
541
549
|
}
|
542
550
|
}
|
543
551
|
|
544
|
-
if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
|
552
|
+
if (Prefs.optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
|
545
553
|
}
|
546
554
|
}
|
547
555
|
|
@@ -552,5 +560,11 @@ export default (funcs, globals, pages, tags) => {
|
|
552
560
|
}
|
553
561
|
}
|
554
562
|
|
563
|
+
for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
564
|
+
if (exceptionUse[x] === 0) {
|
565
|
+
exceptions.splice(+x, 1);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
|
555
569
|
// return funcs;
|
556
570
|
};
|
package/compiler/parse.js
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
import { log } from "./log.js";
|
2
|
+
import Prefs from './prefs.js';
|
3
|
+
|
2
4
|
// import { parse } from 'acorn';
|
3
5
|
|
4
6
|
// deno compat
|
@@ -8,8 +10,8 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
|
8
10
|
}
|
9
11
|
|
10
12
|
// should we try to support types (while parsing)
|
11
|
-
const types =
|
12
|
-
globalThis.typedInput = types &&
|
13
|
+
const types = Prefs.parseTypes;
|
14
|
+
globalThis.typedInput = types && Prefs.optTypes;
|
13
15
|
|
14
16
|
// todo: review which to use by default
|
15
17
|
// supported parsers:
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import { Opcodes } from './wasmSpec.js';
|
2
|
+
|
3
|
+
import fs from 'node:fs';
|
4
|
+
import { join } from 'node:path';
|
5
|
+
|
6
|
+
import { fileURLToPath } from 'node:url';
|
7
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
8
|
+
|
9
|
+
const TYPES = {
|
10
|
+
number: 0x00,
|
11
|
+
boolean: 0x01,
|
12
|
+
string: 0x02,
|
13
|
+
undefined: 0x03,
|
14
|
+
object: 0x04,
|
15
|
+
function: 0x05,
|
16
|
+
symbol: 0x06,
|
17
|
+
bigint: 0x07,
|
18
|
+
|
19
|
+
// these are not "typeof" types but tracked internally
|
20
|
+
_array: 0x10,
|
21
|
+
_regexp: 0x11,
|
22
|
+
_bytestring: 0x12
|
23
|
+
};
|
24
|
+
|
25
|
+
// import porfParse from './parse.js';
|
26
|
+
// import porfCodegen from './codeGen.js';
|
27
|
+
|
28
|
+
const argv = process.argv.slice();
|
29
|
+
|
30
|
+
const compile = async (file, [ _funcs, _globals ]) => {
|
31
|
+
const source = fs.readFileSync(file, 'utf8');
|
32
|
+
const first = source.slice(0, source.indexOf('\n'));
|
33
|
+
|
34
|
+
let args = ['-bytestring'];
|
35
|
+
if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
|
36
|
+
if (first.startsWith('// @porf')) {
|
37
|
+
args = args.concat(first.slice('// @porf '.length).split(' '));
|
38
|
+
}
|
39
|
+
process.argv = argv.concat(args);
|
40
|
+
|
41
|
+
// const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
|
42
|
+
// const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
|
43
|
+
|
44
|
+
// let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
|
45
|
+
|
46
|
+
const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
|
47
|
+
|
48
|
+
let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
|
49
|
+
|
50
|
+
const allocated = new Set();
|
51
|
+
|
52
|
+
const exports = funcs.filter(x => x.export);
|
53
|
+
for (const x of exports) {
|
54
|
+
if (x.data) x.data = x.data.map(x => data[x]);
|
55
|
+
if (x.exceptions) x.exceptions = x.exceptions.map(x => {
|
56
|
+
const obj = exceptions[x];
|
57
|
+
if (obj) obj.exceptId = x;
|
58
|
+
return obj;
|
59
|
+
}).filter(x => x);
|
60
|
+
|
61
|
+
const locals = Object.keys(x.locals).reduce((acc, y) => {
|
62
|
+
acc[x.locals[y].idx] = { ...x.locals[y], name: y };
|
63
|
+
return acc;
|
64
|
+
}, {});
|
65
|
+
|
66
|
+
for (let i = 0; i < x.wasm.length; i++) {
|
67
|
+
const y = x.wasm[i];
|
68
|
+
const n = x.wasm[i + 1];
|
69
|
+
if (y[0] === Opcodes.call) {
|
70
|
+
const f = funcs.find(x => x.index === y[1]);
|
71
|
+
if (!f) continue;
|
72
|
+
|
73
|
+
y[1] = f.name;
|
74
|
+
}
|
75
|
+
|
76
|
+
if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
|
77
|
+
const l = locals[n[1]];
|
78
|
+
if (!l) continue;
|
79
|
+
if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
|
80
|
+
if (!x.pages) continue;
|
81
|
+
|
82
|
+
const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
|
83
|
+
if (!pageName || allocated.has(pageName)) continue;
|
84
|
+
allocated.add(pageName);
|
85
|
+
|
86
|
+
y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
|
87
|
+
// y.push(x.pages.get(pageName));
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
_funcs.push(...exports);
|
93
|
+
_globals.push(...Object.values(globals));
|
94
|
+
};
|
95
|
+
|
96
|
+
const precompile = async () => {
|
97
|
+
const dir = join(__dirname, 'builtins');
|
98
|
+
|
99
|
+
let funcs = [], globals = [];
|
100
|
+
for (const file of fs.readdirSync(dir)) {
|
101
|
+
if (file.endsWith('.d.ts')) continue;
|
102
|
+
await compile(join(dir, file), [ funcs, globals ]);
|
103
|
+
}
|
104
|
+
|
105
|
+
// ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
|
106
|
+
// ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
|
107
|
+
|
108
|
+
return `// autogenerated by compiler/precompile.js
|
109
|
+
import { number } from './embedding.js';
|
110
|
+
|
111
|
+
export const BuiltinFuncs = function() {
|
112
|
+
${funcs.map(x => ` this.${x.name} = {
|
113
|
+
wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtypeBinary})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
|
114
|
+
params: ${JSON.stringify(x.params)},
|
115
|
+
typedParams: true,
|
116
|
+
returns: ${JSON.stringify(x.returns)},
|
117
|
+
typedReturns: true,
|
118
|
+
locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
|
119
|
+
localNames: ${JSON.stringify(Object.keys(x.locals))},
|
120
|
+
${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
|
121
|
+
${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
|
122
|
+
};`.replaceAll('\n\n', '\n')).join('\n')}
|
123
|
+
};`;
|
124
|
+
};
|
125
|
+
|
126
|
+
const code = await precompile();
|
127
|
+
// console.log(code);
|
128
|
+
|
129
|
+
fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
|
@@ -0,0 +1,26 @@
|
|
1
|
+
const onByDefault = [ 'bytestring' ];
|
2
|
+
|
3
|
+
const cache = {};
|
4
|
+
const obj = new Proxy({}, {
|
5
|
+
get(_, p) {
|
6
|
+
// intentionally misses with undefined values cached
|
7
|
+
if (cache[p]) return cache[p];
|
8
|
+
|
9
|
+
return cache[p] = (() => {
|
10
|
+
// fooBar -> foo-bar
|
11
|
+
const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
|
12
|
+
if (process.argv.includes('-' + name)) return true;
|
13
|
+
if (process.argv.includes('-no-' + name)) return false;
|
14
|
+
|
15
|
+
const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
|
16
|
+
if (valArg) return valArg.slice(name.length + 2);
|
17
|
+
|
18
|
+
if (onByDefault.includes(p)) return true;
|
19
|
+
return undefined;
|
20
|
+
})();
|
21
|
+
}
|
22
|
+
});
|
23
|
+
|
24
|
+
obj.uncache = () => cache = {};
|
25
|
+
|
26
|
+
export default obj;
|