porffor 0.2.0-08a272e → 0.2.0-15592d6
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 -44
- package/compiler/2c.js +316 -71
- package/compiler/builtins/base64.ts +88 -0
- package/compiler/builtins/porffor.d.ts +10 -0
- package/compiler/builtins.js +177 -60
- package/compiler/codeGen.js +404 -133
- package/compiler/decompile.js +3 -3
- package/compiler/generated_builtins.js +3 -0
- package/compiler/index.js +15 -9
- package/compiler/opt.js +26 -2
- package/compiler/precompile.js +79 -0
- package/compiler/prototype.js +171 -17
- package/compiler/sections.js +1 -1
- package/compiler/wasmSpec.js +6 -2
- package/compiler/wrap.js +103 -9
- package/demo.js +3 -0
- package/demo.ts +1 -0
- package/filesize.cmd +2 -0
- package/package.json +1 -1
- package/porf +2 -0
- package/runner/index.js +20 -3
- package/tmp.c +1248 -0
- package/compiler/builtins/base64.js +0 -92
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`)
|
package/compiler/index.js
CHANGED
@@ -4,7 +4,6 @@ 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';
|
9
8
|
|
10
9
|
globalThis.decompile = decompile;
|
@@ -37,10 +36,6 @@ export default (code, flags) => {
|
|
37
36
|
globalThis.allocLog = process.argv.includes('-alloc-log');
|
38
37
|
globalThis.regexLog = process.argv.includes('-regex-log');
|
39
38
|
|
40
|
-
for (const x in BuiltinPreludes) {
|
41
|
-
if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
|
42
|
-
}
|
43
|
-
|
44
39
|
const t0 = performance.now();
|
45
40
|
const program = parse(code, flags);
|
46
41
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
@@ -52,7 +47,7 @@ export default (code, flags) => {
|
|
52
47
|
if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
|
53
48
|
|
54
49
|
const t2 = performance.now();
|
55
|
-
opt(funcs, globals, pages);
|
50
|
+
opt(funcs, globals, pages, tags, exceptions);
|
56
51
|
if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
|
57
52
|
|
58
53
|
if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
|
@@ -68,11 +63,17 @@ export default (code, flags) => {
|
|
68
63
|
console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
|
69
64
|
}
|
70
65
|
|
71
|
-
const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
|
66
|
+
const out = { wasm: sections, funcs, globals, tags, exceptions, pages, data };
|
72
67
|
|
73
68
|
const target = getArg('target') ?? getArg('t') ?? 'wasm';
|
74
69
|
const outFile = getArg('o');
|
75
70
|
|
71
|
+
if (target === 'wasm' && outFile) {
|
72
|
+
writeFileSync(outFile, Buffer.from(sections));
|
73
|
+
|
74
|
+
if (process.version) process.exit();
|
75
|
+
}
|
76
|
+
|
76
77
|
if (target === 'c') {
|
77
78
|
const c = toc(out);
|
78
79
|
out.c = c;
|
@@ -87,11 +88,16 @@ export default (code, flags) => {
|
|
87
88
|
}
|
88
89
|
|
89
90
|
if (target === 'native') {
|
90
|
-
|
91
|
+
let compiler = getArg('compiler') ?? 'clang';
|
91
92
|
const cO = getArg('cO') ?? 'Ofast';
|
92
93
|
|
94
|
+
if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
|
95
|
+
else compiler = [ compiler ];
|
96
|
+
|
93
97
|
const tmpfile = 'tmp.c';
|
94
|
-
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
|
98
|
+
// 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' ];
|
99
|
+
// const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-target', 'x86_64-linux' ];
|
100
|
+
const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
|
95
101
|
|
96
102
|
const c = toc(out);
|
97
103
|
writeFileSync(tmpfile, c);
|
package/compiler/opt.js
CHANGED
@@ -11,7 +11,7 @@ const performWasmOp = (op, a, b) => {
|
|
11
11
|
}
|
12
12
|
};
|
13
13
|
|
14
|
-
export default (funcs, globals, pages) => {
|
14
|
+
export default (funcs, globals, pages, tags, exceptions) => {
|
15
15
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
16
16
|
if (optLevel === 0) return;
|
17
17
|
|
@@ -99,6 +99,9 @@ export default (funcs, globals, pages) => {
|
|
99
99
|
|
100
100
|
if (process.argv.includes('-opt-inline-only')) return;
|
101
101
|
|
102
|
+
const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
103
|
+
const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
|
104
|
+
|
102
105
|
// wasm transform pass
|
103
106
|
for (const f of funcs) {
|
104
107
|
const wasm = f.wasm;
|
@@ -127,6 +130,13 @@ export default (funcs, globals, pages) => {
|
|
127
130
|
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
128
131
|
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
129
132
|
|
133
|
+
if (inst[0] === Opcodes.throw) {
|
134
|
+
tagUse[inst[1]]++;
|
135
|
+
|
136
|
+
const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
|
137
|
+
exceptionUse[exceptId]++;
|
138
|
+
}
|
139
|
+
|
130
140
|
if (inst[0] === Opcodes.block) {
|
131
141
|
// remove unneeded blocks (no brs inside)
|
132
142
|
// block
|
@@ -143,7 +153,7 @@ export default (funcs, globals, pages) => {
|
|
143
153
|
depth--;
|
144
154
|
if (depth <= 0) break;
|
145
155
|
}
|
146
|
-
if (op === Opcodes.br || op === Opcodes.br_if) {
|
156
|
+
if (op === Opcodes.br || op === Opcodes.br_if || op === Opcodes.br_table) {
|
147
157
|
hasBranch = true;
|
148
158
|
break;
|
149
159
|
}
|
@@ -188,6 +198,7 @@ export default (funcs, globals, pages) => {
|
|
188
198
|
let missing = false;
|
189
199
|
if (type === 'Array') missing = !pages.hasArray;
|
190
200
|
if (type === 'String') missing = !pages.hasString;
|
201
|
+
if (type === 'ByteString') missing = !pages.hasByteString;
|
191
202
|
|
192
203
|
if (missing) {
|
193
204
|
let j = i, depth = 0;
|
@@ -541,5 +552,18 @@ export default (funcs, globals, pages) => {
|
|
541
552
|
}
|
542
553
|
}
|
543
554
|
|
555
|
+
for (const x in tagUse) {
|
556
|
+
if (tagUse[x] === 0) {
|
557
|
+
const el = tags.find(y => y.idx === x);
|
558
|
+
tags.splice(tags.indexOf(el), 1);
|
559
|
+
}
|
560
|
+
}
|
561
|
+
|
562
|
+
for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
563
|
+
if (exceptionUse[x] === 0) {
|
564
|
+
exceptions.splice(+x, 1);
|
565
|
+
}
|
566
|
+
}
|
567
|
+
|
544
568
|
// return funcs;
|
545
569
|
};
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import fs from 'node:fs';
|
2
|
+
import { join } from 'node:path';
|
3
|
+
|
4
|
+
import { fileURLToPath } from 'node:url';
|
5
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
6
|
+
|
7
|
+
// import porfParse from './parse.js';
|
8
|
+
// import porfCodegen from './codeGen.js';
|
9
|
+
|
10
|
+
const argv = process.argv.slice();
|
11
|
+
|
12
|
+
const compile = async (file, [ _funcs, _globals ]) => {
|
13
|
+
const source = fs.readFileSync(file, 'utf8');
|
14
|
+
const first = source.slice(0, source.indexOf('\n'));
|
15
|
+
|
16
|
+
let args = ['-bytestring'];
|
17
|
+
if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
|
18
|
+
if (first.startsWith('// @porf')) {
|
19
|
+
args = args.concat(first.slice('// @porf '.length).split(' '));
|
20
|
+
}
|
21
|
+
process.argv = argv.concat(args);
|
22
|
+
|
23
|
+
// globalThis.optLog = process.argv.includes('-opt-log');
|
24
|
+
// globalThis.codeLog = process.argv.includes('-code-log');
|
25
|
+
// globalThis.allocLog = process.argv.includes('-alloc-log');
|
26
|
+
// globalThis.regexLog = process.argv.includes('-regex-log');
|
27
|
+
|
28
|
+
// const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
|
29
|
+
// const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
|
30
|
+
|
31
|
+
// let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
|
32
|
+
|
33
|
+
const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
|
34
|
+
|
35
|
+
let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
|
36
|
+
|
37
|
+
funcs = funcs.filter(x => x.export);
|
38
|
+
for (const x of funcs) {
|
39
|
+
if (x.data) x.data = x.data.map(x => data[x]);
|
40
|
+
if (x.exceptions) x.exceptions = x.exceptions.map(x => {
|
41
|
+
const obj = exceptions[x];
|
42
|
+
if (obj) obj.exceptId = x;
|
43
|
+
return obj;
|
44
|
+
}).filter(x => x);
|
45
|
+
}
|
46
|
+
|
47
|
+
_funcs.push(...funcs);
|
48
|
+
_globals.push(...Object.values(globals));
|
49
|
+
};
|
50
|
+
|
51
|
+
const precompile = async () => {
|
52
|
+
const dir = join(__dirname, 'builtins');
|
53
|
+
|
54
|
+
let funcs = [], globals = [];
|
55
|
+
for (const file of fs.readdirSync(dir)) {
|
56
|
+
if (file.endsWith('.d.ts')) continue;
|
57
|
+
await compile(join(dir, file), [ funcs, globals ]);
|
58
|
+
}
|
59
|
+
|
60
|
+
// todo: globals, exceptions, pages per func
|
61
|
+
|
62
|
+
return `// autogenerated by precompile.js
|
63
|
+
|
64
|
+
export const BuiltinFuncs = function() {
|
65
|
+
${funcs.map(x => ` this.${x.name} = {
|
66
|
+
wasm: ${JSON.stringify(x.wasm)},
|
67
|
+
params: ${JSON.stringify(x.params)},
|
68
|
+
typedParams: true,
|
69
|
+
returns: ${JSON.stringify(x.returns)},
|
70
|
+
typedReturns: true,
|
71
|
+
locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length * 2).map(x => x.type))},
|
72
|
+
${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
|
73
|
+
${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
|
74
|
+
${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
|
75
|
+
};`.replaceAll('\n\n', '\n')).join('\n')}
|
76
|
+
}`;
|
77
|
+
};
|
78
|
+
|
79
|
+
console.log(await precompile());
|
package/compiler/prototype.js
CHANGED
@@ -16,7 +16,8 @@ const TYPES = {
|
|
16
16
|
|
17
17
|
// these are not "typeof" types but tracked internally
|
18
18
|
_array: 0x10,
|
19
|
-
_regexp: 0x11
|
19
|
+
_regexp: 0x11,
|
20
|
+
_bytestring: 0x12
|
20
21
|
};
|
21
22
|
|
22
23
|
// todo: turn these into built-ins once arrays and these become less hacky
|
@@ -71,7 +72,7 @@ export const PrototypeFuncs = function() {
|
|
71
72
|
],
|
72
73
|
|
73
74
|
// todo: only for 1 argument
|
74
|
-
push: (pointer, length, wNewMember) => [
|
75
|
+
push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
|
75
76
|
// get memory offset of array at last index (length)
|
76
77
|
...length.getCachedI32(),
|
77
78
|
...number(ValtypeSize[valtype], Valtype.i32),
|
@@ -92,22 +93,28 @@ export const PrototypeFuncs = function() {
|
|
92
93
|
...number(1, Valtype.i32),
|
93
94
|
[ Opcodes.i32_add ],
|
94
95
|
|
95
|
-
...
|
96
|
-
|
96
|
+
...(unusedValue() ? [] : [
|
97
|
+
...length.setCachedI32(),
|
98
|
+
...length.getCachedI32(),
|
99
|
+
])
|
97
100
|
]),
|
98
101
|
|
99
|
-
...
|
100
|
-
|
102
|
+
...(unusedValue() ? [] : [
|
103
|
+
...length.getCachedI32(),
|
104
|
+
Opcodes.i32_from_u
|
105
|
+
])
|
101
106
|
|
102
107
|
// ...length.get()
|
103
108
|
],
|
104
109
|
|
105
|
-
pop: (pointer, length) => [
|
110
|
+
pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
|
106
111
|
// if length == 0, noop
|
107
112
|
...length.getCachedI32(),
|
108
113
|
[ Opcodes.i32_eqz ],
|
109
114
|
[ Opcodes.if, Blocktype.void ],
|
110
|
-
...
|
115
|
+
...(unusedValue() ? [] : [
|
116
|
+
...number(UNDEFINED),
|
117
|
+
]),
|
111
118
|
[ Opcodes.br, 1 ],
|
112
119
|
[ Opcodes.end ],
|
113
120
|
|
@@ -119,19 +126,23 @@ export const PrototypeFuncs = function() {
|
|
119
126
|
...number(1, Valtype.i32),
|
120
127
|
[ Opcodes.i32_sub ],
|
121
128
|
|
122
|
-
...
|
123
|
-
|
129
|
+
...(unusedValue() ? [] : [
|
130
|
+
...length.setCachedI32(),
|
131
|
+
...length.getCachedI32(),
|
132
|
+
])
|
124
133
|
]),
|
125
134
|
|
126
135
|
// load last element
|
127
|
-
...
|
128
|
-
|
129
|
-
|
136
|
+
...(unusedValue() ? [] : [
|
137
|
+
...length.getCachedI32(),
|
138
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
139
|
+
[ Opcodes.i32_mul ],
|
130
140
|
|
131
|
-
|
132
|
-
|
141
|
+
...pointer,
|
142
|
+
[ Opcodes.i32_add ],
|
133
143
|
|
134
|
-
|
144
|
+
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
145
|
+
])
|
135
146
|
],
|
136
147
|
|
137
148
|
shift: (pointer, length) => [
|
@@ -331,8 +342,8 @@ export const PrototypeFuncs = function() {
|
|
331
342
|
...number(0, Valtype.i32), // base 0 for store later
|
332
343
|
|
333
344
|
...wIndex,
|
334
|
-
|
335
345
|
Opcodes.i32_to,
|
346
|
+
|
336
347
|
...number(ValtypeSize.i16, Valtype.i32),
|
337
348
|
[ Opcodes.i32_mul ],
|
338
349
|
|
@@ -472,8 +483,151 @@ export const PrototypeFuncs = function() {
|
|
472
483
|
this[TYPES.string].at.returnType = TYPES.string;
|
473
484
|
this[TYPES.string].charAt.returnType = TYPES.string;
|
474
485
|
this[TYPES.string].charCodeAt.local = Valtype.i32;
|
486
|
+
this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
475
487
|
|
476
488
|
this[TYPES.string].isWellFormed.local = Valtype.i32;
|
477
489
|
this[TYPES.string].isWellFormed.local2 = Valtype.i32;
|
478
490
|
this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
|
491
|
+
|
492
|
+
if (process.argv.includes('-bytestring')) {
|
493
|
+
this[TYPES._bytestring] = {
|
494
|
+
at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
|
495
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
496
|
+
|
497
|
+
return [
|
498
|
+
// setup new/out array
|
499
|
+
...newOut,
|
500
|
+
[ Opcodes.drop ],
|
501
|
+
|
502
|
+
...number(0, Valtype.i32), // base 0 for store later
|
503
|
+
|
504
|
+
...wIndex,
|
505
|
+
Opcodes.i32_to_u,
|
506
|
+
[ Opcodes.local_tee, iTmp ],
|
507
|
+
|
508
|
+
// if index < 0: access index + array length
|
509
|
+
...number(0, Valtype.i32),
|
510
|
+
[ Opcodes.i32_lt_s ],
|
511
|
+
[ Opcodes.if, Blocktype.void ],
|
512
|
+
[ Opcodes.local_get, iTmp ],
|
513
|
+
...length.getCachedI32(),
|
514
|
+
[ Opcodes.i32_add ],
|
515
|
+
[ Opcodes.local_set, iTmp ],
|
516
|
+
[ Opcodes.end ],
|
517
|
+
|
518
|
+
// if still < 0 or >= length: return undefined
|
519
|
+
[ Opcodes.local_get, iTmp ],
|
520
|
+
...number(0, Valtype.i32),
|
521
|
+
[ Opcodes.i32_lt_s ],
|
522
|
+
|
523
|
+
[ Opcodes.local_get, iTmp ],
|
524
|
+
...length.getCachedI32(),
|
525
|
+
[ Opcodes.i32_ge_s ],
|
526
|
+
[ Opcodes.i32_or ],
|
527
|
+
|
528
|
+
[ Opcodes.if, Blocktype.void ],
|
529
|
+
...number(UNDEFINED),
|
530
|
+
[ Opcodes.br, 1 ],
|
531
|
+
[ Opcodes.end ],
|
532
|
+
|
533
|
+
[ Opcodes.local_get, iTmp ],
|
534
|
+
|
535
|
+
...pointer,
|
536
|
+
[ Opcodes.i32_add ],
|
537
|
+
|
538
|
+
// load current string ind {arg}
|
539
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
540
|
+
|
541
|
+
// store to new string ind 0
|
542
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
543
|
+
|
544
|
+
// return new string (pointer)
|
545
|
+
...number(newPointer)
|
546
|
+
];
|
547
|
+
},
|
548
|
+
|
549
|
+
// todo: out of bounds properly
|
550
|
+
charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
|
551
|
+
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
552
|
+
|
553
|
+
return [
|
554
|
+
// setup new/out array
|
555
|
+
...newOut,
|
556
|
+
[ Opcodes.drop ],
|
557
|
+
|
558
|
+
...number(0, Valtype.i32), // base 0 for store later
|
559
|
+
|
560
|
+
...wIndex,
|
561
|
+
Opcodes.i32_to,
|
562
|
+
|
563
|
+
...pointer,
|
564
|
+
[ Opcodes.i32_add ],
|
565
|
+
|
566
|
+
// load current string ind {arg}
|
567
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
568
|
+
|
569
|
+
// store to new string ind 0
|
570
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
571
|
+
|
572
|
+
// return new string (page)
|
573
|
+
...number(newPointer)
|
574
|
+
];
|
575
|
+
},
|
576
|
+
|
577
|
+
charCodeAt: (pointer, length, wIndex, iTmp) => {
|
578
|
+
return [
|
579
|
+
...wIndex,
|
580
|
+
Opcodes.i32_to,
|
581
|
+
|
582
|
+
...(zeroChecks.charcodeat ? [] : [
|
583
|
+
[ Opcodes.local_set, iTmp ],
|
584
|
+
|
585
|
+
// index < 0
|
586
|
+
...(noUnlikelyChecks ? [] : [
|
587
|
+
[ Opcodes.local_get, iTmp ],
|
588
|
+
...number(0, Valtype.i32),
|
589
|
+
[ Opcodes.i32_lt_s ],
|
590
|
+
]),
|
591
|
+
|
592
|
+
// index >= length
|
593
|
+
[ Opcodes.local_get, iTmp ],
|
594
|
+
...length.getCachedI32(),
|
595
|
+
[ Opcodes.i32_ge_s ],
|
596
|
+
|
597
|
+
...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
|
598
|
+
[ Opcodes.if, Blocktype.void ],
|
599
|
+
...number(NaN),
|
600
|
+
[ Opcodes.br, 1 ],
|
601
|
+
[ Opcodes.end ],
|
602
|
+
|
603
|
+
[ Opcodes.local_get, iTmp ],
|
604
|
+
]),
|
605
|
+
|
606
|
+
...pointer,
|
607
|
+
[ Opcodes.i32_add ],
|
608
|
+
|
609
|
+
// load current string ind {arg}
|
610
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
611
|
+
Opcodes.i32_from_u
|
612
|
+
];
|
613
|
+
},
|
614
|
+
|
615
|
+
isWellFormed: () => {
|
616
|
+
return [
|
617
|
+
// we know it must be true as it is a bytestring lol
|
618
|
+
...number(1)
|
619
|
+
]
|
620
|
+
}
|
621
|
+
};
|
622
|
+
|
623
|
+
this[TYPES._bytestring].at.local = Valtype.i32;
|
624
|
+
this[TYPES._bytestring].at.returnType = TYPES._bytestring;
|
625
|
+
this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
|
626
|
+
this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
|
627
|
+
this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
|
628
|
+
|
629
|
+
this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
|
630
|
+
this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
|
631
|
+
this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
|
632
|
+
}
|
479
633
|
};
|
package/compiler/sections.js
CHANGED
@@ -95,7 +95,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
95
95
|
// https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
|
96
96
|
const chSection = !compileHints ? [] : customSection(
|
97
97
|
'compilationHints',
|
98
|
-
// for now just do everything as
|
98
|
+
// for now just do everything as optimize eager
|
99
99
|
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
100
100
|
);
|
101
101
|
|
package/compiler/wasmSpec.js
CHANGED
@@ -32,8 +32,6 @@ export const Opcodes = {
|
|
32
32
|
throw: 0x08,
|
33
33
|
rethrow: 0x09,
|
34
34
|
|
35
|
-
return: 0x0F,
|
36
|
-
|
37
35
|
call: 0x10,
|
38
36
|
call_indirect: 0x11,
|
39
37
|
return_call: 0x12,
|
@@ -42,7 +40,10 @@ export const Opcodes = {
|
|
42
40
|
end: 0x0b,
|
43
41
|
br: 0x0c,
|
44
42
|
br_if: 0x0d,
|
43
|
+
br_table: 0x0e,
|
44
|
+
return: 0x0f,
|
45
45
|
call: 0x10,
|
46
|
+
|
46
47
|
drop: 0x1a,
|
47
48
|
|
48
49
|
local_get: 0x20,
|
@@ -56,9 +57,12 @@ export const Opcodes = {
|
|
56
57
|
i64_load: 0x29,
|
57
58
|
f64_load: 0x2b,
|
58
59
|
|
60
|
+
i32_load8_s: 0x2c,
|
61
|
+
i32_load8_u: 0x2d,
|
59
62
|
i32_load16_s: 0x2e,
|
60
63
|
i32_load16_u: 0x2f,
|
61
64
|
|
65
|
+
i32_store8: 0x3a,
|
62
66
|
i32_store16: 0x3b,
|
63
67
|
|
64
68
|
i32_store: 0x36,
|
package/compiler/wrap.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
|
+
import { encodeVector, encodeLocal } from './encoding.js';
|
3
4
|
// import fs from 'node:fs';
|
4
5
|
|
5
6
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
@@ -18,7 +19,8 @@ const TYPES = {
|
|
18
19
|
|
19
20
|
// internal
|
20
21
|
[internalTypeBase]: '_array',
|
21
|
-
[internalTypeBase + 1]: '_regexp'
|
22
|
+
[internalTypeBase + 1]: '_regexp',
|
23
|
+
[internalTypeBase + 2]: '_bytestring'
|
22
24
|
};
|
23
25
|
|
24
26
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
@@ -35,14 +37,98 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
35
37
|
if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
36
38
|
|
37
39
|
const t2 = performance.now();
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
|
41
|
+
let instance;
|
42
|
+
try {
|
43
|
+
0, { instance } = await WebAssembly.instantiate(wasm, {
|
44
|
+
'': {
|
45
|
+
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
46
|
+
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
47
|
+
t: _ => performance.now(),
|
48
|
+
...customImports
|
49
|
+
}
|
50
|
+
});
|
51
|
+
} catch (e) {
|
52
|
+
// only backtrace for runner, not test262/etc
|
53
|
+
if (!process.argv[1].includes('/runner')) throw e;
|
54
|
+
|
55
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
|
56
|
+
const blobOffset = parseInt(e.message.split('@')[1]);
|
57
|
+
|
58
|
+
// convert blob offset -> function wasm offset.
|
59
|
+
// this is not good code and is somewhat duplicated
|
60
|
+
// I just want it to work for debugging, I don't care about perf/yes
|
61
|
+
|
62
|
+
const func = funcs.find(x => x.index === funcInd);
|
63
|
+
const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
|
64
|
+
|
65
|
+
let localDecl = [], typeCount = 0, lastType;
|
66
|
+
for (let i = 0; i < locals.length; i++) {
|
67
|
+
const local = locals[i];
|
68
|
+
if (i !== 0 && local.type !== lastType) {
|
69
|
+
localDecl.push(encodeLocal(typeCount, lastType));
|
70
|
+
typeCount = 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
typeCount++;
|
74
|
+
lastType = local.type;
|
75
|
+
}
|
76
|
+
|
77
|
+
if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
|
78
|
+
|
79
|
+
const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 40));
|
80
|
+
|
81
|
+
let i = 0;
|
82
|
+
for (; i < wasm.length; i++) {
|
83
|
+
let mismatch = false;
|
84
|
+
for (let j = 0; j < toFind.length; j++) {
|
85
|
+
if (wasm[i + j] !== toFind[j]) {
|
86
|
+
mismatch = true;
|
87
|
+
break;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
if (!mismatch) break;
|
92
|
+
}
|
93
|
+
|
94
|
+
if (i === wasm.length) throw e;
|
95
|
+
|
96
|
+
const offset = (blobOffset - i) + encodeVector(localDecl).length;
|
97
|
+
|
98
|
+
let cumLen = 0;
|
99
|
+
i = 0;
|
100
|
+
for (; i < func.wasm.length; i++) {
|
101
|
+
cumLen += func.wasm[i].filter(x => x != null && x <= 0xff).length;
|
102
|
+
if (cumLen === offset) break;
|
103
|
+
}
|
104
|
+
|
105
|
+
if (cumLen !== offset) throw e;
|
106
|
+
|
107
|
+
i -= 1;
|
108
|
+
|
109
|
+
console.log(`\x1B[35m\x1B[1mporffor backtrace\u001b[0m`);
|
110
|
+
|
111
|
+
console.log('\x1B[4m' + func.name + '\x1B[0m');
|
112
|
+
|
113
|
+
const surrounding = 6;
|
114
|
+
|
115
|
+
const decomp = decompile(func.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, func.locals, func.params, func.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
|
116
|
+
|
117
|
+
const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
|
118
|
+
let longest = 0;
|
119
|
+
for (let j = 0; j < decomp.length; j++) {
|
120
|
+
longest = Math.max(longest, noAnsi(decomp[j]).length);
|
44
121
|
}
|
45
|
-
|
122
|
+
|
123
|
+
const middle = Math.floor(decomp.length / 2);
|
124
|
+
decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
|
125
|
+
|
126
|
+
console.log('\x1B[90m...\x1B[0m');
|
127
|
+
console.log(decomp.join('\n'));
|
128
|
+
console.log('\x1B[90m...\x1B[0m\n');
|
129
|
+
|
130
|
+
throw e;
|
131
|
+
}
|
46
132
|
|
47
133
|
times.push(performance.now() - t2);
|
48
134
|
if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
@@ -95,10 +181,18 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
95
181
|
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
96
182
|
}
|
97
183
|
|
184
|
+
case '_bytestring': {
|
185
|
+
const pointer = ret;
|
186
|
+
const length = new Int32Array(memory.buffer, pointer, 1);
|
187
|
+
|
188
|
+
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
189
|
+
}
|
190
|
+
|
98
191
|
case 'function': {
|
99
192
|
// wasm func index, including all imports
|
100
193
|
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
101
|
-
if (!func) return ret;
|
194
|
+
// if (!func) return ret;
|
195
|
+
if (!func) return function () {};
|
102
196
|
|
103
197
|
// make fake empty func for repl/etc
|
104
198
|
return {[func.name]() {}}[func.name];
|
package/demo.js
ADDED
package/demo.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
console.log("hello world");
|
package/filesize.cmd
ADDED