porffor 0.0.0-61de729 → 0.0.0-679c4ea
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 +48 -14
- package/c +0 -0
- package/c.exe +0 -0
- package/compiler/2c.js +350 -0
- package/compiler/builtins.js +6 -1
- package/compiler/codeGen.js +435 -123
- package/compiler/decompile.js +3 -3
- package/compiler/embedding.js +9 -5
- package/compiler/encoding.js +4 -2
- package/compiler/index.js +47 -5
- package/compiler/opt.js +48 -22
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +92 -29
- package/compiler/sections.js +42 -4
- package/compiler/wrap.js +12 -3
- package/cool.exe +0 -0
- package/g +0 -0
- package/g.exe +0 -0
- package/hi.c +37 -0
- package/out +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/r.js +39 -0
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +54 -40
- package/runner/info.js +37 -2
- package/runner/transform.js +2 -1
- package/tmp.c +58 -0
package/compiler/decompile.js
CHANGED
@@ -42,8 +42,8 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
42
42
|
out += ` ${read_ieee754_binary64(inst.slice(1))}`;
|
43
43
|
} else if (inst[0] === Opcodes.i32_const || inst[0] === Opcodes.i64_const) {
|
44
44
|
out += ` ${read_signedLEB128(inst.slice(1))}`;
|
45
|
-
} else if (inst[0] === Opcodes.i32_load || inst[0] === Opcodes.i64_load || inst[0] === Opcodes.f64_load || inst[0] === Opcodes.i32_store || inst[0] === Opcodes.i64_store || inst[0] === Opcodes.f64_store) {
|
46
|
-
out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}
|
45
|
+
} else if (inst[0] === Opcodes.i32_load || inst[0] === Opcodes.i64_load || inst[0] === Opcodes.f64_load || inst[0] === Opcodes.i32_store || inst[0] === Opcodes.i64_store || inst[0] === Opcodes.f64_store || inst[0] === Opcodes.i32_store16 || inst[0] === Opcodes.i32_load16_u) {
|
46
|
+
out += ` ${inst[1]} ${read_unsignedLEB128(inst.slice(2))}`;
|
47
47
|
} else for (const operand of inst.slice(1)) {
|
48
48
|
if (inst[0] === Opcodes.if || inst[0] === Opcodes.loop || inst[0] === Opcodes.block) {
|
49
49
|
if (operand === Blocktype.void) continue;
|
@@ -57,7 +57,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
57
57
|
out += ` ;; label @${depth}`;
|
58
58
|
}
|
59
59
|
|
60
|
-
if (inst[0] === Opcodes.br) {
|
60
|
+
if (inst[0] === Opcodes.br || inst[0] === Opcodes.br_if) {
|
61
61
|
out += ` ;; goto @${depth - inst[1]}`;
|
62
62
|
}
|
63
63
|
|
package/compiler/embedding.js
CHANGED
@@ -9,11 +9,15 @@ export const number = (n, valtype = valtypeBinary) => {
|
|
9
9
|
}
|
10
10
|
};
|
11
11
|
|
12
|
-
const
|
12
|
+
export const enforceOneByte = arr => [ arr[0] ?? 0 ];
|
13
|
+
export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
|
14
|
+
export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
|
15
|
+
export const enforceEightBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0, arr[4] ?? 0, arr[5] ?? 0, arr[6] ?? 0, arr[7] ?? 0 ];
|
16
|
+
|
13
17
|
export const i32x4 = (a, b, c, d) => [ [
|
14
18
|
...Opcodes.v128_const,
|
15
|
-
...
|
16
|
-
...
|
17
|
-
...
|
18
|
-
...
|
19
|
+
...enforceFourBytes(signedLEB128(a)),
|
20
|
+
...enforceFourBytes(signedLEB128(b)),
|
21
|
+
...enforceFourBytes(signedLEB128(c)),
|
22
|
+
...enforceFourBytes(signedLEB128(d))
|
19
23
|
] ];
|
package/compiler/encoding.js
CHANGED
@@ -22,15 +22,15 @@ export const encodeLocal = (count, type) => [
|
|
22
22
|
type
|
23
23
|
];
|
24
24
|
|
25
|
+
// todo: this only works with integers within 32 bit range
|
25
26
|
export const signedLEB128 = n => {
|
26
|
-
|
27
|
+
n |= 0;
|
27
28
|
|
28
29
|
// just input for small numbers (for perf as common)
|
29
30
|
if (n >= 0 && n <= 63) return [ n ];
|
30
31
|
if (n >= -64 && n <= 0) return [ 128 + n ];
|
31
32
|
|
32
33
|
const buffer = [];
|
33
|
-
n |= 0;
|
34
34
|
|
35
35
|
while (true) {
|
36
36
|
let byte = n & 0x7f;
|
@@ -50,6 +50,8 @@ export const signedLEB128 = n => {
|
|
50
50
|
};
|
51
51
|
|
52
52
|
export const unsignedLEB128 = n => {
|
53
|
+
n |= 0;
|
54
|
+
|
53
55
|
// just input for small numbers (for perf as common)
|
54
56
|
if (n >= 0 && n <= 127) return [ n ];
|
55
57
|
|
package/compiler/index.js
CHANGED
@@ -4,6 +4,8 @@ import opt from './opt.js';
|
|
4
4
|
import produceSections from './sections.js';
|
5
5
|
import decompile from './decompile.js';
|
6
6
|
import { BuiltinPreludes } from './builtins.js';
|
7
|
+
import toc from './2c.js';
|
8
|
+
|
7
9
|
|
8
10
|
globalThis.decompile = decompile;
|
9
11
|
|
@@ -15,7 +17,8 @@ const areaColors = {
|
|
15
17
|
codegen: [ 20, 80, 250 ],
|
16
18
|
opt: [ 250, 20, 80 ],
|
17
19
|
sections: [ 20, 250, 80 ],
|
18
|
-
alloc: [ 250, 250, 20 ]
|
20
|
+
alloc: [ 250, 250, 20 ],
|
21
|
+
'2c': [ 20, 250, 250 ]
|
19
22
|
};
|
20
23
|
|
21
24
|
globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
|
@@ -36,10 +39,16 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
36
39
|
console.log();
|
37
40
|
};
|
38
41
|
|
42
|
+
const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
|
43
|
+
|
44
|
+
const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
|
45
|
+
const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
46
|
+
|
39
47
|
export default (code, flags) => {
|
40
48
|
globalThis.optLog = process.argv.includes('-opt-log');
|
41
49
|
globalThis.codeLog = process.argv.includes('-code-log');
|
42
50
|
globalThis.allocLog = process.argv.includes('-alloc-log');
|
51
|
+
globalThis.regexLog = process.argv.includes('-regex-log');
|
43
52
|
|
44
53
|
for (const x in BuiltinPreludes) {
|
45
54
|
if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
|
@@ -50,7 +59,7 @@ export default (code, flags) => {
|
|
50
59
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
51
60
|
|
52
61
|
const t1 = performance.now();
|
53
|
-
const { funcs, globals, tags, exceptions, pages } = codeGen(program);
|
62
|
+
const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
|
54
63
|
if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
55
64
|
|
56
65
|
if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
|
@@ -62,15 +71,48 @@ export default (code, flags) => {
|
|
62
71
|
if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
|
63
72
|
|
64
73
|
const t3 = performance.now();
|
65
|
-
const sections = produceSections(funcs, globals, tags, pages, flags);
|
74
|
+
const sections = produceSections(funcs, globals, tags, pages, data, flags);
|
66
75
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
67
76
|
|
68
77
|
if (allocLog) {
|
69
78
|
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
70
79
|
const bytes = wasmPages * 65536;
|
71
80
|
log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
|
72
|
-
|
81
|
+
console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
|
82
|
+
}
|
83
|
+
|
84
|
+
const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
|
85
|
+
|
86
|
+
const target = getArg('target') ?? getArg('t') ?? 'wasm';
|
87
|
+
const outFile = getArg('o');
|
88
|
+
|
89
|
+
if (target === 'c') {
|
90
|
+
const c = toc(out);
|
91
|
+
|
92
|
+
if (outFile) {
|
93
|
+
writeFileSync(outFile, c);
|
94
|
+
} else {
|
95
|
+
console.log(c);
|
96
|
+
}
|
97
|
+
|
98
|
+
process.exit();
|
99
|
+
}
|
100
|
+
|
101
|
+
if (target === 'native') {
|
102
|
+
const compiler = getArg('compiler') ?? 'clang';
|
103
|
+
const cO = getArg('cO') ?? 'Ofast';
|
104
|
+
|
105
|
+
const tmpfile = 'tmp.c';
|
106
|
+
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
|
107
|
+
|
108
|
+
const c = toc(out);
|
109
|
+
writeFileSync(tmpfile, c);
|
110
|
+
|
111
|
+
// obvious command escape is obvious
|
112
|
+
execSync(args.join(' '), { stdio: 'inherit' });
|
113
|
+
|
114
|
+
process.exit();
|
73
115
|
}
|
74
116
|
|
75
|
-
return
|
117
|
+
return out;
|
76
118
|
};
|
package/compiler/opt.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Opcodes, Valtype } from "./wasmSpec.js";
|
2
2
|
import { number } from "./embedding.js";
|
3
|
+
import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
|
3
4
|
|
4
5
|
// deno compat
|
5
6
|
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
@@ -95,7 +96,6 @@ export default (funcs, globals) => {
|
|
95
96
|
}
|
96
97
|
|
97
98
|
if (t.index > c.index) t.index--; // adjust index if after removed func
|
98
|
-
if (c.memory) t.memory = true;
|
99
99
|
}
|
100
100
|
|
101
101
|
funcs.splice(funcs.indexOf(c), 1); // remove func from funcs
|
@@ -142,7 +142,7 @@ export default (funcs, globals) => {
|
|
142
142
|
depth--;
|
143
143
|
if (depth <= 0) break;
|
144
144
|
}
|
145
|
-
if (op === Opcodes.br) {
|
145
|
+
if (op === Opcodes.br || op === Opcodes.br_if) {
|
146
146
|
hasBranch = true;
|
147
147
|
break;
|
148
148
|
}
|
@@ -213,9 +213,9 @@ export default (funcs, globals) => {
|
|
213
213
|
// i32.const 0
|
214
214
|
// drop
|
215
215
|
// -->
|
216
|
-
// <nothing
|
216
|
+
// <nothing>
|
217
217
|
|
218
|
-
wasm.splice(i - 1, 2); // remove
|
218
|
+
wasm.splice(i - 1, 2); // remove these inst
|
219
219
|
i -= 2;
|
220
220
|
continue;
|
221
221
|
}
|
@@ -259,6 +259,36 @@ export default (funcs, globals) => {
|
|
259
259
|
continue;
|
260
260
|
}
|
261
261
|
|
262
|
+
if (lastInst[0] === Opcodes.const && (inst === Opcodes.i32_to || inst === Opcodes.i32_to_u)) {
|
263
|
+
// change const and immediate i32 convert to i32 const
|
264
|
+
// f64.const 0
|
265
|
+
// i32.trunc_sat_f64_s || i32.trunc_sat_f64_u
|
266
|
+
// -->
|
267
|
+
// i32.const 0
|
268
|
+
|
269
|
+
wasm[i - 1] = number((valtype === 'f64' ? read_ieee754_binary64 : read_signedLEB128)(lastInst.slice(1)), Valtype.i32)[0]; // f64.const -> i32.const
|
270
|
+
|
271
|
+
wasm.splice(i, 1); // remove this inst
|
272
|
+
i--;
|
273
|
+
if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
274
|
+
continue;
|
275
|
+
}
|
276
|
+
|
277
|
+
if (lastInst[0] === Opcodes.i32_const && (inst === Opcodes.i32_from || inst === Opcodes.i32_from_u)) {
|
278
|
+
// change i32 const and immediate convert to const (opposite way of previous)
|
279
|
+
// i32.const 0
|
280
|
+
// f64.convert_i32_s || f64.convert_i32_u
|
281
|
+
// -->
|
282
|
+
// f64.const 0
|
283
|
+
|
284
|
+
wasm[i - 1] = number(read_signedLEB128(lastInst.slice(1)))[0]; // i32.const -> f64.const
|
285
|
+
|
286
|
+
wasm.splice(i, 1); // remove this inst
|
287
|
+
i--;
|
288
|
+
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
289
|
+
continue;
|
290
|
+
}
|
291
|
+
|
262
292
|
if (tailCall && lastInst[0] === Opcodes.call && inst[0] === Opcodes.return) {
|
263
293
|
// replace call, return with tail calls (return_call)
|
264
294
|
// call X
|
@@ -287,28 +317,24 @@ export default (funcs, globals) => {
|
|
287
317
|
continue;
|
288
318
|
}
|
289
319
|
|
290
|
-
|
291
|
-
const
|
320
|
+
// remove unneeded before get with update exprs (n++, etc) when value is unused
|
321
|
+
if (i < wasm.length - 4 && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get && wasm[i + 1][0] === Opcodes.const && [Opcodes.add, Opcodes.sub].includes(wasm[i + 2][0]) && wasm[i + 3][0] === Opcodes.local_set && wasm[i + 3][1] === inst[1] && (wasm[i + 4][0] === Opcodes.drop || wasm[i + 4][0] === Opcodes.br)) {
|
322
|
+
// local.get 1
|
323
|
+
// local.get 1
|
324
|
+
// -->
|
325
|
+
// local.get 1
|
292
326
|
|
293
|
-
|
294
|
-
|
295
|
-
if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
|
296
|
-
// local.get 1
|
297
|
-
// local.get 1
|
298
|
-
// -->
|
299
|
-
// local.get 1
|
300
|
-
|
301
|
-
// remove drop at the end as well
|
302
|
-
if (wasm[i + 4][0] === Opcodes.drop) {
|
303
|
-
wasm.splice(i + 4, 1);
|
304
|
-
}
|
327
|
+
// remove drop at the end as well
|
328
|
+
if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
|
305
329
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
}
|
330
|
+
wasm.splice(i, 1); // remove this inst (second get)
|
331
|
+
i--;
|
332
|
+
continue;
|
310
333
|
}
|
311
334
|
|
335
|
+
if (i < 2) continue;
|
336
|
+
const lastLastInst = wasm[i - 2];
|
337
|
+
|
312
338
|
if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
|
313
339
|
// local.set x
|
314
340
|
// local.tee y
|
package/compiler/parse.js
CHANGED
package/compiler/prototype.js
CHANGED
@@ -15,13 +15,17 @@ const TYPES = {
|
|
15
15
|
bigint: 0xffffffffffff7,
|
16
16
|
|
17
17
|
// these are not "typeof" types but tracked internally
|
18
|
-
_array:
|
18
|
+
_array: 0xfffffffffff0f,
|
19
|
+
_regexp: 0xfffffffffff1f
|
19
20
|
};
|
20
21
|
|
21
22
|
// todo: turn these into built-ins once arrays and these become less hacky
|
22
23
|
|
23
24
|
export const PrototypeFuncs = function() {
|
24
25
|
const noUnlikelyChecks = process.argv.includes('-funsafe-no-unlikely-proto-checks');
|
26
|
+
let zeroChecks = process.argv.find(x => x.startsWith('-funsafe-zero-proto-checks='));
|
27
|
+
if (zeroChecks) zeroChecks = zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
|
28
|
+
else zeroChecks = {};
|
25
29
|
|
26
30
|
this[TYPES._array] = {
|
27
31
|
// lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
|
@@ -35,7 +39,7 @@ export const PrototypeFuncs = function() {
|
|
35
39
|
[ Opcodes.i32_lt_s ],
|
36
40
|
[ Opcodes.if, Blocktype.void ],
|
37
41
|
[ Opcodes.local_get, iTmp ],
|
38
|
-
...length.
|
42
|
+
...length.getCachedI32(),
|
39
43
|
[ Opcodes.i32_add ],
|
40
44
|
[ Opcodes.local_set, iTmp ],
|
41
45
|
[ Opcodes.end ],
|
@@ -46,7 +50,7 @@ export const PrototypeFuncs = function() {
|
|
46
50
|
[ Opcodes.i32_lt_s ],
|
47
51
|
|
48
52
|
[ Opcodes.local_get, iTmp ],
|
49
|
-
...length.
|
53
|
+
...length.getCachedI32(),
|
50
54
|
[ Opcodes.i32_ge_s ],
|
51
55
|
[ Opcodes.i32_or ],
|
52
56
|
|
@@ -66,7 +70,7 @@ export const PrototypeFuncs = function() {
|
|
66
70
|
// todo: only for 1 argument
|
67
71
|
push: (pointer, length, wNewMember) => [
|
68
72
|
// get memory offset of array at last index (length)
|
69
|
-
...length.
|
73
|
+
...length.getCachedI32(),
|
70
74
|
...number(ValtypeSize[valtype], Valtype.i32),
|
71
75
|
[ Opcodes.i32_mul ],
|
72
76
|
|
@@ -78,17 +82,17 @@ export const PrototypeFuncs = function() {
|
|
78
82
|
|
79
83
|
// bump array length by 1 and return it
|
80
84
|
...length.setI32([
|
81
|
-
...length.
|
85
|
+
...length.getCachedI32(),
|
82
86
|
...number(1, Valtype.i32),
|
83
87
|
[ Opcodes.i32_add ]
|
84
88
|
]),
|
85
89
|
|
86
|
-
...length.get
|
90
|
+
...length.get()
|
87
91
|
],
|
88
92
|
|
89
93
|
pop: (pointer, length) => [
|
90
94
|
// if length == 0, noop
|
91
|
-
...length.
|
95
|
+
...length.getCachedI32(),
|
92
96
|
[ Opcodes.i32_eqz ],
|
93
97
|
[ Opcodes.if, Blocktype.void ],
|
94
98
|
...number(UNDEFINED),
|
@@ -99,13 +103,13 @@ export const PrototypeFuncs = function() {
|
|
99
103
|
|
100
104
|
// decrement length by 1
|
101
105
|
...length.setI32([
|
102
|
-
...length.
|
106
|
+
...length.getCachedI32(),
|
103
107
|
...number(1, Valtype.i32),
|
104
108
|
[ Opcodes.i32_sub ]
|
105
109
|
]),
|
106
110
|
|
107
111
|
// load last element
|
108
|
-
...length.
|
112
|
+
...length.getCachedI32(),
|
109
113
|
...number(ValtypeSize[valtype], Valtype.i32),
|
110
114
|
[ Opcodes.i32_mul ],
|
111
115
|
|
@@ -114,7 +118,7 @@ export const PrototypeFuncs = function() {
|
|
114
118
|
|
115
119
|
shift: (pointer, length) => [
|
116
120
|
// if length == 0, noop
|
117
|
-
...length.
|
121
|
+
...length.getCachedI32(),
|
118
122
|
Opcodes.i32_eqz,
|
119
123
|
[ Opcodes.if, Blocktype.void ],
|
120
124
|
...number(UNDEFINED),
|
@@ -125,7 +129,7 @@ export const PrototypeFuncs = function() {
|
|
125
129
|
|
126
130
|
// decrement length by 1
|
127
131
|
...length.setI32([
|
128
|
-
...length.
|
132
|
+
...length.getCachedI32(),
|
129
133
|
...number(1, Valtype.i32),
|
130
134
|
[ Opcodes.i32_sub ]
|
131
135
|
]),
|
@@ -139,11 +143,66 @@ export const PrototypeFuncs = function() {
|
|
139
143
|
...number(pointer + ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32), // src = base array index + length size + an index
|
140
144
|
...number(pageSize - ValtypeSize.i32 - ValtypeSize[valtype], Valtype.i32), // size = PageSize - length size - an index
|
141
145
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ]
|
146
|
+
],
|
147
|
+
|
148
|
+
fill: (pointer, length, wElement, iTmp) => [
|
149
|
+
...wElement,
|
150
|
+
[ Opcodes.local_set, iTmp ],
|
151
|
+
|
152
|
+
// use cached length i32 as pointer
|
153
|
+
...length.getCachedI32(),
|
154
|
+
|
155
|
+
// length - 1 for indexes
|
156
|
+
...number(1, Valtype.i32),
|
157
|
+
[ Opcodes.i32_sub ],
|
158
|
+
|
159
|
+
// * sizeof value
|
160
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
161
|
+
[ Opcodes.i32_mul ],
|
162
|
+
|
163
|
+
...length.setCachedI32(),
|
164
|
+
|
165
|
+
...(noUnlikelyChecks ? [] : [
|
166
|
+
...length.getCachedI32(),
|
167
|
+
...number(0, Valtype.i32),
|
168
|
+
[ Opcodes.i32_lt_s ],
|
169
|
+
[ Opcodes.if, Blocktype.void ],
|
170
|
+
...number(pointer),
|
171
|
+
[ Opcodes.br, 1 ],
|
172
|
+
[ Opcodes.end ]
|
173
|
+
]),
|
174
|
+
|
175
|
+
[ Opcodes.loop, Blocktype.void ],
|
176
|
+
|
177
|
+
// set element using pointer
|
178
|
+
...length.getCachedI32(),
|
179
|
+
[ Opcodes.local_get, iTmp ],
|
180
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
|
181
|
+
|
182
|
+
// pointer - sizeof value
|
183
|
+
...length.getCachedI32(),
|
184
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
185
|
+
[ Opcodes.i32_sub ],
|
186
|
+
|
187
|
+
...length.setCachedI32(),
|
188
|
+
|
189
|
+
// if pointer >= 0, loop
|
190
|
+
...length.getCachedI32(),
|
191
|
+
...number(0, Valtype.i32),
|
192
|
+
[ Opcodes.i32_ge_s ],
|
193
|
+
[ Opcodes.br_if, 0 ],
|
194
|
+
|
195
|
+
[ Opcodes.end ],
|
196
|
+
|
197
|
+
// return this array
|
198
|
+
...number(pointer)
|
142
199
|
]
|
143
200
|
};
|
144
201
|
|
145
202
|
this[TYPES._array].at.local = Valtype.i32;
|
146
203
|
this[TYPES._array].push.noArgRetLength = true;
|
204
|
+
this[TYPES._array].fill.local = valtypeBinary;
|
205
|
+
this[TYPES._array].fill.returnType = TYPES._array;
|
147
206
|
|
148
207
|
this[TYPES.string] = {
|
149
208
|
at: (pointer, length, wIndex, iTmp, arrayShell) => {
|
@@ -165,7 +224,7 @@ export const PrototypeFuncs = function() {
|
|
165
224
|
[ Opcodes.i32_lt_s ],
|
166
225
|
[ Opcodes.if, Blocktype.void ],
|
167
226
|
[ Opcodes.local_get, iTmp ],
|
168
|
-
...length.
|
227
|
+
...length.getCachedI32(),
|
169
228
|
[ Opcodes.i32_add ],
|
170
229
|
[ Opcodes.local_set, iTmp ],
|
171
230
|
[ Opcodes.end ],
|
@@ -176,7 +235,7 @@ export const PrototypeFuncs = function() {
|
|
176
235
|
[ Opcodes.i32_lt_s ],
|
177
236
|
|
178
237
|
[ Opcodes.local_get, iTmp ],
|
179
|
-
...length.
|
238
|
+
...length.getCachedI32(),
|
180
239
|
[ Opcodes.i32_ge_s ],
|
181
240
|
[ Opcodes.i32_or ],
|
182
241
|
|
@@ -232,27 +291,31 @@ export const PrototypeFuncs = function() {
|
|
232
291
|
return [
|
233
292
|
...wIndex,
|
234
293
|
Opcodes.i32_to,
|
235
|
-
[ Opcodes.local_set, iTmp ],
|
236
294
|
|
237
|
-
|
238
|
-
|
295
|
+
...(zeroChecks.charcodeat ? [] : [
|
296
|
+
[ Opcodes.local_set, iTmp ],
|
297
|
+
|
298
|
+
// index < 0
|
299
|
+
...(noUnlikelyChecks ? [] : [
|
300
|
+
[ Opcodes.local_get, iTmp ],
|
301
|
+
...number(0, Valtype.i32),
|
302
|
+
[ Opcodes.i32_lt_s ],
|
303
|
+
]),
|
304
|
+
|
305
|
+
// index >= length
|
239
306
|
[ Opcodes.local_get, iTmp ],
|
240
|
-
...
|
241
|
-
[ Opcodes.
|
242
|
-
]),
|
307
|
+
...length.getCachedI32(),
|
308
|
+
[ Opcodes.i32_ge_s ],
|
243
309
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
310
|
+
...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
|
311
|
+
[ Opcodes.if, Blocktype.void ],
|
312
|
+
...number(NaN),
|
313
|
+
[ Opcodes.br, 1 ],
|
314
|
+
[ Opcodes.end ],
|
248
315
|
|
249
|
-
|
250
|
-
|
251
|
-
...number(NaN),
|
252
|
-
[ Opcodes.br, 1 ],
|
253
|
-
[ Opcodes.end ],
|
316
|
+
[ Opcodes.local_get, iTmp ],
|
317
|
+
]),
|
254
318
|
|
255
|
-
[ Opcodes.local_get, iTmp ],
|
256
319
|
...number(ValtypeSize.i16, Valtype.i32),
|
257
320
|
[ Opcodes.i32_mul ],
|
258
321
|
|
package/compiler/sections.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
|
2
|
-
import { encodeVector, encodeString, encodeLocal } from './encoding.js';
|
2
|
+
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
|
3
3
|
import { number } from './embedding.js';
|
4
4
|
import { importedFuncs } from './builtins.js';
|
5
5
|
|
@@ -8,11 +8,26 @@ const createSection = (type, data) => [
|
|
8
8
|
...encodeVector(data)
|
9
9
|
];
|
10
10
|
|
11
|
-
|
11
|
+
const customSection = (name, data) => [
|
12
|
+
Section.custom,
|
13
|
+
...encodeVector([...encodeString(name), ...data])
|
14
|
+
];
|
15
|
+
|
16
|
+
const chHint = (topTier, baselineTier, strategy) => {
|
17
|
+
// 1 byte of 4 2 bit components: spare, top tier, baseline tier, compilation strategy
|
18
|
+
// tiers: 0x00 = default, 0x01 = baseline (liftoff), 0x02 = optimized (turbofan)
|
19
|
+
// strategy: 0x00 = default, 0x01 = lazy, 0x02 = eager, 0x03 = lazy baseline, eager top tier
|
20
|
+
return (strategy | (baselineTier << 2) | (topTier << 4));
|
21
|
+
};
|
22
|
+
|
23
|
+
export default (funcs, globals, tags, pages, data, flags) => {
|
12
24
|
const types = [], typeCache = {};
|
13
25
|
|
14
26
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
15
27
|
|
28
|
+
const compileHints = process.argv.includes('-compile-hints');
|
29
|
+
if (compileHints) log('sections', 'warning: compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
|
30
|
+
|
16
31
|
const getType = (params, returns) => {
|
17
32
|
const hash = `${params.join(',')}_${returns.join(',')}`;
|
18
33
|
if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
|
@@ -61,6 +76,7 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
61
76
|
}
|
62
77
|
}
|
63
78
|
}
|
79
|
+
globalThis.importFuncs = importFuncs;
|
64
80
|
|
65
81
|
if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
|
66
82
|
|
@@ -74,6 +90,14 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
74
90
|
encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
|
75
91
|
);
|
76
92
|
|
93
|
+
// compilation hints section - unspec v8 only
|
94
|
+
// https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
|
95
|
+
const chSection = !compileHints ? [] : customSection(
|
96
|
+
'compilationHints',
|
97
|
+
// for now just do everything as optimise eager
|
98
|
+
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
99
|
+
);
|
100
|
+
|
77
101
|
const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
|
78
102
|
Section.global,
|
79
103
|
encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
|
@@ -131,13 +155,24 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
131
155
|
encodeVector(types)
|
132
156
|
);
|
133
157
|
|
158
|
+
const dataSection = data.length === 0 ? [] : createSection(
|
159
|
+
Section.data,
|
160
|
+
encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
|
161
|
+
);
|
162
|
+
|
163
|
+
const dataCountSection = data.length === 0 ? [] : createSection(
|
164
|
+
Section.data_count,
|
165
|
+
unsignedLEB128(data.length)
|
166
|
+
);
|
167
|
+
|
134
168
|
if (process.argv.includes('-sections')) console.log({
|
135
169
|
typeSection: typeSection.map(x => x.toString(16)),
|
136
170
|
importSection: importSection.map(x => x.toString(16)),
|
137
171
|
funcSection: funcSection.map(x => x.toString(16)),
|
138
172
|
globalSection: globalSection.map(x => x.toString(16)),
|
139
173
|
exportSection: exportSection.map(x => x.toString(16)),
|
140
|
-
codeSection: codeSection.map(x => x.toString(16))
|
174
|
+
codeSection: codeSection.map(x => x.toString(16)),
|
175
|
+
dataSection: dataSection.map(x => x.toString(16)),
|
141
176
|
});
|
142
177
|
|
143
178
|
return Uint8Array.from([
|
@@ -146,10 +181,13 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
146
181
|
...typeSection,
|
147
182
|
...importSection,
|
148
183
|
...funcSection,
|
184
|
+
...chSection,
|
149
185
|
...memorySection,
|
150
186
|
...tagSection,
|
151
187
|
...globalSection,
|
152
188
|
...exportSection,
|
153
|
-
...
|
189
|
+
...dataCountSection,
|
190
|
+
...codeSection,
|
191
|
+
...dataSection
|
154
192
|
]);
|
155
193
|
};
|
package/compiler/wrap.js
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
|
-
import fs from 'node:fs';
|
3
|
+
// import fs from 'node:fs';
|
4
4
|
|
5
5
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
6
6
|
|
7
7
|
const typeBase = 0xffffffffffff0;
|
8
|
+
const internalTypeBase = 0xfffffffffff0f;
|
8
9
|
const TYPES = {
|
9
10
|
[typeBase]: 'number',
|
10
11
|
[typeBase + 1]: 'boolean',
|
@@ -16,7 +17,8 @@ const TYPES = {
|
|
16
17
|
[typeBase + 7]: 'bigint',
|
17
18
|
|
18
19
|
// internal
|
19
|
-
[
|
20
|
+
[internalTypeBase]: '_array',
|
21
|
+
[internalTypeBase + 1]: '_regexp'
|
20
22
|
};
|
21
23
|
|
22
24
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
@@ -27,16 +29,23 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
27
29
|
|
28
30
|
if (source.includes('export function')) flags.push('module');
|
29
31
|
|
30
|
-
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
32
|
+
// fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
31
33
|
|
32
34
|
times.push(performance.now() - t1);
|
33
35
|
if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
34
36
|
|
37
|
+
const getString = pointer => {
|
38
|
+
const length = new Int32Array(memory.buffer, pointer, 1);
|
39
|
+
|
40
|
+
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
41
|
+
};
|
42
|
+
|
35
43
|
const t2 = performance.now();
|
36
44
|
const { instance } = await WebAssembly.instantiate(wasm, {
|
37
45
|
'': {
|
38
46
|
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
39
47
|
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
48
|
+
s: valtype === 'i64' ? i => print(getString(Number(i))) : i => print(getString(i)),
|
40
49
|
a: c => { if (!Number(c)) throw new Error(`assert failed`); },
|
41
50
|
t: _ => performance.now(),
|
42
51
|
...customImports
|
package/cool.exe
ADDED
Binary file
|
package/g
ADDED
Binary file
|
package/g.exe
ADDED
Binary file
|