porffor 0.50.20 → 0.50.21
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 +0 -3
- package/compiler/assemble.js +119 -63
- package/compiler/codegen.js +6 -4
- package/compiler/embedding.js +1 -14
- package/compiler/encoding.js +16 -0
- package/compiler/index.js +38 -29
- package/package.json +1 -1
- package/runner/index.js +1 -1
- package/ts.js +198655 -0
package/README.md
CHANGED
@@ -175,9 +175,6 @@ No particular order and no guarantees, just what could happen soon™
|
|
175
175
|
- PGO
|
176
176
|
- Self hosted testing?
|
177
177
|
|
178
|
-
## VSCode extension
|
179
|
-
There is a vscode extension in `vscode-ext` which tweaks JS syntax highlighting to be nicer with porffor features (eg highlighting wasm inside of inline asm).
|
180
|
-
|
181
178
|
## Wasm proposals used
|
182
179
|
Porffor intentionally does not use Wasm proposals which are not commonly implemented yet (eg GC) so it can be used in as many places as possible.
|
183
180
|
|
package/compiler/assemble.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Valtype, FuncType, ExportDesc, Section, Magic, Opcodes, PageSize, Reftype } from './wasmSpec.js';
|
2
|
-
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64, ieee754_binary64_into } from './encoding.js';
|
2
|
+
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64, ieee754_binary64_into, unsignedLEB128_length } from './encoding.js';
|
3
3
|
import { importedFuncs } from './builtins.js';
|
4
4
|
import { log } from './log.js';
|
5
5
|
import './prefs.js';
|
@@ -250,9 +250,7 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
|
|
250
250
|
let codeSection = [];
|
251
251
|
for (let i = 0; i < funcs.length; i++) {
|
252
252
|
const x = funcs[i];
|
253
|
-
// time(x.name);
|
254
253
|
const locals = Object.values(x.locals).sort((a, b) => a.idx - b.idx);
|
255
|
-
// time(' locals gen');
|
256
254
|
|
257
255
|
const paramCount = x.params.length;
|
258
256
|
let localDecl = [], typeCount = 0, lastType, declCount = 0;
|
@@ -269,77 +267,131 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
|
|
269
267
|
lastType = local?.type;
|
270
268
|
}
|
271
269
|
|
272
|
-
// time(' localDecl gen');
|
273
|
-
|
274
|
-
const makeAssembled = Prefs.d;
|
275
270
|
let wasm = [], wasmNonFlat = [];
|
276
|
-
|
277
|
-
let
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
(
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
271
|
+
if (Prefs.d) {
|
272
|
+
for (let i = 0; i < x.wasm.length; i++) {
|
273
|
+
let o = x.wasm[i];
|
274
|
+
|
275
|
+
// encode local/global ops as unsigned leb128 from raw number
|
276
|
+
if (
|
277
|
+
(o[0] >= Opcodes.local_get && o[0] <= Opcodes.global_set) &&
|
278
|
+
o[1] > 127
|
279
|
+
) {
|
280
|
+
const n = o[1];
|
281
|
+
o = [ o[0] ];
|
282
|
+
unsignedLEB128_into(n, o);
|
283
|
+
}
|
288
284
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
285
|
+
// encode f64.const ops as ieee754 from raw number
|
286
|
+
if (o[0] === Opcodes.f64_const) {
|
287
|
+
const n = o[1];
|
288
|
+
o = ieee754_binary64(n);
|
289
|
+
if (o.length === 8) o.unshift(Opcodes.f64_const);
|
290
|
+
}
|
295
291
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
292
|
+
// encode call ops as unsigned leb128 from raw number
|
293
|
+
if ((o[0] === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
|
294
|
+
const n = o[1] - importDelta;
|
295
|
+
o = [ Opcodes.call ];
|
296
|
+
unsignedLEB128_into(n, o);
|
297
|
+
}
|
302
298
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
299
|
+
// encode call indirect ops as types from info
|
300
|
+
if (o[0] === Opcodes.call_indirect) {
|
301
|
+
o = [...o];
|
302
|
+
const params = [];
|
303
|
+
for (let i = 0; i < o[1]; i++) {
|
304
|
+
params.push(valtypeBinary, Valtype.i32);
|
305
|
+
}
|
306
|
+
|
307
|
+
let returns = [ valtypeBinary, Valtype.i32 ];
|
308
|
+
if (o.at(-1) === 'no_type_return') {
|
309
|
+
o.pop();
|
310
|
+
returns = [ valtypeBinary ];
|
311
|
+
}
|
312
|
+
|
313
|
+
o[1] = getType(params, returns);
|
309
314
|
}
|
310
315
|
|
311
|
-
let
|
312
|
-
|
313
|
-
|
314
|
-
|
316
|
+
for (let j = 0; j < o.length; j++) {
|
317
|
+
const x = o[j];
|
318
|
+
if (x == null || !(x <= 0xff)) continue;
|
319
|
+
wasm.push(x);
|
315
320
|
}
|
316
321
|
|
317
|
-
o
|
322
|
+
wasmNonFlat.push(o);
|
318
323
|
}
|
319
324
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
wasm
|
324
|
-
|
325
|
+
x.assembled = { localDecl, wasm, wasmNonFlat };
|
326
|
+
} else {
|
327
|
+
for (let i = 0; i < x.wasm.length; i++) {
|
328
|
+
let o = x.wasm[i];
|
329
|
+
const op = o[0];
|
330
|
+
|
331
|
+
// encode local/global ops as unsigned leb128 from raw number
|
332
|
+
if (
|
333
|
+
(op >= Opcodes.local_get && op <= Opcodes.global_set) &&
|
334
|
+
o[1] > 127
|
335
|
+
) {
|
336
|
+
wasm.push(op);
|
337
|
+
unsignedLEB128_into(o[1], wasm);
|
338
|
+
continue;
|
339
|
+
}
|
325
340
|
|
326
|
-
|
327
|
-
|
328
|
-
|
341
|
+
// encode f64.const ops as ieee754 from raw number
|
342
|
+
if (op === Opcodes.f64_const) {
|
343
|
+
wasm.push(op);
|
344
|
+
ieee754_binary64_into(o[1], wasm);
|
345
|
+
continue;
|
346
|
+
}
|
329
347
|
|
330
|
-
|
331
|
-
|
332
|
-
|
348
|
+
// encode call ops as unsigned leb128 from raw number
|
349
|
+
if ((op === Opcodes.call /* || o[0] === Opcodes.return_call */) && o[1] >= importedFuncs.length) {
|
350
|
+
wasm.push(op);
|
351
|
+
unsignedLEB128_into(o[1] - importDelta, wasm);
|
352
|
+
continue;
|
353
|
+
}
|
354
|
+
|
355
|
+
// encode call indirect ops as types from info
|
356
|
+
if (op === Opcodes.call_indirect) {
|
357
|
+
const params = [];
|
358
|
+
for (let i = 0; i < o[1]; i++) {
|
359
|
+
params.push(valtypeBinary, Valtype.i32);
|
360
|
+
}
|
333
361
|
|
334
|
-
|
335
|
-
|
362
|
+
let returns = [ valtypeBinary, Valtype.i32 ];
|
363
|
+
if (o.at(-1) === 'no_type_return') {
|
364
|
+
returns = [ valtypeBinary ];
|
365
|
+
}
|
366
|
+
|
367
|
+
wasm.push(op, getType(params, returns), o[2]);
|
368
|
+
continue;
|
369
|
+
}
|
336
370
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
371
|
+
for (let j = 0; j < o.length; j++) {
|
372
|
+
const x = o[j];
|
373
|
+
if (x == null || !(x <= 0xff)) continue;
|
374
|
+
wasm.push(x);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
}
|
341
378
|
|
342
|
-
|
379
|
+
if (wasm.length > 100000) {
|
380
|
+
// slow path for handling large arrays which break v8 due to running out of stack size
|
381
|
+
const out = unsignedLEB128(declCount)
|
382
|
+
.concat(localDecl, wasm, Opcodes.end);
|
383
|
+
codeSection = codeSection.concat(unsignedLEB128(out.length), out);
|
384
|
+
} else {
|
385
|
+
codeSection.push(
|
386
|
+
...unsignedLEB128(unsignedLEB128_length(declCount) + localDecl.length + wasm.length + 1),
|
387
|
+
...unsignedLEB128(declCount),
|
388
|
+
...localDecl,
|
389
|
+
...wasm,
|
390
|
+
Opcodes.end
|
391
|
+
);
|
392
|
+
}
|
393
|
+
|
394
|
+
// globalThis.progress?.(`${i}/${funcs.length}`);
|
343
395
|
}
|
344
396
|
|
345
397
|
codeSection.unshift(...unsignedLEB128(funcs.length, codeSection));
|
@@ -368,10 +420,14 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
|
|
368
420
|
dataSection.push(0x01);
|
369
421
|
}
|
370
422
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
423
|
+
if (x.bytes.length > 100000) {
|
424
|
+
codeSection = codeSection.concat(unsignedLEB128(x.bytes.length), x.bytes);
|
425
|
+
} else {
|
426
|
+
dataSection.push(
|
427
|
+
...unsignedLEB128(x.bytes.length),
|
428
|
+
...x.bytes
|
429
|
+
);
|
430
|
+
}
|
375
431
|
}
|
376
432
|
|
377
433
|
dataSection.unshift(...unsignedLEB128(data.length, dataSection));
|
package/compiler/codegen.js
CHANGED
@@ -6457,8 +6457,6 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6457
6457
|
return [ func, out ];
|
6458
6458
|
}
|
6459
6459
|
|
6460
|
-
globalThis.progress?.(null, ' ' + name);
|
6461
|
-
|
6462
6460
|
const params = decl.params ?? [];
|
6463
6461
|
|
6464
6462
|
// TODO: share scope/locals between !!!
|
@@ -6762,15 +6760,18 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6762
6760
|
return [ func, out ];
|
6763
6761
|
};
|
6764
6762
|
|
6763
|
+
let largestBlockBody = 0;
|
6765
6764
|
const generateBlock = (scope, decl) => {
|
6766
6765
|
let out = [];
|
6767
6766
|
|
6768
6767
|
scope.inferTree ??= [];
|
6769
6768
|
scope.inferTree.push(decl);
|
6770
6769
|
|
6771
|
-
let j = 0;
|
6772
|
-
|
6770
|
+
let len = decl.body.length, j = 0;
|
6771
|
+
if (len > largestBlockBody) largestBlockBody = len;
|
6772
|
+
for (let i = 0; i < len; i++) {
|
6773
6773
|
const x = decl.body[i];
|
6774
|
+
if (len >= largestBlockBody) globalThis.progress?.(`${i}/${len}`);
|
6774
6775
|
if (isEmptyNode(x)) continue;
|
6775
6776
|
|
6776
6777
|
if (j++ > 0) out.push([ Opcodes.drop ]);
|
@@ -6946,6 +6947,7 @@ export default program => {
|
|
6946
6947
|
data = [];
|
6947
6948
|
currentFuncIndex = importedFuncs.length;
|
6948
6949
|
typeswitchDepth = 0;
|
6950
|
+
largestBlockBody = 0;
|
6949
6951
|
usedTypes = new Set([ TYPES.empty, TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ]);
|
6950
6952
|
coctc = new Map();
|
6951
6953
|
globalInfer = new Map();
|
package/compiler/embedding.js
CHANGED
@@ -8,17 +8,4 @@ export const number = (n, valtype = valtypeBinary) => {
|
|
8
8
|
signedLEB128_into(n, out);
|
9
9
|
|
10
10
|
return out;
|
11
|
-
};
|
12
|
-
|
13
|
-
export const enforceOneByte = arr => [ arr[0] ?? 0 ];
|
14
|
-
export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
|
15
|
-
export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
|
16
|
-
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 ];
|
17
|
-
|
18
|
-
export const i32x4 = (a, b, c, d) => [ [
|
19
|
-
...Opcodes.v128_const,
|
20
|
-
...enforceFourBytes(signedLEB128(a)),
|
21
|
-
...enforceFourBytes(signedLEB128(b)),
|
22
|
-
...enforceFourBytes(signedLEB128(c)),
|
23
|
-
...enforceFourBytes(signedLEB128(d))
|
24
|
-
] ];
|
11
|
+
};
|
package/compiler/encoding.js
CHANGED
@@ -64,6 +64,22 @@ export const unsignedLEB128 = n => {
|
|
64
64
|
return buffer;
|
65
65
|
};
|
66
66
|
|
67
|
+
export const unsignedLEB128_length = n => {
|
68
|
+
if (n < 0) n = n >>> 0;
|
69
|
+
if (n <= 127) return 1;
|
70
|
+
if (n <= 16383) return 2;
|
71
|
+
if (n <= 2097151) return 3;
|
72
|
+
if (n <= 268435455) return 4;
|
73
|
+
if (n <= 34359738367) return 5;
|
74
|
+
|
75
|
+
let length = 0;
|
76
|
+
do {
|
77
|
+
length++;
|
78
|
+
n >>>= 7;
|
79
|
+
} while (n > 0);
|
80
|
+
return length;
|
81
|
+
};
|
82
|
+
|
67
83
|
export const big_signedLEB128 = n => {
|
68
84
|
// just input for small numbers (for perf as common)
|
69
85
|
if (n >= 0n && n <= 63n) return [ Number(n) ];
|
package/compiler/index.js
CHANGED
@@ -35,7 +35,7 @@ const progressStart = msg => {
|
|
35
35
|
|
36
36
|
const log = (extra, after) => {
|
37
37
|
const pre = extra ? `${extra}` : spinner[spin++ % 4];
|
38
|
-
process.stdout.write(`\r\u001b[90m${' '.repeat(
|
38
|
+
process.stdout.write(`\r\u001b[90m${' '.repeat(120)}\r${' '.repeat(12 - pre.length)}${pre} ${msg}${after ?? ''}\u001b[0m`);
|
39
39
|
};
|
40
40
|
log();
|
41
41
|
|
@@ -48,7 +48,7 @@ const progressDone = (msg, start) => {
|
|
48
48
|
clearInterval(progressInterval);
|
49
49
|
|
50
50
|
const timeStr = (performance.now() - start).toFixed(0);
|
51
|
-
console.log(`\r${' '.repeat(
|
51
|
+
console.log(`\r${' '.repeat(120)}\r\u001b[90m${' '.repeat(10 - timeStr.length)}${timeStr}ms\u001b[0m \u001b[92m${msg}\u001b[0m`);
|
52
52
|
progressLines++;
|
53
53
|
};
|
54
54
|
const progressClear = () => {
|
@@ -66,9 +66,8 @@ export default (code, module = undefined) => {
|
|
66
66
|
let target = Prefs.target ?? 'wasm';
|
67
67
|
if (Prefs.native) target = 'native';
|
68
68
|
|
69
|
-
const
|
70
|
-
|
71
|
-
let outFile = Prefs.o;
|
69
|
+
const outFile = Prefs.o;
|
70
|
+
const logProgress = Prefs.profileCompiler || (Prefs.target && outFile && !Prefs.native && globalThis.file);
|
72
71
|
|
73
72
|
globalThis.valtype = Prefs.valtype ?? 'f64';
|
74
73
|
globalThis.valtypeBinary = Valtype[valtype];
|
@@ -214,6 +213,12 @@ export default (code, module = undefined) => {
|
|
214
213
|
if (target === 'wasm' && outFile) {
|
215
214
|
fs.writeFileSync(outFile, Buffer.from(wasm));
|
216
215
|
|
216
|
+
if (logProgress) {
|
217
|
+
const total = performance.now();
|
218
|
+
progressClear();
|
219
|
+
console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
|
220
|
+
}
|
221
|
+
|
217
222
|
if (process.version) process.exit();
|
218
223
|
}
|
219
224
|
|
@@ -227,6 +232,12 @@ export default (code, module = undefined) => {
|
|
227
232
|
console.log(c);
|
228
233
|
}
|
229
234
|
|
235
|
+
if (logProgress) {
|
236
|
+
const total = performance.now();
|
237
|
+
progressClear();
|
238
|
+
console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
|
239
|
+
}
|
240
|
+
|
230
241
|
if (process.version) process.exit();
|
231
242
|
}
|
232
243
|
|
@@ -260,35 +271,33 @@ export default (code, module = undefined) => {
|
|
260
271
|
|
261
272
|
if (logProgress) progressStart(`compiled C to native (using ${compiler})`, t5);
|
262
273
|
|
263
|
-
if (
|
264
|
-
|
265
|
-
const cleanup = () => {
|
266
|
-
try {
|
267
|
-
fs.unlinkSync(outFile);
|
268
|
-
} catch {}
|
269
|
-
};
|
270
|
-
|
271
|
-
process.on('exit', cleanup);
|
272
|
-
process.on('beforeExit', cleanup);
|
273
|
-
process.on('SIGINT', () => {
|
274
|
-
cleanup();
|
275
|
-
process.exit();
|
276
|
-
});
|
277
|
-
|
278
|
-
const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
|
274
|
+
if (Prefs.native) {
|
275
|
+
const cleanup = () => {
|
279
276
|
try {
|
280
|
-
|
277
|
+
fs.unlinkSync(outFile);
|
281
278
|
} catch {}
|
282
|
-
}
|
279
|
+
};
|
283
280
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
281
|
+
process.on('exit', cleanup);
|
282
|
+
process.on('beforeExit', cleanup);
|
283
|
+
process.on('SIGINT', () => {
|
284
|
+
cleanup();
|
285
|
+
process.exit();
|
286
|
+
});
|
287
|
+
|
288
|
+
const runArgs = process.argv.slice(2).filter(x => !x.startsWith('-'));
|
289
|
+
try {
|
290
|
+
execSync([ outFile, ...runArgs.slice(1) ].join(' '), { stdio: 'inherit' });
|
291
|
+
} catch {}
|
292
|
+
}
|
289
293
|
|
290
|
-
|
294
|
+
if (logProgress) {
|
295
|
+
const total = performance.now();
|
296
|
+
progressClear();
|
297
|
+
console.log(`\u001b[90m[${total.toFixed(0)}ms]\u001b[0m \u001b[32mcompiled ${globalThis.file} \u001b[90m->\u001b[0m \u001b[92m${outFile}\u001b[90m (${(fs.statSync(outFile).size / 1000).toFixed(1)}KB)\u001b[0m`);
|
291
298
|
}
|
299
|
+
|
300
|
+
process.exit();
|
292
301
|
}
|
293
302
|
|
294
303
|
return out;
|
package/package.json
CHANGED