porffor 0.57.7 → 0.57.8
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/compiler/assemble.js +7 -1
- package/compiler/builtins.js +37 -54
- package/compiler/index.js +3 -0
- package/compiler/pgo.js +13 -13
- package/compiler/wrap.js +55 -34
- package/package.json +1 -1
- package/runtime/debug.js +61 -61
- package/runtime/flamegraph.js +15 -17
- package/runtime/hotlines.js +16 -19
- package/runtime/index.js +1 -1
package/compiler/assemble.js
CHANGED
@@ -95,7 +95,13 @@ export default (funcs, globals, tags, pages, data, noTreeshake = false) => {
|
|
95
95
|
|
96
96
|
const importSection = importFuncs.length === 0 ? [] : createSection(
|
97
97
|
Section.import,
|
98
|
-
encodeVector(importFuncs.map(x =>
|
98
|
+
encodeVector(importFuncs.map(x => {
|
99
|
+
return [
|
100
|
+
0, 1, x.import.charCodeAt(0),
|
101
|
+
ExportDesc.func,
|
102
|
+
getType(x.params, x.returns)
|
103
|
+
];
|
104
|
+
}))
|
99
105
|
);
|
100
106
|
time('import section');
|
101
107
|
|
package/compiler/builtins.js
CHANGED
@@ -5,61 +5,44 @@ import { TYPES, TYPE_NAMES } from './types.js';
|
|
5
5
|
import { number, unsignedLEB128 } from './encoding.js';
|
6
6
|
import './prefs.js';
|
7
7
|
|
8
|
-
export const importedFuncs =
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
name: 'profile1',
|
35
|
-
import: 'y',
|
36
|
-
params: [ Valtype.i32 ],
|
37
|
-
returns: 0
|
38
|
-
},
|
39
|
-
{
|
40
|
-
name: 'profile2',
|
41
|
-
import: 'z',
|
42
|
-
params: [ Valtype.i32 ],
|
43
|
-
returns: 0
|
44
|
-
},
|
45
|
-
{
|
46
|
-
name: '__Porffor_readArgv',
|
47
|
-
import: 'w',
|
48
|
-
params: 2,
|
49
|
-
returns: 1
|
50
|
-
},
|
51
|
-
{
|
52
|
-
name: '__Porffor_readFile',
|
53
|
-
import: 'q',
|
54
|
-
params: 2,
|
55
|
-
returns: 1
|
56
|
-
}
|
57
|
-
];
|
8
|
+
export const importedFuncs = {};
|
9
|
+
Object.defineProperty(importedFuncs, 'length', { configurable: true, writable: true, value: 0 });
|
10
|
+
|
11
|
+
export const createImport = (name, params, returns, js = null, c = null) => {
|
12
|
+
if (name in importedFuncs) return false;
|
13
|
+
|
14
|
+
const call = importedFuncs.length;
|
15
|
+
const ident = String.fromCharCode(97 + importedFuncs.length);
|
16
|
+
let obj;
|
17
|
+
const get = () => {
|
18
|
+
if (obj) return obj;
|
19
|
+
|
20
|
+
if (typeof params === 'function') params = params();
|
21
|
+
if (typeof returns === 'function') returns = returns();
|
22
|
+
if (typeof params === 'number') params = new Array(params).fill(valtypeBinary);
|
23
|
+
if (typeof returns === 'number') returns = new Array(returns).fill(valtypeBinary);
|
24
|
+
|
25
|
+
obj = new Number(call);
|
26
|
+
obj.name = name;
|
27
|
+
obj.import = ident;
|
28
|
+
obj.params = params;
|
29
|
+
obj.returns = returns;
|
30
|
+
obj.js = js;
|
31
|
+
obj.c = c;
|
32
|
+
return obj;
|
33
|
+
};
|
58
34
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
35
|
+
Object.defineProperty(importedFuncs, name, {
|
36
|
+
get,
|
37
|
+
configurable: true,
|
38
|
+
enumerable: true
|
39
|
+
});
|
40
|
+
Object.defineProperty(importedFuncs, call, {
|
41
|
+
get,
|
42
|
+
configurable: true
|
43
|
+
});
|
44
|
+
importedFuncs.length = call + 1;
|
45
|
+
};
|
63
46
|
|
64
47
|
export const UNDEFINED = 0;
|
65
48
|
export const NULL = 0;
|
package/compiler/index.js
CHANGED
@@ -62,6 +62,9 @@ const progressClear = () => {
|
|
62
62
|
export default (code, module = undefined) => {
|
63
63
|
if (module !== undefined) Prefs.module = module;
|
64
64
|
|
65
|
+
globalThis.valtype = Prefs.valtype ?? 'f64';
|
66
|
+
globalThis.valtypeBinary = Valtype[valtype];
|
67
|
+
|
65
68
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
66
69
|
|
67
70
|
let target = Prefs.target ?? 'wasm';
|
package/compiler/pgo.js
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
import { Opcodes, Valtype } from './wasmSpec.js';
|
2
2
|
import { number } from './encoding.js';
|
3
|
-
import { importedFuncs } from './builtins.js';
|
3
|
+
import { createImport, importedFuncs } from './builtins.js';
|
4
4
|
import assemble from './assemble.js';
|
5
5
|
import wrap, { writeByteStr } from './wrap.js';
|
6
6
|
import * as Havoc from './havoc.js';
|
7
7
|
import './prefs.js';
|
8
8
|
|
9
9
|
export const setup = () => {
|
10
|
-
importedFuncs[importedFuncs.profile2].params = [ Valtype.i32, valtypeBinary ];
|
11
|
-
|
12
10
|
// enable these prefs by default for pgo
|
13
11
|
for (const x of [
|
14
12
|
'typeswitchUniqueTmp', // use unique tmps for typeswitches
|
@@ -36,6 +34,15 @@ export const run = obj => {
|
|
36
34
|
|
37
35
|
time(0, `injecting PGO logging...`);
|
38
36
|
|
37
|
+
let activeFunc = null, abort = false;
|
38
|
+
createImport('profile1', 1, 0, n => {
|
39
|
+
activeFunc = n;
|
40
|
+
});
|
41
|
+
createImport('profile2', 2, 0, (i, n) => {
|
42
|
+
if (activeFunc == null) throw 'fail';
|
43
|
+
localData[activeFunc][i].push(n);
|
44
|
+
});
|
45
|
+
|
39
46
|
let funcs = [];
|
40
47
|
for (let i = 0; i < wasmFuncs.length; i++) {
|
41
48
|
const { name, internal, params, locals, wasm } = wasmFuncs[i];
|
@@ -79,7 +86,7 @@ export const run = obj => {
|
|
79
86
|
time(0, `injected PGO logging`);
|
80
87
|
time(1, `running with PGO logging...`);
|
81
88
|
|
82
|
-
|
89
|
+
|
83
90
|
try {
|
84
91
|
obj.wasm = assemble(obj.funcs, obj.globals, obj.tags, obj.pages, obj.data, true);
|
85
92
|
|
@@ -87,14 +94,7 @@ export const run = obj => {
|
|
87
94
|
Prefs.profileCompiler = false;
|
88
95
|
|
89
96
|
const { exports } = wrap(obj, undefined, {
|
90
|
-
|
91
|
-
activeFunc = n;
|
92
|
-
},
|
93
|
-
z: (i, n) => {
|
94
|
-
if (activeFunc == null) throw 'fail';
|
95
|
-
localData[activeFunc][i].push(n);
|
96
|
-
},
|
97
|
-
w: (ind, outPtr) => { // readArgv
|
97
|
+
readArgv: (ind, outPtr) => {
|
98
98
|
const pgoInd = process.argv.indexOf('--pgo');
|
99
99
|
let args = process.argv.slice(pgoInd);
|
100
100
|
args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
|
@@ -108,7 +108,7 @@ export const run = obj => {
|
|
108
108
|
writeByteStr(exports.$, outPtr, str);
|
109
109
|
return str.length;
|
110
110
|
},
|
111
|
-
|
111
|
+
readFile: (pathPtr, outPtr) => {
|
112
112
|
return -1;
|
113
113
|
}
|
114
114
|
}, () => {});
|
package/compiler/wrap.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { encodeVector } from './encoding.js';
|
2
|
-
import { importedFuncs } from './builtins.js';
|
2
|
+
import { importedFuncs, createImport } from './builtins.js';
|
3
3
|
import compile from './index.js';
|
4
4
|
import disassemble from './disassemble.js';
|
5
5
|
import { TYPES, TYPE_NAMES } from './types.js';
|
@@ -352,16 +352,61 @@ ${flags & 0b0001 ? ` get func idx: ${get}
|
|
352
352
|
}
|
353
353
|
};
|
354
354
|
|
355
|
+
export { createImport };
|
355
356
|
export default (source, module = undefined, customImports = {}, print = str => process.stdout.write(str)) => {
|
357
|
+
createImport('print', 1, 0, i => print(i.toString()));
|
358
|
+
createImport('printChar', 1, 0, i => print(String.fromCharCode(i)));
|
359
|
+
createImport('time', 0, 1, () => performance.now());
|
360
|
+
createImport('timeOrigin', 0, 1, () => performance.timeOrigin);
|
361
|
+
|
362
|
+
// todo: these should be provided elsewhere in runtime itself
|
363
|
+
createImport('__Porffor_readArgv', 2, 1, (ind, outPtr) => {
|
364
|
+
let args = process.argv.slice(2);
|
365
|
+
args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
|
366
|
+
|
367
|
+
const str = args[ind - 1];
|
368
|
+
if (!str) return -1;
|
369
|
+
|
370
|
+
writeByteStr(memory, outPtr, str);
|
371
|
+
return str.length;
|
372
|
+
});
|
373
|
+
createImport('__Porffor_readFile', 2, 1, (pathPtr, outPtr) => { // readFile
|
374
|
+
try {
|
375
|
+
const path = pathPtr === 0 ? 0 : readByteStr(memory, pathPtr);
|
376
|
+
const contents = fs.readFileSync(path, 'utf8');
|
377
|
+
writeByteStr(memory, outPtr, contents);
|
378
|
+
return contents.length;
|
379
|
+
} catch {
|
380
|
+
return -1;
|
381
|
+
}
|
382
|
+
});
|
383
|
+
|
384
|
+
for (const x in customImports) {
|
385
|
+
const custom = customImports[x];
|
386
|
+
|
387
|
+
if (x in importedFuncs) {
|
388
|
+
// overwrite with user custom import
|
389
|
+
const existing = importedFuncs[x];
|
390
|
+
if (typeof custom === 'function') {
|
391
|
+
existing.js = custom;
|
392
|
+
} else {
|
393
|
+
existing.params = custom.params;
|
394
|
+
existing.returns = custom.returns;
|
395
|
+
existing.js = custom.js;
|
396
|
+
existing.c = custom.c;
|
397
|
+
}
|
398
|
+
continue;
|
399
|
+
}
|
400
|
+
|
401
|
+
// todo: make a simpler api for just js functions at some point using function.length etc
|
402
|
+
createImport(x, custom.params, custom.returns, custom.js, custom.c);
|
403
|
+
}
|
404
|
+
|
356
405
|
const times = [];
|
357
406
|
|
358
407
|
const t1 = performance.now();
|
359
408
|
const { wasm, funcs, globals, tags, exceptions, pages, c } = typeof source === 'object' ? source : compile(source, module);
|
360
409
|
|
361
|
-
globalThis.porfDebugInfo = { funcs, globals };
|
362
|
-
|
363
|
-
// fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
364
|
-
|
365
410
|
times.push(performance.now() - t1);
|
366
411
|
if (Prefs.profileCompiler && !globalThis.onProgress) console.log(`\u001b[1mcompiled in ${times[0].toFixed(2)}ms\u001b[0m`);
|
367
412
|
|
@@ -448,35 +493,11 @@ export default (source, module = undefined, customImports = {}, print = str => p
|
|
448
493
|
try {
|
449
494
|
const module = new WebAssembly.Module(wasm);
|
450
495
|
instance = new WebAssembly.Instance(module, {
|
451
|
-
'': {
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
y: () => {},
|
457
|
-
z: () => {},
|
458
|
-
w: (ind, outPtr) => { // readArgv
|
459
|
-
let args = process.argv.slice(2);
|
460
|
-
args = args.slice(args.findIndex(x => !x.startsWith('-')) + 1);
|
461
|
-
|
462
|
-
const str = args[ind - 1];
|
463
|
-
if (!str) return -1;
|
464
|
-
|
465
|
-
writeByteStr(memory, outPtr, str);
|
466
|
-
return str.length;
|
467
|
-
},
|
468
|
-
q: (pathPtr, outPtr) => { // readFile
|
469
|
-
try {
|
470
|
-
const path = pathPtr === 0 ? 0 : readByteStr(memory, pathPtr);
|
471
|
-
const contents = fs.readFileSync(path, 'utf8');
|
472
|
-
writeByteStr(memory, outPtr, contents);
|
473
|
-
return contents.length;
|
474
|
-
} catch {
|
475
|
-
return -1;
|
476
|
-
}
|
477
|
-
},
|
478
|
-
...customImports
|
479
|
-
}
|
496
|
+
'': Object.keys(importedFuncs).reduce((acc, y) => {
|
497
|
+
const x = importedFuncs[y];
|
498
|
+
acc[x.import] = x.js ?? (() => {});
|
499
|
+
return acc;
|
500
|
+
}, {})
|
480
501
|
});
|
481
502
|
} catch (e) {
|
482
503
|
if (!Prefs.d) throw e;
|
package/package.json
CHANGED
package/runtime/debug.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
import compile from '../compiler/wrap.js';
|
2
|
+
import compile, { createImport } from '../compiler/wrap.js';
|
3
3
|
import Byg from '../byg/index.js';
|
4
4
|
import fs from 'node:fs';
|
5
5
|
|
@@ -41,75 +41,75 @@ let lastLine;
|
|
41
41
|
|
42
42
|
let output = '';
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if (callStarts[callStarts.length - 1] === n - 1) {
|
48
|
-
// end of call
|
44
|
+
createImport('profile1', 1, 0, n => {
|
45
|
+
if (callStarts[callStarts.length - 1] === n - 1) {
|
46
|
+
// end of call
|
49
47
|
|
50
|
-
|
51
|
-
|
48
|
+
callStarts.pop();
|
49
|
+
callStack.pop();
|
52
50
|
|
53
|
-
|
54
|
-
|
51
|
+
paused = _paused;
|
52
|
+
}
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
}
|
76
|
-
]
|
77
|
-
)) {
|
78
|
-
case 'resume': {
|
79
|
-
paused = false;
|
80
|
-
break;
|
81
|
-
}
|
82
|
-
|
83
|
-
case 'stepOver': {
|
84
|
-
break;
|
85
|
-
}
|
86
|
-
|
87
|
-
case 'stepIn': {
|
88
|
-
stepIn = true;
|
89
|
-
// paused = false;
|
90
|
-
break;
|
91
|
-
}
|
92
|
-
|
93
|
-
case 'stepOut': {
|
94
|
-
stepOut = true;
|
95
|
-
paused = false;
|
96
|
-
break;
|
97
|
-
}
|
54
|
+
lastLine = n;
|
55
|
+
|
56
|
+
if (breakpoints[n]) paused = true;
|
57
|
+
|
58
|
+
if (paused) {
|
59
|
+
stepIn = false; stepOut = false;
|
60
|
+
|
61
|
+
switch (byg(
|
62
|
+
paused,
|
63
|
+
n,
|
64
|
+
`\x1b[1mporffor debugger\x1b[22m: ${file}@${n + 1} ${callStack.join('->')}`,
|
65
|
+
[
|
66
|
+
{
|
67
|
+
x: termWidth - 1 - 40 - 6,
|
68
|
+
y: () => 4,
|
69
|
+
width: 40,
|
70
|
+
height: 20,
|
71
|
+
title: 'console',
|
72
|
+
content: output.split('\n')
|
98
73
|
}
|
74
|
+
]
|
75
|
+
)) {
|
76
|
+
case 'resume': {
|
77
|
+
paused = false;
|
78
|
+
break;
|
99
79
|
}
|
100
|
-
},
|
101
|
-
z: n => {
|
102
|
-
// start of call
|
103
|
-
callStack.push(funcs[n]);
|
104
80
|
|
105
|
-
|
81
|
+
case 'stepOver': {
|
82
|
+
break;
|
83
|
+
}
|
106
84
|
|
107
|
-
|
108
|
-
|
109
|
-
|
85
|
+
case 'stepIn': {
|
86
|
+
stepIn = true;
|
87
|
+
// paused = false;
|
88
|
+
break;
|
89
|
+
}
|
90
|
+
|
91
|
+
case 'stepOut': {
|
92
|
+
stepOut = true;
|
93
|
+
paused = false;
|
94
|
+
break;
|
95
|
+
}
|
110
96
|
}
|
111
|
-
}
|
97
|
+
}
|
98
|
+
});
|
99
|
+
|
100
|
+
createImport('profile2', 1, 0, n => {
|
101
|
+
// start of call
|
102
|
+
callStack.push(funcs[n]);
|
103
|
+
|
104
|
+
callStarts.push(lastLine);
|
112
105
|
|
106
|
+
_paused = paused;
|
107
|
+
if (!stepIn) paused = false;
|
108
|
+
else paused = true;
|
109
|
+
});
|
110
|
+
|
111
|
+
try {
|
112
|
+
const { exports } = compile(source, undefined, {}, s => output += s);
|
113
113
|
exports.main();
|
114
114
|
} catch (e) {
|
115
115
|
console.error(e);
|
package/runtime/flamegraph.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import { Opcodes, Valtype } from '../compiler/wasmSpec.js';
|
3
3
|
import { number } from '../compiler/encoding.js';
|
4
4
|
import { importedFuncs } from '../compiler/builtins.js';
|
5
|
-
import compile from '../compiler/wrap.js';
|
5
|
+
import compile, { createImport } from '../compiler/wrap.js';
|
6
6
|
import fs from 'node:fs';
|
7
7
|
|
8
8
|
const file = process.argv.slice(2).find(x => x[0] !== '-');
|
@@ -148,7 +148,19 @@ const render = () => {
|
|
148
148
|
lastRenderTime = performance.now() - renderStart;
|
149
149
|
};
|
150
150
|
|
151
|
-
|
151
|
+
createImport('profile1', 1, 0, f => { // pre-call
|
152
|
+
samplesStart.push(performance.now());
|
153
|
+
running[runningIdx++] = samplesFunc.push(f) - 1;
|
154
|
+
});
|
155
|
+
createImport('profile2', 1, 0, f => { // post-call
|
156
|
+
const now = performance.now();
|
157
|
+
samplesEnd[running[--runningIdx]] = now;
|
158
|
+
|
159
|
+
if (now > last) {
|
160
|
+
last = now + 500;
|
161
|
+
render();
|
162
|
+
}
|
163
|
+
});
|
152
164
|
|
153
165
|
Prefs.treeshakeWasmImports = false;
|
154
166
|
let funcLookup = new Map();
|
@@ -180,21 +192,7 @@ globalThis.compileCallback = ({ funcs }) => {
|
|
180
192
|
|
181
193
|
let last = 0;
|
182
194
|
let running = new Uint32Array(1024), runningIdx = 0;
|
183
|
-
const { exports } = compile(source, undefined, {
|
184
|
-
y: f => { // pre-call
|
185
|
-
samplesStart.push(performance.now());
|
186
|
-
running[runningIdx++] = samplesFunc.push(f) - 1;
|
187
|
-
},
|
188
|
-
z: f => { // post-call
|
189
|
-
const now = performance.now();
|
190
|
-
samplesEnd[running[--runningIdx]] = now;
|
191
|
-
|
192
|
-
if (now > last) {
|
193
|
-
last = now + 500;
|
194
|
-
render();
|
195
|
-
}
|
196
|
-
}
|
197
|
-
}, () => {});
|
195
|
+
const { exports } = compile(source, undefined, {}, () => {});
|
198
196
|
|
199
197
|
start = performance.now();
|
200
198
|
render();
|
package/runtime/hotlines.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
import compile from '../compiler/wrap.js';
|
2
|
+
import compile, { createImport } from '../compiler/wrap.js';
|
3
3
|
import fs from 'node:fs';
|
4
4
|
|
5
5
|
const file = process.argv.slice(2).find(x => x[0] !== '-');
|
@@ -18,29 +18,26 @@ const spinner = ['-', '\\', '|', '/'];
|
|
18
18
|
let spin = 0;
|
19
19
|
let last = 0;
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
const t = performance.now();
|
28
|
-
times[n] += t - tmp[n];
|
29
|
-
|
30
|
-
samples++;
|
31
|
-
if (t > last) {
|
32
|
-
process.stdout.write(`\r${spinner[spin++ % 4]} running: collected ${samples} samples...`);
|
33
|
-
last = t + 100;
|
34
|
-
}
|
35
|
-
}
|
36
|
-
});
|
21
|
+
createImport('profile1', 1, 0, n => {
|
22
|
+
tmp[n] = performance.now();
|
23
|
+
});
|
24
|
+
createImport('profile2', 1, 0, n => {
|
25
|
+
const t = performance.now();
|
26
|
+
times[n] += t - tmp[n];
|
37
27
|
|
38
|
-
|
28
|
+
samples++;
|
29
|
+
if (t > last) {
|
30
|
+
process.stdout.write(`\r${spinner[spin++ % 4]} running: collected ${samples} samples...`);
|
31
|
+
last = t + 100;
|
32
|
+
}
|
33
|
+
})
|
39
34
|
|
35
|
+
try {
|
36
|
+
const { exports } = compile(source, undefined);
|
37
|
+
const start = performance.now();
|
40
38
|
exports.main();
|
41
39
|
|
42
40
|
const total = performance.now() - start;
|
43
|
-
|
44
41
|
console.log(`\ntotal: ${total}ms\nsamples: ${samples}\n\n\n` + source.split('\n').map(x => {
|
45
42
|
let time = 0;
|
46
43
|
if (x.startsWith('profile')) {
|