porffor 0.2.0-fbab1de → 0.2.0-fdf0fc5
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/CONTRIBUTING.md +256 -0
- package/LICENSE +20 -20
- package/README.md +147 -89
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +237 -0
- package/compiler/2c.js +317 -72
- package/compiler/{sections.js → assemble.js} +63 -15
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +149 -0
- package/compiler/builtins/base64.ts +76 -0
- package/compiler/builtins/boolean.ts +20 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2070 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/function.ts +7 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +534 -0
- package/compiler/builtins/object.ts +6 -0
- package/compiler/builtins/porffor.d.ts +59 -0
- package/compiler/builtins/set.ts +5 -0
- package/compiler/builtins/string.ts +1080 -0
- package/compiler/builtins.js +450 -270
- package/compiler/{codeGen.js → codegen.js} +1065 -414
- package/compiler/decompile.js +0 -1
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +1526 -0
- package/compiler/index.js +36 -34
- package/compiler/log.js +6 -3
- package/compiler/opt.js +50 -36
- package/compiler/parse.js +33 -23
- package/compiler/precompile.js +128 -0
- package/compiler/prefs.js +27 -0
- package/compiler/prototype.js +27 -42
- package/compiler/types.js +38 -0
- package/compiler/wasmSpec.js +28 -8
- package/compiler/wrap.js +51 -46
- package/package.json +9 -5
- package/porf +4 -0
- package/rhemyn/compile.js +46 -27
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +91 -11
- package/runner/profiler.js +102 -0
- package/runner/repl.js +42 -9
- package/runner/sizes.js +37 -37
- package/compiler/builtins/base64.js +0 -92
- package/r.js +0 -9
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
package/compiler/2c.js
CHANGED
@@ -1,24 +1,106 @@
|
|
1
|
-
import { read_ieee754_binary64, read_signedLEB128 } from './encoding.js';
|
1
|
+
import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from './encoding.js';
|
2
2
|
import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
|
3
3
|
import { operatorOpcode } from './expression.js';
|
4
4
|
import { log } from "./log.js";
|
5
5
|
|
6
6
|
const CValtype = {
|
7
|
-
i8: '
|
8
|
-
i16: '
|
9
|
-
i32: '
|
10
|
-
|
11
|
-
i64: '
|
12
|
-
|
7
|
+
i8: 'i8',
|
8
|
+
i16: 'i16',
|
9
|
+
i32: 'i32',
|
10
|
+
u32: 'u32',
|
11
|
+
i64: 'i64',
|
12
|
+
u64: 'u64',
|
13
13
|
|
14
|
-
f32: '
|
15
|
-
f64: '
|
14
|
+
f32: 'f32',
|
15
|
+
f64: 'f64',
|
16
16
|
|
17
17
|
undefined: 'void'
|
18
18
|
};
|
19
19
|
|
20
|
+
const alwaysPreface = `typedef uint8_t i8;
|
21
|
+
typedef uint16_t i16;
|
22
|
+
typedef int32_t i32;
|
23
|
+
typedef uint32_t u32;
|
24
|
+
typedef int64_t i64;
|
25
|
+
typedef uint64_t u64;
|
26
|
+
typedef float f32;
|
27
|
+
typedef double f64;
|
28
|
+
|
29
|
+
f64 NAN = 0e+0/0e+0;
|
30
|
+
|
31
|
+
struct ReturnValue {
|
32
|
+
${CValtype.f64} value;
|
33
|
+
${CValtype.i32} type;
|
34
|
+
};\n\n`;
|
35
|
+
|
36
|
+
// todo: is memcpy/etc safe with host endianness?
|
37
|
+
|
38
|
+
// all:
|
39
|
+
// immediates: ['align', 'offset']
|
40
|
+
const CMemFuncs = {
|
41
|
+
[Opcodes.i32_store]: {
|
42
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
43
|
+
args: ['pointer', 'value'],
|
44
|
+
argTypes: [CValtype.i32, CValtype.i32],
|
45
|
+
returns: false
|
46
|
+
},
|
47
|
+
[Opcodes.i32_store16]: {
|
48
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
49
|
+
args: ['pointer', 'value'],
|
50
|
+
argTypes: [CValtype.i32, CValtype.i16],
|
51
|
+
returns: false
|
52
|
+
},
|
53
|
+
[Opcodes.i32_store8]: {
|
54
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
55
|
+
args: ['pointer', 'value'],
|
56
|
+
argTypes: [CValtype.i32, CValtype.i8],
|
57
|
+
returns: false
|
58
|
+
},
|
59
|
+
|
60
|
+
[Opcodes.i32_load]: {
|
61
|
+
c: `${CValtype.i32} out;
|
62
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
63
|
+
return out;`,
|
64
|
+
args: ['pointer'],
|
65
|
+
argTypes: [CValtype.i32],
|
66
|
+
returns: CValtype.i32
|
67
|
+
},
|
68
|
+
[Opcodes.i32_load16_u]: {
|
69
|
+
c: `${CValtype.i16} out;
|
70
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
71
|
+
return out;`,
|
72
|
+
args: ['pointer'],
|
73
|
+
argTypes: [CValtype.i32],
|
74
|
+
returns: CValtype.i32
|
75
|
+
},
|
76
|
+
[Opcodes.i32_load8_u]: {
|
77
|
+
c: `${CValtype.i8} out;
|
78
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
79
|
+
return out;`,
|
80
|
+
args: ['pointer'],
|
81
|
+
argTypes: [CValtype.i32],
|
82
|
+
returns: CValtype.i32
|
83
|
+
},
|
84
|
+
|
85
|
+
[Opcodes.f64_store]: {
|
86
|
+
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
87
|
+
args: ['pointer', 'value'],
|
88
|
+
argTypes: [CValtype.i32, CValtype.f64],
|
89
|
+
returns: false
|
90
|
+
},
|
91
|
+
[Opcodes.f64_load]: {
|
92
|
+
c: `${CValtype.f64} out;
|
93
|
+
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
94
|
+
return out;`,
|
95
|
+
args: ['pointer'],
|
96
|
+
argTypes: [CValtype.i32],
|
97
|
+
returns: CValtype.f64
|
98
|
+
},
|
99
|
+
};
|
100
|
+
|
20
101
|
const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
|
21
102
|
const invOpcodes = inv(Opcodes);
|
103
|
+
const invValtype = inv(Valtype);
|
22
104
|
|
23
105
|
for (const x in CValtype) {
|
24
106
|
if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
|
@@ -32,15 +114,22 @@ const todo = msg => {
|
|
32
114
|
}
|
33
115
|
}
|
34
116
|
|
35
|
-
throw new TodoError(
|
117
|
+
throw new TodoError(msg);
|
36
118
|
};
|
37
119
|
|
38
120
|
const removeBrackets = str => {
|
39
|
-
|
121
|
+
// return str;
|
122
|
+
// if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
|
123
|
+
|
124
|
+
for (const x in CValtype) {
|
125
|
+
const p = `(${x})`;
|
126
|
+
if (str.startsWith(p)) return p + removeBrackets(str.slice(p.length));
|
127
|
+
}
|
128
|
+
|
40
129
|
return str.startsWith('(') && str.endsWith(')') ? str.slice(1, -1) : str;
|
41
130
|
};
|
42
131
|
|
43
|
-
export default ({ funcs, globals, tags, exceptions, pages }) => {
|
132
|
+
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
44
133
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
45
134
|
for (const k in x) {
|
46
135
|
acc[x[k]] = k;
|
@@ -56,12 +145,11 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
56
145
|
}
|
57
146
|
|
58
147
|
const includes = new Map(), unixIncludes = new Map(), winIncludes = new Map();
|
148
|
+
const prepend = new Map(), prependMain = new Map();
|
59
149
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
${CValtype.i32} type;
|
64
|
-
};\n\n`;
|
150
|
+
includes.set('stdint.h', true);
|
151
|
+
|
152
|
+
let out = ``;
|
65
153
|
|
66
154
|
for (const x in globals) {
|
67
155
|
const g = globals[x];
|
@@ -70,6 +158,15 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
70
158
|
out += ';\n';
|
71
159
|
}
|
72
160
|
|
161
|
+
if (pages.size > 0) {
|
162
|
+
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
163
|
+
includes.set('string.h', true);
|
164
|
+
}
|
165
|
+
|
166
|
+
if (data.length > 0) {
|
167
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
|
168
|
+
}
|
169
|
+
|
73
170
|
// for (const [ x, p ] of pages) {
|
74
171
|
// out += `${CValtype[p.type]} ${x.replace(': ', '_').replace(/[^0-9a-zA-Z_]/g, '')}[100]`;
|
75
172
|
// out += ';\n';
|
@@ -78,7 +175,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
78
175
|
if (out) out += '\n';
|
79
176
|
|
80
177
|
let depth = 1;
|
81
|
-
|
178
|
+
let brDepth = 0;
|
179
|
+
const line = (str, semi = true) => out += `${' '.repeat(depth * 2 + brDepth * 2)}${str}${semi ? ';' : ''}\n`;
|
82
180
|
const lines = lines => {
|
83
181
|
for (const x of lines) {
|
84
182
|
out += `${' '.repeat(depth * 2)}${x}\n`;
|
@@ -111,6 +209,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
111
209
|
return tmp;
|
112
210
|
};
|
113
211
|
|
212
|
+
let brId = 0;
|
213
|
+
|
114
214
|
for (const f of funcs) {
|
115
215
|
depth = 1;
|
116
216
|
|
@@ -124,7 +224,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
124
224
|
const returns = f.returns.length > 0;
|
125
225
|
|
126
226
|
const shouldInline = f.internal;
|
127
|
-
out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ?
|
227
|
+
out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
228
|
+
|
229
|
+
if (f.name === 'main') {
|
230
|
+
out += [...prependMain.values()].join('\n');
|
231
|
+
if (prependMain.size > 0) out += '\n\n';
|
232
|
+
}
|
128
233
|
|
129
234
|
const localKeys = Object.keys(f.locals).sort((a, b) => f.locals[a].idx - f.locals[b].idx).slice(f.params.length).sort((a, b) => f.locals[a].idx - f.locals[b].idx);
|
130
235
|
for (const x of localKeys) {
|
@@ -134,11 +239,58 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
134
239
|
|
135
240
|
if (localKeys.length !== 0) out += '\n';
|
136
241
|
|
242
|
+
const rets = [];
|
243
|
+
const runOnEnd = [];
|
244
|
+
|
137
245
|
let vals = [];
|
138
|
-
const endNeedsCurly = []
|
139
|
-
|
246
|
+
const endNeedsCurly = [];
|
247
|
+
const brs = [];
|
248
|
+
let lastCond = false;
|
249
|
+
|
250
|
+
// let brDepth = 0;
|
251
|
+
|
252
|
+
const blockStart = (i, loop) => {
|
253
|
+
// reset "stack"
|
254
|
+
// vals = [];
|
255
|
+
|
256
|
+
rets.push(i[1]);
|
257
|
+
|
258
|
+
const br = brId++;
|
259
|
+
brs.push(br);
|
260
|
+
if (loop) {
|
261
|
+
line(`j${br}:;`, false);
|
262
|
+
runOnEnd.push(null);
|
263
|
+
} else {
|
264
|
+
runOnEnd.push(() => line(`j${br}:;`, false));
|
265
|
+
}
|
266
|
+
|
267
|
+
if (i[1] !== Blocktype.void) line(`${CValtype[i[1]]} _r${br}`);
|
268
|
+
|
269
|
+
brDepth++;
|
270
|
+
};
|
271
|
+
|
272
|
+
const highlight = i => {
|
273
|
+
const surrounding = 6;
|
274
|
+
|
275
|
+
const decomp = decompile(f.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, f.locals, f.params, f.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
|
276
|
+
|
277
|
+
const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
|
278
|
+
let longest = 0;
|
279
|
+
for (let j = 0; j < decomp.length; j++) {
|
280
|
+
longest = Math.max(longest, noAnsi(decomp[j]).length);
|
281
|
+
}
|
282
|
+
|
283
|
+
const middle = Math.floor(decomp.length / 2);
|
284
|
+
decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
|
285
|
+
|
286
|
+
console.log('\x1B[90m...\x1B[0m');
|
287
|
+
console.log(decomp.join('\n'));
|
288
|
+
console.log('\x1B[90m...\x1B[0m\n');
|
289
|
+
};
|
290
|
+
|
140
291
|
for (let _ = 0; _ < f.wasm.length; _++) {
|
141
292
|
const i = f.wasm[_];
|
293
|
+
if (!i || !i[0]) continue;
|
142
294
|
|
143
295
|
if (invOperatorOpcode[i[0]]) {
|
144
296
|
const b = vals.pop();
|
@@ -160,12 +312,12 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
160
312
|
switch (i[1]) {
|
161
313
|
// i32_trunc_sat_f64_s
|
162
314
|
case 0x02:
|
163
|
-
vals.push(`(${CValtype.i32})${vals.pop()}`);
|
315
|
+
vals.push(`(${CValtype.i32})(${vals.pop()})`);
|
164
316
|
break;
|
165
317
|
|
166
318
|
// i32_trunc_sat_f64_u
|
167
319
|
case 0x03:
|
168
|
-
vals.push(`(${CValtype.
|
320
|
+
vals.push(`(${CValtype.u32})(${vals.pop()})`);
|
169
321
|
break;
|
170
322
|
}
|
171
323
|
|
@@ -176,12 +328,19 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
176
328
|
switch (i[0]) {
|
177
329
|
case Opcodes.i32_const:
|
178
330
|
case Opcodes.i64_const:
|
179
|
-
vals.push(read_signedLEB128(i.slice(1)).toString());
|
331
|
+
// vals.push(read_signedLEB128(i.slice(1)).toString());
|
332
|
+
vals.push(new String(read_signedLEB128(i.slice(1)).toString()));
|
333
|
+
vals.at(-1).offset = _;
|
180
334
|
break;
|
181
335
|
|
182
|
-
case Opcodes.f64_const:
|
183
|
-
|
336
|
+
case Opcodes.f64_const: {
|
337
|
+
// const val = read_ieee754_binary64(i.slice(1)).toExponential();
|
338
|
+
const val = new String(read_ieee754_binary64(i.slice(1)).toExponential());
|
339
|
+
// vals.push(val == 'NaN' ? 'NAN' : val);
|
340
|
+
vals.push(val == 'NaN' ? new String('NAN') : val);
|
341
|
+
vals.at(-1).offset = _;
|
184
342
|
break;
|
343
|
+
}
|
185
344
|
|
186
345
|
case Opcodes.local_get:
|
187
346
|
vals.push(`${invLocals[i[1]]}`);
|
@@ -192,9 +351,9 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
192
351
|
break;
|
193
352
|
|
194
353
|
case Opcodes.local_tee:
|
195
|
-
|
196
|
-
|
197
|
-
vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
|
354
|
+
line(`${invLocals[i[1]]} = ${removeBrackets(vals.pop())}`);
|
355
|
+
vals.push(`${invLocals[i[1]]}`);
|
356
|
+
// vals.push(`((${invLocals[i[1]]} = ${vals.pop()}))`);
|
198
357
|
break;
|
199
358
|
|
200
359
|
case Opcodes.global_get:
|
@@ -207,78 +366,102 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
207
366
|
|
208
367
|
case Opcodes.f64_trunc:
|
209
368
|
// vals.push(`trunc(${vals.pop()})`);
|
210
|
-
vals.push(`(
|
369
|
+
vals.push(`(${CValtype.i32})(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
211
370
|
break;
|
212
371
|
|
213
372
|
case Opcodes.f64_convert_i32_u:
|
214
373
|
case Opcodes.f64_convert_i32_s:
|
215
374
|
case Opcodes.f64_convert_i64_u:
|
216
375
|
case Opcodes.f64_convert_i64_s:
|
217
|
-
// int to
|
218
|
-
vals.push(`(
|
376
|
+
// int to f64
|
377
|
+
vals.push(`(${CValtype.f64})(${removeBrackets(vals.pop())})`);
|
219
378
|
break;
|
220
379
|
|
380
|
+
case Opcodes.i32_eqz:
|
381
|
+
if (lastCond) {
|
382
|
+
vals.push(`!(${removeBrackets(vals.pop())})`);
|
383
|
+
} else {
|
384
|
+
let cond = '(' + removeBrackets(vals.pop());
|
385
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `${cond.slice(`(${CValtype.i32})`.length)}) == 0e+0`;
|
386
|
+
else cond += ') == 0';
|
387
|
+
vals.push(cond);
|
388
|
+
}
|
389
|
+
lastCond = true;
|
390
|
+
continue;
|
391
|
+
|
221
392
|
case Opcodes.return:
|
222
393
|
// line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
223
394
|
line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
|
224
395
|
break;
|
225
396
|
|
226
|
-
case Opcodes.if:
|
397
|
+
case Opcodes.if: {
|
227
398
|
let cond = removeBrackets(vals.pop());
|
228
399
|
if (!lastCond) {
|
229
|
-
if (cond.startsWith(
|
230
|
-
else cond
|
231
|
-
}
|
232
|
-
|
233
|
-
ifTernary = i[1] !== Blocktype.void;
|
234
|
-
if (ifTernary) {
|
235
|
-
ifTernary = cond;
|
236
|
-
break;
|
400
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
|
401
|
+
else cond = `(${cond}) != 0`;
|
237
402
|
}
|
238
403
|
|
239
|
-
if
|
240
|
-
|
241
|
-
line(`while (${cond}) {`, false);
|
242
|
-
|
243
|
-
depth++;
|
244
|
-
endNeedsCurly.push(true);
|
245
|
-
ignoreEnd.push(false, true);
|
246
|
-
break;
|
247
|
-
}
|
404
|
+
line(`// if ${invValtype[i[1]] ?? ''}`, false);
|
405
|
+
blockStart(i, false);
|
248
406
|
|
249
407
|
line(`if (${cond}) {`, false);
|
250
408
|
|
251
409
|
depth++;
|
252
410
|
endNeedsCurly.push(true);
|
253
|
-
ignoreEnd.push(false);
|
254
411
|
break;
|
412
|
+
}
|
255
413
|
|
256
|
-
case Opcodes.else:
|
257
|
-
|
414
|
+
case Opcodes.else: {
|
415
|
+
const br = brs.at(-1);
|
416
|
+
const ret = rets.at(-1);
|
417
|
+
if (ret && ret !== Blocktype.void) {
|
418
|
+
// console.log(vals, ret);
|
419
|
+
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
420
|
+
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
421
|
+
// vals.push(`_r${br}`);
|
422
|
+
}
|
258
423
|
|
259
424
|
depth--;
|
260
425
|
line(`} else {`, false);
|
261
426
|
depth++;
|
427
|
+
|
428
|
+
// reset "stack"
|
429
|
+
// vals = [];
|
262
430
|
break;
|
431
|
+
}
|
263
432
|
|
264
|
-
case Opcodes.loop:
|
265
|
-
|
266
|
-
|
433
|
+
case Opcodes.loop: {
|
434
|
+
line(`// loop ${invValtype[i[1]] ?? ''}`, false);
|
435
|
+
blockStart(i, true);
|
436
|
+
endNeedsCurly.push(false);
|
267
437
|
break;
|
438
|
+
}
|
268
439
|
|
269
|
-
case Opcodes.end:
|
270
|
-
|
440
|
+
case Opcodes.end: {
|
441
|
+
const br = brs.pop();
|
442
|
+
const ret = rets.pop();
|
443
|
+
if (ret && ret !== Blocktype.void) {
|
444
|
+
// console.log(vals, ret);
|
445
|
+
// console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
|
446
|
+
if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
|
447
|
+
vals.push(`_r${br}`);
|
448
|
+
}
|
271
449
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
break;
|
450
|
+
const enc = endNeedsCurly.pop() === true;
|
451
|
+
if (enc) {
|
452
|
+
depth--;
|
453
|
+
line('}', false);
|
277
454
|
}
|
278
455
|
|
279
|
-
|
280
|
-
|
456
|
+
brDepth--;
|
457
|
+
|
458
|
+
line(`// end`, false);
|
459
|
+
|
460
|
+
const roe = runOnEnd.pop();
|
461
|
+
if (roe) roe();
|
462
|
+
|
281
463
|
break;
|
464
|
+
}
|
282
465
|
|
283
466
|
case Opcodes.call:
|
284
467
|
let func = funcs.find(x => x.index === i[1]);
|
@@ -286,7 +469,8 @@ export default ({ funcs, globals, tags, exceptions, pages }) => {
|
|
286
469
|
const importFunc = importFuncs[i[1]];
|
287
470
|
switch (importFunc.name) {
|
288
471
|
case 'print':
|
289
|
-
line(`printf("%f\\n", ${vals.pop()})`);
|
472
|
+
// line(`printf("%f\\n", ${vals.pop()})`);
|
473
|
+
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
290
474
|
includes.set('stdio.h', true);
|
291
475
|
break;
|
292
476
|
case 'printChar':
|
@@ -349,13 +533,74 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
349
533
|
vals.pop();
|
350
534
|
break;
|
351
535
|
|
352
|
-
case Opcodes.
|
353
|
-
|
354
|
-
|
355
|
-
|
536
|
+
case Opcodes.block:
|
537
|
+
line(`// block ${invValtype[i[1]] ?? ''}`, false);
|
538
|
+
blockStart(i, false);
|
539
|
+
endNeedsCurly.push(false);
|
540
|
+
break;
|
541
|
+
|
542
|
+
case Opcodes.br: {
|
543
|
+
const ret = rets[brDepth - i[1] - 1];
|
544
|
+
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
545
|
+
if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
|
546
|
+
line(`goto j${brs[brDepth - i[1] - 1]}`);
|
547
|
+
|
548
|
+
// // reset "stack"
|
549
|
+
// vals = [];
|
550
|
+
break;
|
551
|
+
}
|
552
|
+
|
553
|
+
case Opcodes.br_if: {
|
554
|
+
const ret = rets[brDepth - i[1] - 1];
|
555
|
+
// console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
|
556
|
+
|
557
|
+
let cond = removeBrackets(vals.pop());
|
558
|
+
if (!lastCond) {
|
559
|
+
if (cond.startsWith(`(${CValtype.i32})`)) cond = `(${cond.slice(`(${CValtype.i32})`.length)}) != 0e+0`;
|
560
|
+
else cond = `(${cond}) != 0`;
|
561
|
+
}
|
562
|
+
|
563
|
+
line(`if (${cond}) {`, false);
|
564
|
+
depth++;
|
565
|
+
if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.at(-1))}`);
|
566
|
+
line(`goto j${brs[brDepth - i[1] - 1]}`);
|
567
|
+
depth--;
|
568
|
+
line(`}`, false);
|
569
|
+
|
356
570
|
break;
|
571
|
+
}
|
572
|
+
|
573
|
+
case Opcodes.throw: {
|
574
|
+
const id = vals.pop();
|
575
|
+
|
576
|
+
line(`printf("Uncaught ${exceptions[id].constructor}: ${exceptions[id].message}\\n")`);
|
577
|
+
line(`exit(1)`);
|
578
|
+
|
579
|
+
includes.set('stdlib.h', true);
|
580
|
+
|
581
|
+
break;
|
582
|
+
}
|
357
583
|
|
358
584
|
default:
|
585
|
+
if (CMemFuncs[i[0]]) {
|
586
|
+
const name = invOpcodes[i[0]];
|
587
|
+
const func = CMemFuncs[i[0]];
|
588
|
+
if (!prepend.has(name)) {
|
589
|
+
prepend.set(name, `${func.returns || 'void'} ${name}(${CValtype.i32} align, ${CValtype.i32} offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
|
590
|
+
// generate func c and prepend
|
591
|
+
}
|
592
|
+
|
593
|
+
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
594
|
+
|
595
|
+
let args = [];
|
596
|
+
for (let j = 0; j < func.args.length; j++) args.unshift(removeBrackets(vals.pop()));
|
597
|
+
|
598
|
+
if (func.returns !== false) {
|
599
|
+
vals.push(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
|
600
|
+
} else line(`${name}(${immediates[0]}, ${immediates[1]}, ${args.join(', ')})`);
|
601
|
+
break;
|
602
|
+
}
|
603
|
+
|
359
604
|
log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
|
360
605
|
// todo(`unimplemented op: ${invOpcodes[i[0]]}`);
|
361
606
|
}
|
@@ -379,7 +624,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
379
624
|
|
380
625
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
381
626
|
|
382
|
-
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + out;
|
627
|
+
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
383
628
|
|
384
|
-
return out;
|
629
|
+
return out.trim();
|
385
630
|
};
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
|
2
|
-
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
|
3
|
-
import { number } from './embedding.js';
|
2
|
+
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, ieee754_binary64, unsignedLEB128_into, signedLEB128_into, ieee754_binary64_into } from './encoding.js';
|
3
|
+
// import { number } from './embedding.js';
|
4
4
|
import { importedFuncs } from './builtins.js';
|
5
5
|
import { log } from "./log.js";
|
6
|
+
import Prefs from './prefs.js';
|
6
7
|
|
7
8
|
const createSection = (type, data) => [
|
8
9
|
type,
|
@@ -26,12 +27,12 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
26
27
|
|
27
28
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
28
29
|
|
29
|
-
const compileHints =
|
30
|
-
if (compileHints) log.warning('
|
30
|
+
const compileHints = Prefs.compileHints;
|
31
|
+
if (compileHints) log.warning('assemble', 'compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
|
31
32
|
|
32
33
|
const getType = (params, returns) => {
|
33
34
|
const hash = `${params.join(',')}_${returns.join(',')}`;
|
34
|
-
if (optLog) log('
|
35
|
+
if (Prefs.optLog) log('assemble', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
|
35
36
|
if (optLevel >= 1 && typeCache[hash] !== undefined) return typeCache[hash];
|
36
37
|
|
37
38
|
const type = [ FuncType, ...encodeVector(params), ...encodeVector(returns) ];
|
@@ -44,7 +45,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
44
45
|
|
45
46
|
let importFuncs = [];
|
46
47
|
|
47
|
-
if (optLevel < 1) {
|
48
|
+
if (optLevel < 1 || !Prefs.treeshakeWasmImports) {
|
48
49
|
importFuncs = importedFuncs;
|
49
50
|
} else {
|
50
51
|
let imports = new Map();
|
@@ -79,11 +80,11 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
79
80
|
}
|
80
81
|
globalThis.importFuncs = importFuncs;
|
81
82
|
|
82
|
-
if (optLog) log('
|
83
|
+
if (Prefs.optLog) log('assemble', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
|
83
84
|
|
84
85
|
const importSection = importFuncs.length === 0 ? [] : createSection(
|
85
86
|
Section.import,
|
86
|
-
encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(new Array(x.params).fill(valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
|
87
|
+
encodeVector(importFuncs.map(x => [ 0, ...encodeString(x.import), ExportDesc.func, getType(new Array(x.params).fill(x.name.startsWith('profile') ? Valtype.i32 : valtypeBinary), new Array(x.returns).fill(valtypeBinary)) ]))
|
87
88
|
);
|
88
89
|
|
89
90
|
const funcSection = createSection(
|
@@ -95,18 +96,65 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
95
96
|
// https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
|
96
97
|
const chSection = !compileHints ? [] : customSection(
|
97
98
|
'compilationHints',
|
98
|
-
// for now just do everything as
|
99
|
+
// for now just do everything as optimize eager
|
99
100
|
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
100
101
|
);
|
101
102
|
|
102
|
-
const
|
103
|
-
|
104
|
-
|
105
|
-
);
|
103
|
+
// const t0 = performance.now();
|
104
|
+
|
105
|
+
// specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection(...)
|
106
|
+
const globalsValues = Object.values(globals);
|
107
|
+
|
108
|
+
let globalSection = [];
|
109
|
+
if (globalsValues.length > 0) {
|
110
|
+
let data = unsignedLEB128(globalsValues.length);
|
111
|
+
for (let i = 0; i < globalsValues.length; i++) {
|
112
|
+
const global = globalsValues[i];
|
113
|
+
|
114
|
+
switch (global.type) {
|
115
|
+
case Valtype.i32:
|
116
|
+
if (i > 0) data.push(Opcodes.end, Valtype.i32, 0x01, Opcodes.i32_const);
|
117
|
+
else data.push(Valtype.i32, 0x01, Opcodes.i32_const);
|
118
|
+
|
119
|
+
signedLEB128_into(global.init ?? 0, data);
|
120
|
+
break;
|
121
|
+
|
122
|
+
case Valtype.i64:
|
123
|
+
if (i > 0) data.push(Opcodes.end, Valtype.i64, 0x01, Opcodes.i64_const);
|
124
|
+
else data.push(Valtype.i64, 0x01, Opcodes.i64_const);
|
125
|
+
|
126
|
+
signedLEB128_into(global.init ?? 0, data);
|
127
|
+
break;
|
128
|
+
|
129
|
+
case Valtype.f64:
|
130
|
+
if (i > 0) data.push(Opcodes.end, Valtype.f64, 0x01, Opcodes.f64_const);
|
131
|
+
else data.push(Valtype.f64, 0x01, Opcodes.f64_const);
|
132
|
+
|
133
|
+
ieee754_binary64_into(global.init ?? 0, data);
|
134
|
+
break;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
data.push(Opcodes.end);
|
139
|
+
|
140
|
+
globalSection.push(Section.global);
|
141
|
+
|
142
|
+
unsignedLEB128_into(data.length, globalSection);
|
143
|
+
globalSection = globalSection.concat(data);
|
144
|
+
}
|
145
|
+
|
146
|
+
// if (Prefs.profileCompiler) {
|
147
|
+
// const log = console.log;
|
148
|
+
// console.log = function () {
|
149
|
+
// log.apply(this, arguments);
|
150
|
+
// console.log = log;
|
151
|
+
// console.log(` a. assembled global section in ${(performance.now() - t0).toFixed(2)}ms\n`);
|
152
|
+
// };
|
153
|
+
// }
|
106
154
|
|
107
155
|
const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
|
108
156
|
|
109
|
-
if (
|
157
|
+
if (Prefs.alwaysMemory && pages.size === 0) pages.set('--always-memory', 0);
|
110
158
|
if (optLevel === 0) pages.set('O0 precaution', 0);
|
111
159
|
|
112
160
|
const usesMemory = pages.size > 0;
|
@@ -169,7 +217,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
169
217
|
unsignedLEB128(data.length)
|
170
218
|
);
|
171
219
|
|
172
|
-
if (
|
220
|
+
if (Prefs.sections) console.log({
|
173
221
|
typeSection: typeSection.map(x => x.toString(16)),
|
174
222
|
importSection: importSection.map(x => x.toString(16)),
|
175
223
|
funcSection: funcSection.map(x => x.toString(16)),
|