porffor 0.2.0-50b82f8 → 0.2.0-5ac7ea0
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 +82 -46
- 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/crypto.ts +132 -0
- package/compiler/builtins/porffor.d.ts +26 -0
- package/compiler/builtins.js +590 -255
- package/compiler/codeGen.js +520 -184
- package/compiler/decompile.js +3 -3
- package/compiler/generated_builtins.js +25 -0
- package/compiler/index.js +22 -22
- package/compiler/log.js +4 -1
- package/compiler/opt.js +52 -27
- 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 +8 -7
- package/compiler/wasmSpec.js +20 -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/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,25 @@
|
|
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
|
+
this.__crypto_randomUUID = {
|
16
|
+
wasm: (scope, { allocPage, builtin }) => [...number(allocPage(scope, 'bytestring: bytes', 'i8') * pageSize, 127),[34,0],[16, builtin('__Porffor_i32_ptrUnsafe')],[34,1],[34,2],[65,16],[106],[33,3],[3,64],[32,2],[32,3],[72],[4,64],[32,2],[32,2],[65,1],[106],[33,2],[16, builtin('__Porffor_i32_randomByte')],[58,0,4],[12,1],[11],[11],[32,1],[32,1],[45,0,10],[65,15],[113],[65,192,0],[114],[58,0,10],[32,1],[32,1],[45,0,12],[65,63],[113],[65,128,1],[114],[58,0,12],...number(allocPage(scope, 'bytestring: output', 'i8') * pageSize, 127),[34,4],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,5],[32,1],[33,6],[32,5],[65,8],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,4],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[34,5],[65,12],[106],[33,7],[3,64],[32,5],[32,7],[72],[4,64],[32,6],[32,6],[65,1],[106],[33,6],[45,0,4],[33,8],[65,0],[33,9],[32,8],[65,15],[113],[33,10],[65,0],[33,11],[32,10],[65,10],[72],[4,64],[65,48],[32,10],[106],[34,10],[65,0],[33,11],[26],[5],[65,225,0],[32,10],[65,10],[107],[106],[34,10],[65,0],[33,11],[26],[11],[32,8],[65,4],[117],[33,12],[65,0],[33,13],[32,12],[65,10],[72],[4,64],[65,48],[32,12],[106],[34,12],[65,0],[33,13],[26],[5],[65,225,0],[32,12],[65,10],[107],[106],[34,12],[65,0],[33,13],[26],[11],[32,5],[32,5],[65,1],[106],[33,5],[32,12],[58,0,4],[32,5],[32,5],[65,1],[106],[33,5],[32,10],[58,0,4],[12,1],[11],[11],[32,5],[65,1],[106],[33,5],[32,4],[65,18],[15]],
|
17
|
+
params: [],
|
18
|
+
typedParams: true,
|
19
|
+
returns: [127,127],
|
20
|
+
typedReturns: true,
|
21
|
+
locals: [127,127,127,127,127,127,127,127,127,127,127,127,127,127],
|
22
|
+
localNames: ["bytes","bytesPtr","a","aEndPtr","output","i","j","endPtr","byte","byte#type","lower","lower#type","upper","upper#type"],
|
23
|
+
data: [{"offset":0,"bytes":[16,0,0,0,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46]},{"offset":65536,"bytes":[36,0,0,0,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45]}],
|
24
|
+
};
|
25
|
+
};
|
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);
|
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) => {
|
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) => {
|
|
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) => {
|
|
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,7 +98,10 @@ export default (funcs, globals, pages) => {
|
|
97
98
|
}
|
98
99
|
}
|
99
100
|
|
100
|
-
if (
|
101
|
+
if (Prefs.optInlineOnly) return;
|
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; }, {});
|
101
105
|
|
102
106
|
// wasm transform pass
|
103
107
|
for (const f of funcs) {
|
@@ -127,6 +131,13 @@ export default (funcs, globals, pages) => {
|
|
127
131
|
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
128
132
|
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
129
133
|
|
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
|
+
}
|
140
|
+
|
130
141
|
if (inst[0] === Opcodes.block) {
|
131
142
|
// remove unneeded blocks (no brs inside)
|
132
143
|
// block
|
@@ -143,7 +154,7 @@ export default (funcs, globals, pages) => {
|
|
143
154
|
depth--;
|
144
155
|
if (depth <= 0) break;
|
145
156
|
}
|
146
|
-
if (op === Opcodes.br || op === Opcodes.br_if) {
|
157
|
+
if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
|
147
158
|
hasBranch = true;
|
148
159
|
break;
|
149
160
|
}
|
@@ -156,7 +167,7 @@ export default (funcs, globals, pages) => {
|
|
156
167
|
|
157
168
|
wasm.splice(j - 1, 1); // remove end of this block
|
158
169
|
|
159
|
-
if (optLog) log('opt', `removed unneeded block in for loop`);
|
170
|
+
if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
|
160
171
|
}
|
161
172
|
}
|
162
173
|
|
@@ -188,6 +199,7 @@ export default (funcs, globals, pages) => {
|
|
188
199
|
let missing = false;
|
189
200
|
if (type === 'Array') missing = !pages.hasArray;
|
190
201
|
if (type === 'String') missing = !pages.hasString;
|
202
|
+
if (type === 'ByteString') missing = !pages.hasByteString;
|
191
203
|
|
192
204
|
if (missing) {
|
193
205
|
let j = i, depth = 0;
|
@@ -204,7 +216,7 @@ export default (funcs, globals, pages) => {
|
|
204
216
|
i -= 4;
|
205
217
|
inst = wasm[i];
|
206
218
|
|
207
|
-
if (optLog) log('opt', `removed unneeded typeswitch check`);
|
219
|
+
if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
|
208
220
|
}
|
209
221
|
}
|
210
222
|
|
@@ -227,7 +239,7 @@ export default (funcs, globals, pages) => {
|
|
227
239
|
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
228
240
|
wasm.splice(i - 1, 1); // remove this inst
|
229
241
|
|
230
|
-
if (optLog) log('opt', 'removed unneeded entire typeswitch');
|
242
|
+
if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
|
231
243
|
|
232
244
|
if (i > 0) i--;
|
233
245
|
continue;
|
@@ -235,7 +247,7 @@ export default (funcs, globals, pages) => {
|
|
235
247
|
}
|
236
248
|
|
237
249
|
// remove setting last type if it is never gotten
|
238
|
-
if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType
|
250
|
+
if (!f.gotLastType && inst[0] === Opcodes.local_set && inst[1] === lastType?.idx) {
|
239
251
|
// replace this inst with drop
|
240
252
|
wasm.splice(i, 1, [ Opcodes.drop ]); // remove this and last inst
|
241
253
|
if (i > 0) i--;
|
@@ -256,7 +268,7 @@ export default (funcs, globals, pages) => {
|
|
256
268
|
|
257
269
|
getCount[inst[1]]--;
|
258
270
|
i--;
|
259
|
-
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
271
|
+
// if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
|
260
272
|
continue;
|
261
273
|
}
|
262
274
|
|
@@ -324,7 +336,7 @@ export default (funcs, globals, pages) => {
|
|
324
336
|
|
325
337
|
wasm.splice(i - 1, 2); // remove this inst and last
|
326
338
|
i -= 2;
|
327
|
-
// if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
339
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
328
340
|
continue;
|
329
341
|
}
|
330
342
|
|
@@ -337,7 +349,7 @@ export default (funcs, globals, pages) => {
|
|
337
349
|
|
338
350
|
wasm.splice(i - 1, 2); // remove this inst and last
|
339
351
|
i -= 2;
|
340
|
-
// if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
352
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
341
353
|
continue;
|
342
354
|
}
|
343
355
|
|
@@ -352,7 +364,7 @@ export default (funcs, globals, pages) => {
|
|
352
364
|
|
353
365
|
wasm.splice(i, 1); // remove this inst
|
354
366
|
i--;
|
355
|
-
if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
367
|
+
if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
356
368
|
continue;
|
357
369
|
}
|
358
370
|
|
@@ -367,7 +379,7 @@ export default (funcs, globals, pages) => {
|
|
367
379
|
|
368
380
|
wasm.splice(i, 1); // remove this inst
|
369
381
|
i--;
|
370
|
-
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
382
|
+
if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
|
371
383
|
continue;
|
372
384
|
}
|
373
385
|
|
@@ -382,7 +394,7 @@ export default (funcs, globals, pages) => {
|
|
382
394
|
|
383
395
|
wasm.splice(i, 1); // remove this inst (return)
|
384
396
|
i--;
|
385
|
-
if (optLog) log('opt', `tail called return, call`);
|
397
|
+
if (Prefs.optLog) log('opt', `tail called return, call`);
|
386
398
|
continue;
|
387
399
|
}
|
388
400
|
|
@@ -395,7 +407,7 @@ export default (funcs, globals, pages) => {
|
|
395
407
|
|
396
408
|
wasm.splice(i, 1); // remove this inst (return)
|
397
409
|
i--;
|
398
|
-
// if (optLog) log('opt', `removed redundant return at end`);
|
410
|
+
// if (Prefs.optLog) log('opt', `removed redundant return at end`);
|
399
411
|
continue;
|
400
412
|
}
|
401
413
|
|
@@ -425,7 +437,7 @@ export default (funcs, globals, pages) => {
|
|
425
437
|
// <nothing>
|
426
438
|
|
427
439
|
wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
|
428
|
-
if (optLog) log('opt', `removed redundant inline param local handling`);
|
440
|
+
if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
|
429
441
|
i -= 3;
|
430
442
|
continue;
|
431
443
|
}
|
@@ -433,12 +445,12 @@ export default (funcs, globals, pages) => {
|
|
433
445
|
|
434
446
|
if (optLevel < 2) continue;
|
435
447
|
|
436
|
-
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(', ')}`);
|
437
449
|
|
438
450
|
// remove unneeded var: remove pass
|
439
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
|
440
452
|
let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
|
441
|
-
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})`);
|
442
454
|
|
443
455
|
// note: disabled for now due to instability
|
444
456
|
if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
|
@@ -456,7 +468,7 @@ export default (funcs, globals, pages) => {
|
|
456
468
|
wasm.splice(i - 1, 2); // remove insts
|
457
469
|
i -= 2;
|
458
470
|
delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
|
459
|
-
if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
471
|
+
if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
460
472
|
}
|
461
473
|
|
462
474
|
if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
|
@@ -484,7 +496,7 @@ export default (funcs, globals, pages) => {
|
|
484
496
|
unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
|
485
497
|
unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
|
486
498
|
|
487
|
-
if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
499
|
+
if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
488
500
|
}
|
489
501
|
}
|
490
502
|
|
@@ -520,7 +532,7 @@ export default (funcs, globals, pages) => {
|
|
520
532
|
let b = lastInst[1];
|
521
533
|
|
522
534
|
const val = performWasmOp(inst[0], a, b);
|
523
|
-
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})`);
|
524
536
|
|
525
537
|
wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
|
526
538
|
i -= 2;
|
@@ -532,12 +544,25 @@ export default (funcs, globals, pages) => {
|
|
532
544
|
for (const x in useCount) {
|
533
545
|
if (useCount[x] === 0) {
|
534
546
|
const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
|
535
|
-
if (optLog) log('opt', `removed internal local ${x} (${name})`);
|
547
|
+
if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
|
536
548
|
delete f.locals[name];
|
537
549
|
}
|
538
550
|
}
|
539
551
|
|
540
|
-
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(', ')}`);
|
553
|
+
}
|
554
|
+
}
|
555
|
+
|
556
|
+
for (const x in tagUse) {
|
557
|
+
if (tagUse[x] === 0) {
|
558
|
+
const el = tags.find(y => y.idx === x);
|
559
|
+
tags.splice(tags.indexOf(el), 1);
|
560
|
+
}
|
561
|
+
}
|
562
|
+
|
563
|
+
for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
564
|
+
if (exceptionUse[x] === 0) {
|
565
|
+
exceptions.splice(+x, 1);
|
541
566
|
}
|
542
567
|
}
|
543
568
|
|
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;
|