porffor 0.0.0-bddcdc3 → 0.0.0-d650361
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 +45 -12
- 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 +397 -128
- package/compiler/decompile.js +3 -3
- package/compiler/encoding.js +4 -2
- package/compiler/index.js +53 -2
- package/compiler/opt.js +35 -5
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +92 -33
- package/compiler/sections.js +28 -2
- package/compiler/wrap.js +21 -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 -34
- package/runner/repl.js +3 -10
- package/runner/transform.js +2 -1
- package/runner/version.js +10 -0
- 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/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
|
|
@@ -14,7 +16,9 @@ const bold = x => `\u001b[1m${x}\u001b[0m`;
|
|
14
16
|
const areaColors = {
|
15
17
|
codegen: [ 20, 80, 250 ],
|
16
18
|
opt: [ 250, 20, 80 ],
|
17
|
-
sections: [ 20, 250, 80 ]
|
19
|
+
sections: [ 20, 250, 80 ],
|
20
|
+
alloc: [ 250, 250, 20 ],
|
21
|
+
'2c': [ 20, 250, 250 ]
|
18
22
|
};
|
19
23
|
|
20
24
|
globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
|
@@ -35,9 +39,16 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
35
39
|
console.log();
|
36
40
|
};
|
37
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
|
+
|
38
47
|
export default (code, flags) => {
|
39
48
|
globalThis.optLog = process.argv.includes('-opt-log');
|
40
49
|
globalThis.codeLog = process.argv.includes('-code-log');
|
50
|
+
globalThis.allocLog = process.argv.includes('-alloc-log');
|
51
|
+
globalThis.regexLog = process.argv.includes('-regex-log');
|
41
52
|
|
42
53
|
for (const x in BuiltinPreludes) {
|
43
54
|
if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
|
@@ -63,5 +74,45 @@ export default (code, flags) => {
|
|
63
74
|
const sections = produceSections(funcs, globals, tags, pages, flags);
|
64
75
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
65
76
|
|
66
|
-
|
77
|
+
if (allocLog) {
|
78
|
+
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
79
|
+
const bytes = wasmPages * 65536;
|
80
|
+
log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
|
81
|
+
// console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\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();
|
115
|
+
}
|
116
|
+
|
117
|
+
return out;
|
67
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') {
|
@@ -20,7 +21,7 @@ export default (funcs, globals) => {
|
|
20
21
|
if (optLevel === 0) return;
|
21
22
|
|
22
23
|
const tailCall = process.argv.includes('-tail-call');
|
23
|
-
if (tailCall) log('opt', 'tail call proposal is not widely implemented! (you used -tail-call)');
|
24
|
+
if (tailCall) log('opt', 'warning: tail call proposal is not widely implemented! (you used -tail-call)');
|
24
25
|
|
25
26
|
if (optLevel >= 2 && !process.argv.includes('-opt-no-inline')) {
|
26
27
|
// inline pass (very WIP)
|
@@ -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
|
package/compiler/parse.js
CHANGED
package/compiler/prototype.js
CHANGED
@@ -15,17 +15,18 @@ 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
|
+
const noCCAChecks = process.argv.includes('-funsafe-no-charcodeat-checks');
|
25
27
|
|
26
28
|
this[TYPES._array] = {
|
27
29
|
// lX = local accessor of X ({ get, set }), iX = local index of X, wX = wasm ops of X
|
28
|
-
// todo: out of bounds (>) properly
|
29
30
|
at: (pointer, length, wIndex, iTmp) => [
|
30
31
|
...wIndex,
|
31
32
|
Opcodes.i32_to,
|
@@ -36,7 +37,7 @@ export const PrototypeFuncs = function() {
|
|
36
37
|
[ Opcodes.i32_lt_s ],
|
37
38
|
[ Opcodes.if, Blocktype.void ],
|
38
39
|
[ Opcodes.local_get, iTmp ],
|
39
|
-
...length.
|
40
|
+
...length.getCachedI32(),
|
40
41
|
[ Opcodes.i32_add ],
|
41
42
|
[ Opcodes.local_set, iTmp ],
|
42
43
|
[ Opcodes.end ],
|
@@ -47,7 +48,7 @@ export const PrototypeFuncs = function() {
|
|
47
48
|
[ Opcodes.i32_lt_s ],
|
48
49
|
|
49
50
|
[ Opcodes.local_get, iTmp ],
|
50
|
-
...length.
|
51
|
+
...length.getCachedI32(),
|
51
52
|
[ Opcodes.i32_ge_s ],
|
52
53
|
[ Opcodes.i32_or ],
|
53
54
|
|
@@ -67,7 +68,7 @@ export const PrototypeFuncs = function() {
|
|
67
68
|
// todo: only for 1 argument
|
68
69
|
push: (pointer, length, wNewMember) => [
|
69
70
|
// get memory offset of array at last index (length)
|
70
|
-
...length.
|
71
|
+
...length.getCachedI32(),
|
71
72
|
...number(ValtypeSize[valtype], Valtype.i32),
|
72
73
|
[ Opcodes.i32_mul ],
|
73
74
|
|
@@ -79,17 +80,17 @@ export const PrototypeFuncs = function() {
|
|
79
80
|
|
80
81
|
// bump array length by 1 and return it
|
81
82
|
...length.setI32([
|
82
|
-
...length.
|
83
|
+
...length.getCachedI32(),
|
83
84
|
...number(1, Valtype.i32),
|
84
85
|
[ Opcodes.i32_add ]
|
85
86
|
]),
|
86
87
|
|
87
|
-
...length.get
|
88
|
+
...length.get()
|
88
89
|
],
|
89
90
|
|
90
91
|
pop: (pointer, length) => [
|
91
92
|
// if length == 0, noop
|
92
|
-
...length.
|
93
|
+
...length.getCachedI32(),
|
93
94
|
[ Opcodes.i32_eqz ],
|
94
95
|
[ Opcodes.if, Blocktype.void ],
|
95
96
|
...number(UNDEFINED),
|
@@ -100,13 +101,13 @@ export const PrototypeFuncs = function() {
|
|
100
101
|
|
101
102
|
// decrement length by 1
|
102
103
|
...length.setI32([
|
103
|
-
...length.
|
104
|
+
...length.getCachedI32(),
|
104
105
|
...number(1, Valtype.i32),
|
105
106
|
[ Opcodes.i32_sub ]
|
106
107
|
]),
|
107
108
|
|
108
109
|
// load last element
|
109
|
-
...length.
|
110
|
+
...length.getCachedI32(),
|
110
111
|
...number(ValtypeSize[valtype], Valtype.i32),
|
111
112
|
[ Opcodes.i32_mul ],
|
112
113
|
|
@@ -115,7 +116,7 @@ export const PrototypeFuncs = function() {
|
|
115
116
|
|
116
117
|
shift: (pointer, length) => [
|
117
118
|
// if length == 0, noop
|
118
|
-
...length.
|
119
|
+
...length.getCachedI32(),
|
119
120
|
Opcodes.i32_eqz,
|
120
121
|
[ Opcodes.if, Blocktype.void ],
|
121
122
|
...number(UNDEFINED),
|
@@ -126,7 +127,7 @@ export const PrototypeFuncs = function() {
|
|
126
127
|
|
127
128
|
// decrement length by 1
|
128
129
|
...length.setI32([
|
129
|
-
...length.
|
130
|
+
...length.getCachedI32(),
|
130
131
|
...number(1, Valtype.i32),
|
131
132
|
[ Opcodes.i32_sub ]
|
132
133
|
]),
|
@@ -140,14 +141,68 @@ export const PrototypeFuncs = function() {
|
|
140
141
|
...number(pointer + ValtypeSize.i32 + ValtypeSize[valtype], Valtype.i32), // src = base array index + length size + an index
|
141
142
|
...number(pageSize - ValtypeSize.i32 - ValtypeSize[valtype], Valtype.i32), // size = PageSize - length size - an index
|
142
143
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ]
|
144
|
+
],
|
145
|
+
|
146
|
+
fill: (pointer, length, wElement, iTmp) => [
|
147
|
+
...wElement,
|
148
|
+
[ Opcodes.local_set, iTmp ],
|
149
|
+
|
150
|
+
// use cached length i32 as pointer
|
151
|
+
...length.getCachedI32(),
|
152
|
+
|
153
|
+
// length - 1 for indexes
|
154
|
+
...number(1, Valtype.i32),
|
155
|
+
[ Opcodes.i32_sub ],
|
156
|
+
|
157
|
+
// * sizeof value
|
158
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
159
|
+
[ Opcodes.i32_mul ],
|
160
|
+
|
161
|
+
...length.setCachedI32(),
|
162
|
+
|
163
|
+
...(noUnlikelyChecks ? [] : [
|
164
|
+
...length.getCachedI32(),
|
165
|
+
...number(0, Valtype.i32),
|
166
|
+
[ Opcodes.i32_lt_s ],
|
167
|
+
[ Opcodes.if, Blocktype.void ],
|
168
|
+
...number(pointer),
|
169
|
+
[ Opcodes.br, 1 ],
|
170
|
+
[ Opcodes.end ]
|
171
|
+
]),
|
172
|
+
|
173
|
+
[ Opcodes.loop, Blocktype.void ],
|
174
|
+
|
175
|
+
// set element using pointer
|
176
|
+
...length.getCachedI32(),
|
177
|
+
[ Opcodes.local_get, iTmp ],
|
178
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32) ],
|
179
|
+
|
180
|
+
// pointer - sizeof value
|
181
|
+
...length.getCachedI32(),
|
182
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
183
|
+
[ Opcodes.i32_sub ],
|
184
|
+
|
185
|
+
...length.setCachedI32(),
|
186
|
+
|
187
|
+
// if pointer >= 0, loop
|
188
|
+
...length.getCachedI32(),
|
189
|
+
...number(0, Valtype.i32),
|
190
|
+
[ Opcodes.i32_ge_s ],
|
191
|
+
[ Opcodes.br_if, 0 ],
|
192
|
+
|
193
|
+
[ Opcodes.end ],
|
194
|
+
|
195
|
+
// return this array
|
196
|
+
...number(pointer)
|
143
197
|
]
|
144
198
|
};
|
145
199
|
|
146
200
|
this[TYPES._array].at.local = Valtype.i32;
|
147
201
|
this[TYPES._array].push.noArgRetLength = true;
|
202
|
+
this[TYPES._array].fill.local = valtypeBinary;
|
203
|
+
this[TYPES._array].fill.returnType = TYPES._array;
|
148
204
|
|
149
205
|
this[TYPES.string] = {
|
150
|
-
// todo: out of bounds properly
|
151
206
|
at: (pointer, length, wIndex, iTmp, arrayShell) => {
|
152
207
|
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
153
208
|
|
@@ -157,9 +212,9 @@ export const PrototypeFuncs = function() {
|
|
157
212
|
[ Opcodes.drop ],
|
158
213
|
|
159
214
|
...number(0, Valtype.i32), // base 0 for store later
|
160
|
-
Opcodes.i32_to_u,
|
161
215
|
|
162
216
|
...wIndex,
|
217
|
+
Opcodes.i32_to_u,
|
163
218
|
[ Opcodes.local_tee, iTmp ],
|
164
219
|
|
165
220
|
// if index < 0: access index + array length
|
@@ -167,7 +222,7 @@ export const PrototypeFuncs = function() {
|
|
167
222
|
[ Opcodes.i32_lt_s ],
|
168
223
|
[ Opcodes.if, Blocktype.void ],
|
169
224
|
[ Opcodes.local_get, iTmp ],
|
170
|
-
...length.
|
225
|
+
...length.getCachedI32(),
|
171
226
|
[ Opcodes.i32_add ],
|
172
227
|
[ Opcodes.local_set, iTmp ],
|
173
228
|
[ Opcodes.end ],
|
@@ -178,7 +233,7 @@ export const PrototypeFuncs = function() {
|
|
178
233
|
[ Opcodes.i32_lt_s ],
|
179
234
|
|
180
235
|
[ Opcodes.local_get, iTmp ],
|
181
|
-
...length.
|
236
|
+
...length.getCachedI32(),
|
182
237
|
[ Opcodes.i32_ge_s ],
|
183
238
|
[ Opcodes.i32_or ],
|
184
239
|
|
@@ -234,27 +289,31 @@ export const PrototypeFuncs = function() {
|
|
234
289
|
return [
|
235
290
|
...wIndex,
|
236
291
|
Opcodes.i32_to,
|
237
|
-
[ Opcodes.local_set, iTmp ],
|
238
292
|
|
239
|
-
|
240
|
-
|
293
|
+
...(noCCAChecks ? [] : [
|
294
|
+
[ Opcodes.local_set, iTmp ],
|
295
|
+
|
296
|
+
// index < 0
|
297
|
+
...(noUnlikelyChecks ? [] : [
|
298
|
+
[ Opcodes.local_get, iTmp ],
|
299
|
+
...number(0, Valtype.i32),
|
300
|
+
[ Opcodes.i32_lt_s ],
|
301
|
+
]),
|
302
|
+
|
303
|
+
// index >= length
|
241
304
|
[ Opcodes.local_get, iTmp ],
|
242
|
-
...
|
243
|
-
[ Opcodes.
|
244
|
-
]),
|
305
|
+
...length.getCachedI32(),
|
306
|
+
[ Opcodes.i32_ge_s ],
|
245
307
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
308
|
+
...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
|
309
|
+
[ Opcodes.if, Blocktype.void ],
|
310
|
+
...number(NaN),
|
311
|
+
[ Opcodes.br, 1 ],
|
312
|
+
[ Opcodes.end ],
|
250
313
|
|
251
|
-
|
252
|
-
|
253
|
-
...number(NaN),
|
254
|
-
[ Opcodes.br, 1 ],
|
255
|
-
[ Opcodes.end ],
|
314
|
+
[ Opcodes.local_get, iTmp ],
|
315
|
+
]),
|
256
316
|
|
257
|
-
[ Opcodes.local_get, iTmp ],
|
258
317
|
...number(ValtypeSize.i16, Valtype.i32),
|
259
318
|
[ Opcodes.i32_mul ],
|
260
319
|
|
@@ -265,7 +324,7 @@ export const PrototypeFuncs = function() {
|
|
265
324
|
},
|
266
325
|
};
|
267
326
|
|
268
|
-
this[TYPES.string].at.local =
|
327
|
+
this[TYPES.string].at.local = Valtype.i32;
|
269
328
|
this[TYPES.string].at.returnType = TYPES.string;
|
270
329
|
this[TYPES.string].charAt.returnType = TYPES.string;
|
271
330
|
this[TYPES.string].charCodeAt.local = Valtype.i32;
|
package/compiler/sections.js
CHANGED
@@ -8,11 +8,26 @@ const createSection = (type, data) => [
|
|
8
8
|
...encodeVector(data)
|
9
9
|
];
|
10
10
|
|
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
|
+
|
11
23
|
export default (funcs, globals, tags, pages, 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]}`);
|
@@ -36,7 +51,7 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
36
51
|
// tree shake imports
|
37
52
|
for (const f of funcs) {
|
38
53
|
for (const inst of f.wasm) {
|
39
|
-
if (inst[0] === Opcodes.call && inst[1] < importedFuncs.length) {
|
54
|
+
if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] < importedFuncs.length) {
|
40
55
|
const idx = inst[1];
|
41
56
|
const func = importedFuncs[idx];
|
42
57
|
|
@@ -51,15 +66,17 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
51
66
|
// fix call indexes for non-imports
|
52
67
|
const delta = importedFuncs.length - importFuncs.length;
|
53
68
|
for (const f of funcs) {
|
69
|
+
f.originalIndex = f.index;
|
54
70
|
f.index -= delta;
|
55
71
|
|
56
72
|
for (const inst of f.wasm) {
|
57
|
-
if (inst[0] === Opcodes.call && inst[1] >= importedFuncs.length) {
|
73
|
+
if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
|
58
74
|
inst[1] -= delta;
|
59
75
|
}
|
60
76
|
}
|
61
77
|
}
|
62
78
|
}
|
79
|
+
globalThis.importFuncs = importFuncs;
|
63
80
|
|
64
81
|
if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
|
65
82
|
|
@@ -73,6 +90,14 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
73
90
|
encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
|
74
91
|
);
|
75
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
|
+
|
76
101
|
const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
|
77
102
|
Section.global,
|
78
103
|
encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
|
@@ -145,6 +170,7 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
145
170
|
...typeSection,
|
146
171
|
...importSection,
|
147
172
|
...funcSection,
|
173
|
+
...chSection,
|
148
174
|
...memorySection,
|
149
175
|
...tagSection,
|
150
176
|
...globalSection,
|
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
|
@@ -90,6 +99,15 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
90
99
|
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
91
100
|
}
|
92
101
|
|
102
|
+
case 'function': {
|
103
|
+
// wasm func index, including all imports
|
104
|
+
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
105
|
+
if (!func) return ret;
|
106
|
+
|
107
|
+
// make fake empty func for repl/etc
|
108
|
+
return {[func.name]() {}}[func.name];
|
109
|
+
}
|
110
|
+
|
93
111
|
default: return ret;
|
94
112
|
}
|
95
113
|
} catch (e) {
|
package/cool.exe
ADDED
Binary file
|
package/g
ADDED
Binary file
|
package/g.exe
ADDED
Binary file
|
package/hi.c
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
|
3
|
+
double inline f64_f(double x, double y) {
|
4
|
+
return x - (int)(x / y) * y;
|
5
|
+
}
|
6
|
+
|
7
|
+
double isPrime(double number) {
|
8
|
+
double i;
|
9
|
+
|
10
|
+
if (number < 2e+0) {
|
11
|
+
return 0e+0;
|
12
|
+
}
|
13
|
+
i = 2e+0;
|
14
|
+
while (i < number) {
|
15
|
+
if (f64_f(number, i) == 0e+0) {
|
16
|
+
return 0e+0;
|
17
|
+
}
|
18
|
+
i = i + 1e+0;
|
19
|
+
}
|
20
|
+
return 1e+0;
|
21
|
+
}
|
22
|
+
|
23
|
+
int main() {
|
24
|
+
double sum;
|
25
|
+
double counter;
|
26
|
+
|
27
|
+
sum = 0e+0;
|
28
|
+
counter = 0e+0;
|
29
|
+
while (counter <= 1e+5) {
|
30
|
+
if (isPrime(counter) == 1e+0) {
|
31
|
+
sum = sum + counter;
|
32
|
+
}
|
33
|
+
counter = counter + 1e+0;
|
34
|
+
}
|
35
|
+
printf("%f\n", sum);
|
36
|
+
}
|
37
|
+
|
package/out
ADDED
Binary file
|
package/out.exe
ADDED
Binary file
|
package/package.json
CHANGED