porffor 0.37.8 → 0.37.10
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 +3 -4
- package/compiler/assemble.js +1 -2
- package/compiler/index.js +9 -2
- package/compiler/opt.js +1 -82
- package/package.json +1 -1
- package/runner/index.js +1 -1
package/README.md
CHANGED
@@ -64,8 +64,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
64
64
|
- `--valtype=i32|i64|f64` (default: `f64`) to set valtype
|
65
65
|
- `-O0` to disable opt
|
66
66
|
- `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
|
67
|
-
- `-O2` to enable advanced opt (
|
68
|
-
- `-O3` to enable advanceder opt (precompute const math). unstable!
|
67
|
+
- `-O2` to enable advanced opt (partial evaluation). unstable!
|
69
68
|
|
70
69
|
## Current limitations
|
71
70
|
- Limited async support
|
@@ -274,7 +273,7 @@ Currently, Porffor is seriously limited in features and functionality, however i
|
|
274
273
|
- More in future probably?
|
275
274
|
|
276
275
|
## Todo
|
277
|
-
No particular order and no
|
276
|
+
No particular order and no guarantees, just what could happen soon™
|
278
277
|
|
279
278
|
- Asur
|
280
279
|
- Support memory
|
@@ -328,7 +327,7 @@ Porffor intentionally does not use Wasm proposals which are not commonly impleme
|
|
328
327
|
### 2. Why at all?
|
329
328
|
Yes!
|
330
329
|
|
331
|
-
|
330
|
+
### 3. Isn't this the same as AssemblyScript/other Wasm langs?
|
332
331
|
No. they are not alike at all internally and have very different goals/ideals:
|
333
332
|
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
334
333
|
- Porffor primarily consumes JS
|
package/compiler/assemble.js
CHANGED
@@ -58,7 +58,7 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
|
|
58
58
|
const getType = (params, returns) => {
|
59
59
|
const hash = `${params.join(',')}_${returns.join(',')}`;
|
60
60
|
if (Prefs.optLog) log('assemble', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
|
61
|
-
if (
|
61
|
+
if (typeCache[hash] !== undefined) return typeCache[hash];
|
62
62
|
|
63
63
|
const type = [ FuncType, ...encodeVector(params), ...encodeVector(returns) ];
|
64
64
|
const idx = types.length;
|
@@ -233,7 +233,6 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
|
|
233
233
|
time('global section');
|
234
234
|
|
235
235
|
if (Prefs.alwaysMemory && pages.size === 0) pages.set('--always-memory', 0);
|
236
|
-
if (optLevel === 0) pages.set('O0 precaution', 0);
|
237
236
|
|
238
237
|
const usesMemory = pages.size > 0;
|
239
238
|
const memorySection = !usesMemory ? [] : createSection(
|
package/compiler/index.js
CHANGED
@@ -53,6 +53,8 @@ const progressClear = () => {
|
|
53
53
|
};
|
54
54
|
|
55
55
|
export default (code, flags) => {
|
56
|
+
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
57
|
+
|
56
58
|
let target = Prefs.target ?? 'wasm';
|
57
59
|
if (Prefs.native) target = 'native';
|
58
60
|
|
@@ -67,8 +69,13 @@ export default (code, flags) => {
|
|
67
69
|
|
68
70
|
// change some prefs by default for c/native
|
69
71
|
if (target !== 'wasm') {
|
70
|
-
Prefs.pgo = Prefs.pgo === false ? false : true;
|
71
|
-
Prefs.passiveData = false;
|
72
|
+
Prefs.pgo = Prefs.pgo === false ? false : true; // enable pgo
|
73
|
+
Prefs.passiveData = false; // disable using passive Wasm data as unsupported by 2c for now
|
74
|
+
}
|
75
|
+
|
76
|
+
// change some prefs by default for -O2
|
77
|
+
if (optLevel >= 2) {
|
78
|
+
Prefs.cyclone = Prefs.cyclone === false ? false : true; // enable cyclone
|
72
79
|
}
|
73
80
|
|
74
81
|
if (Prefs.pgo) pgo.setup();
|
package/compiler/opt.js
CHANGED
@@ -9,88 +9,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
9
9
|
if (optLevel === 0) return;
|
10
10
|
|
11
11
|
const tailCall = Prefs.tailCall;
|
12
|
-
if (tailCall) log.warning('opt', 'tail call proposal is not widely implemented! (you used
|
13
|
-
|
14
|
-
if (optLevel >= 2 && !Prefs.optNoInline) {
|
15
|
-
// inline pass (very WIP)
|
16
|
-
// get candidates for inlining
|
17
|
-
// todo: pick smart in future (if func is used <N times? or?)
|
18
|
-
const callsSelf = f => f.wasm.some(x => x[0] === Opcodes.call && x[1] === f.index);
|
19
|
-
const suitableReturns = wasm => wasm.reduce((acc, x) => acc + (x[0] === Opcodes.return), 0) <= 1;
|
20
|
-
const candidates = funcs.filter(x => x.name !== 'main' && Object.keys(x.locals).length === x.params.length && (x.returns.length === 0 || suitableReturns(x.wasm)) && !callsSelf(x) && !x.throws).reverse();
|
21
|
-
if (Prefs.optLog) {
|
22
|
-
log('opt', `found inline candidates: ${candidates.map(x => x.name).join(', ')} (${candidates.length}/${funcs.length - 1})`);
|
23
|
-
|
24
|
-
let reasons = {};
|
25
|
-
for (const f of funcs) {
|
26
|
-
if (f.name === 'main') continue;
|
27
|
-
reasons[f.name] = [];
|
28
|
-
|
29
|
-
if (f.name === 'main') reasons[f.name].push('main');
|
30
|
-
if (Object.keys(f.locals).length !== f.params.length) reasons[f.name].push('cannot inline funcs with locals yet');
|
31
|
-
if (f.returns.length !== 0 && !suitableReturns(f.wasm)) reasons[f.name].push('cannot inline funcs with multiple returns yet');
|
32
|
-
if (callsSelf(f)) reasons[f.name].push('cannot inline func calling itself');
|
33
|
-
if (f.throws) reasons[f.name].push('will not inline funcs throwing yet');
|
34
|
-
}
|
35
|
-
|
36
|
-
if (Object.values(reasons).some(x => x.length > 0)) console.log(` reasons not:\n${Object.keys(reasons).filter(x => reasons[x].length > 0).map(x => ` ${x}: ${reasons[x].join(', ')}`).join('\n')}\n`)
|
37
|
-
}
|
38
|
-
|
39
|
-
for (const c of candidates) {
|
40
|
-
const cWasm = c.wasm;
|
41
|
-
|
42
|
-
for (const t of funcs) {
|
43
|
-
const tWasm = t.wasm;
|
44
|
-
if (t.name === c.name) continue; // skip self
|
45
|
-
|
46
|
-
for (let i = 0; i < tWasm.length; i++) {
|
47
|
-
const inst = tWasm[i];
|
48
|
-
if (inst[0] === Opcodes.call && inst[1] === c.index) {
|
49
|
-
if (Prefs.optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
|
50
|
-
tWasm.splice(i, 1); // remove this call
|
51
|
-
|
52
|
-
// add params as locals and set in reverse order
|
53
|
-
const paramIdx = {};
|
54
|
-
let localIdx = Math.max(-1, ...Object.values(t.locals).map(x => x.idx)) + 1;
|
55
|
-
for (let j = c.params.length - 1; j >= 0; j--) {
|
56
|
-
const name = `__porf_inline_${c.name}_param_${j}`;
|
57
|
-
|
58
|
-
if (t.locals[name] === undefined) {
|
59
|
-
t.locals[name] = { idx: localIdx++, type: c.params[j] };
|
60
|
-
}
|
61
|
-
|
62
|
-
const idx = t.locals[name].idx;
|
63
|
-
paramIdx[j] = idx;
|
64
|
-
|
65
|
-
tWasm.splice(i, 0, [ Opcodes.local_set, idx ]);
|
66
|
-
i++;
|
67
|
-
}
|
68
|
-
|
69
|
-
let iWasm = cWasm.slice().map(x => x.slice()); // deep clone arr (depth 2)
|
70
|
-
// remove final return
|
71
|
-
if (iWasm.length !== 0 && iWasm[iWasm.length - 1][0] === Opcodes.return) iWasm = iWasm.slice(0, -1);
|
72
|
-
|
73
|
-
// adjust local operands to go to correct param index
|
74
|
-
for (const inst of iWasm) {
|
75
|
-
if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) && inst[1] < c.params.length) {
|
76
|
-
if (Prefs.optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
|
77
|
-
inst[1] = paramIdx[inst[1]];
|
78
|
-
}
|
79
|
-
}
|
80
|
-
|
81
|
-
tWasm.splice(i, 0, ...iWasm);
|
82
|
-
i += iWasm.length;
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
if (t.index > c.index) t.index--; // adjust index if after removed func
|
87
|
-
}
|
88
|
-
|
89
|
-
funcs.splice(funcs.indexOf(c), 1); // remove func from funcs
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
if (Prefs.optInlineOnly) return;
|
12
|
+
if (tailCall) log.warning('opt', 'tail call proposal is not widely implemented! (you used --tail-call)');
|
94
13
|
|
95
14
|
// todo: this breaks exceptions after due to indexes not being adjusted
|
96
15
|
// const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
package/package.json
CHANGED