porffor 0.16.0-594397507 → 0.16.0-688a50c13
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 +1 -1
- package/a.txt +457 -0
- package/b.txt +457 -0
- package/compiler/2c.js +53 -65
- package/compiler/allocators/grow.js +26 -0
- package/compiler/allocators/index.js +10 -0
- package/compiler/allocators/static.js +42 -0
- package/compiler/assemble.js +5 -4
- package/compiler/builtins/date.ts +2 -4
- package/compiler/builtins/porffor.d.ts +3 -0
- package/compiler/builtins/set.ts +2 -4
- package/compiler/builtins.js +25 -5
- package/compiler/codegen.js +210 -325
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +3 -1
- package/compiler/generated_builtins.js +18 -18
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +89 -6
- package/compiler/opt.js +10 -44
- package/compiler/parse.js +1 -1
- package/compiler/pgo.js +207 -0
- package/compiler/precompile.js +11 -6
- package/compiler/prefs.js +7 -2
- package/compiler/prototype.js +34 -43
- package/compiler/wrap.js +66 -25
- package/no_pgo.txt +923 -0
- package/package.json +3 -5
- package/pgo.txt +916 -0
- package/runner/index.js +21 -11
- /package/runner/{profiler.js → profile.js} +0 -0
package/compiler/parse.js
CHANGED
@@ -43,7 +43,7 @@ export default (input, flags) => {
|
|
43
43
|
webcompat: true,
|
44
44
|
|
45
45
|
// babel
|
46
|
-
plugins: types ? ['estree', 'typescript'] : ['estree'],
|
46
|
+
plugins: types || flags.includes('typed') ? ['estree', 'typescript'] : ['estree'],
|
47
47
|
|
48
48
|
// multiple
|
49
49
|
sourceType: flags.includes('module') ? 'module' : 'script',
|
package/compiler/pgo.js
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
import { Opcodes, Valtype } from './wasmSpec.js';
|
2
|
+
import { number } from './embedding.js';
|
3
|
+
import { importedFuncs } from './builtins.js';
|
4
|
+
import Prefs from './prefs.js';
|
5
|
+
import assemble from './assemble.js';
|
6
|
+
import wrap, { writeByteStr } from './wrap.js';
|
7
|
+
import * as Havoc from './havoc.js';
|
8
|
+
|
9
|
+
export const setup = () => {
|
10
|
+
importedFuncs[importedFuncs.profile2].params = [ Valtype.i32, valtypeBinary ];
|
11
|
+
|
12
|
+
// enable these prefs by default for pgo
|
13
|
+
Prefs.typeswitchUniqueTmp = Prefs.typeswitchUniqueTmp === false ? false : true;
|
14
|
+
Prefs.cyclone = Prefs.cyclone === false ? false : true;
|
15
|
+
};
|
16
|
+
|
17
|
+
export const run = obj => {
|
18
|
+
const wasmFuncs = obj.funcs;
|
19
|
+
|
20
|
+
let starts = {};
|
21
|
+
const time = (id, msg) => {
|
22
|
+
if (!Prefs.pgoLog) return;
|
23
|
+
|
24
|
+
if (!starts[id]) {
|
25
|
+
process.stdout.write(msg);
|
26
|
+
starts[id] = performance.now();
|
27
|
+
} else {
|
28
|
+
process.stdout.write(`\r${' '.repeat(50)}\r[${(performance.now() - starts[id]).toFixed(2)}ms] ${msg}\n`);
|
29
|
+
}
|
30
|
+
};
|
31
|
+
|
32
|
+
time(0, `injecting PGO logging...`);
|
33
|
+
|
34
|
+
let funcs = [];
|
35
|
+
for (let i = 0; i < wasmFuncs.length; i++) {
|
36
|
+
const { name, internal, params, locals, wasm } = wasmFuncs[i];
|
37
|
+
if (internal) continue; // ignore internal funcs
|
38
|
+
wasmFuncs[i].originalWasm = structuredClone(wasm);
|
39
|
+
|
40
|
+
const invLocals = Object.keys(locals).reduce((acc, x) => { acc[locals[x].idx] = locals[x]; return acc; }, {});
|
41
|
+
|
42
|
+
const id = funcs.length;
|
43
|
+
funcs.push({ name, id, locals, params, invLocals });
|
44
|
+
|
45
|
+
wasm.unshift(
|
46
|
+
// mark active func
|
47
|
+
...number(i, Valtype.i32),
|
48
|
+
[ Opcodes.call, importedFuncs.profile1 ],
|
49
|
+
|
50
|
+
// log args
|
51
|
+
...params.flatMap((_, i) => [
|
52
|
+
...number(i, Valtype.i32),
|
53
|
+
[ Opcodes.local_get, i ],
|
54
|
+
...(invLocals[i].type !== Valtype.f64 ? [ Opcodes.i32_from ] : []),
|
55
|
+
[ Opcodes.call, importedFuncs.profile2 ]
|
56
|
+
])
|
57
|
+
);
|
58
|
+
|
59
|
+
for (let j = 0; j < wasm.length; j++) {
|
60
|
+
const inst = wasm[j];
|
61
|
+
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
|
62
|
+
wasm.splice(j + 1, 0,
|
63
|
+
...number(inst[1], Valtype.i32),
|
64
|
+
[ Opcodes.local_get, inst[1] ],
|
65
|
+
...(invLocals[inst[1]].type !== Valtype.f64 ? [ Opcodes.i32_from ] : []),
|
66
|
+
[ Opcodes.call, importedFuncs.profile2 ]
|
67
|
+
);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
let localData = funcs.map(x => new Array(Object.keys(x.locals).length).fill(0).map(() => []));
|
73
|
+
|
74
|
+
time(0, `injected PGO logging`);
|
75
|
+
time(1, `running with PGO logging...`);
|
76
|
+
|
77
|
+
let activeFunc = null, abort = false;
|
78
|
+
try {
|
79
|
+
obj.wasm = assemble(obj.funcs, obj.globals, obj.tags, obj.pages, obj.data, obj.flags, true);
|
80
|
+
|
81
|
+
const { exports } = wrap(obj, [], {
|
82
|
+
y: n => {
|
83
|
+
activeFunc = n;
|
84
|
+
},
|
85
|
+
z: (i, n) => {
|
86
|
+
if (activeFunc == null) throw 'fail';
|
87
|
+
localData[activeFunc][i].push(n);
|
88
|
+
},
|
89
|
+
w: (ind, outPtr) => { // readArgv
|
90
|
+
const pgoInd = process.argv.indexOf('--pgo');
|
91
|
+
const args = process.argv.slice(pgoInd).filter(x => !x.startsWith('-'));
|
92
|
+
const str = args[ind - 1];
|
93
|
+
if (pgoInd === -1 || !str) {
|
94
|
+
if (Prefs.pgoLog) console.log('\nPGO warning: script was expecting arguments, please specify args to use for PGO after --pgo arg');
|
95
|
+
return -1;
|
96
|
+
}
|
97
|
+
|
98
|
+
writeByteStr(exports.$, outPtr, str);
|
99
|
+
return str.length;
|
100
|
+
}
|
101
|
+
}, () => {});
|
102
|
+
|
103
|
+
exports.main();
|
104
|
+
} catch (e) {
|
105
|
+
throw e;
|
106
|
+
}
|
107
|
+
|
108
|
+
for (const x of funcs) {
|
109
|
+
const wasmFunc = wasmFuncs.find(y => y.name === x.name);
|
110
|
+
wasmFunc.wasm = wasmFunc.originalWasm;
|
111
|
+
delete wasmFunc.originalWasm;
|
112
|
+
}
|
113
|
+
|
114
|
+
if (abort) {
|
115
|
+
console.log('aborting PGO!');
|
116
|
+
return false;
|
117
|
+
}
|
118
|
+
|
119
|
+
time(1, `ran with PGO logging`);
|
120
|
+
time(2, 'processing PGO data...');
|
121
|
+
|
122
|
+
// process data
|
123
|
+
let log = '';
|
124
|
+
for (let i = 0; i < localData.length; i++) {
|
125
|
+
const func = funcs[i];
|
126
|
+
const total = localData[i].length;
|
127
|
+
|
128
|
+
const localKeys = Object.keys(func.locals).sort((a, b) => a.idx - b.idx);
|
129
|
+
const localValues = Object.values(func.locals).sort((a, b) => a.idx - b.idx);
|
130
|
+
func.localKeys = localKeys;
|
131
|
+
func.localValues = localValues;
|
132
|
+
|
133
|
+
let counts = new Array(10).fill(0);
|
134
|
+
const consistents = localData[i].map((x, j) => {
|
135
|
+
if (j < func.params.length) return false; // param
|
136
|
+
if (x.length === 0 || !x.every((y, i) => i < 1 ? true : y === x[i - 1])) return false; // not consistent
|
137
|
+
|
138
|
+
counts[0]++;
|
139
|
+
return x[0];
|
140
|
+
});
|
141
|
+
|
142
|
+
const integerOnlyF64s = localData[i].map((x, j) => {
|
143
|
+
if (j < func.params.length) return false; // param
|
144
|
+
if (localValues[j].type === Valtype.i32) return false; // already i32
|
145
|
+
if (x.length === 0 || !x.every(y => Number.isInteger(y))) return false; // not all integer values
|
146
|
+
|
147
|
+
counts[1]++;
|
148
|
+
return true;
|
149
|
+
});
|
150
|
+
|
151
|
+
func.consistents = consistents;
|
152
|
+
func.integerOnlyF64s = integerOnlyF64s;
|
153
|
+
|
154
|
+
log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
|
155
|
+
if (Prefs.verbosePgo) {
|
156
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
157
|
+
log += ` ${consistents[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${new Set(localData[i][j]).size} unique values set\n`;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
log += ` ${func.name}: identified ${counts[1]}/${localValues.reduce((acc, x) => acc + (x.type === Valtype.f64 ? 1 : 0), 0)} f64 locals as integer usage only${Prefs.verbosePgo ? ':' : ''}\n`;
|
162
|
+
if (Prefs.verbosePgo) {
|
163
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
164
|
+
if (localValues[j].type !== Valtype.f64) continue;
|
165
|
+
log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
|
166
|
+
}
|
167
|
+
|
168
|
+
log += '\n';
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
time(2, 'processed PGO data' + log);
|
173
|
+
time(3, 'optimizing using PGO data...');
|
174
|
+
|
175
|
+
log = '';
|
176
|
+
for (const x of funcs) {
|
177
|
+
const wasmFunc = wasmFuncs.find(y => y.name === x.name);
|
178
|
+
|
179
|
+
let targets = [];
|
180
|
+
for (let i = 0; i < x.integerOnlyF64s.length; i++) {
|
181
|
+
const c = x.integerOnlyF64s[i];
|
182
|
+
if (c === false) continue;
|
183
|
+
|
184
|
+
targets.push(i);
|
185
|
+
}
|
186
|
+
|
187
|
+
log += ` ${x.name}: replaced ${targets.length} f64 locals with i32s\n`;
|
188
|
+
if (targets.length > 0) Havoc.f64ToI32s(wasmFunc, targets);
|
189
|
+
|
190
|
+
targets = [];
|
191
|
+
let consts = [];
|
192
|
+
for (let i = 0; i < x.consistents.length; i++) {
|
193
|
+
const c = x.consistents[i];
|
194
|
+
if (c === false) continue;
|
195
|
+
|
196
|
+
targets.push(i);
|
197
|
+
|
198
|
+
const valtype = x.localValues[i].type;
|
199
|
+
consts.push(number(c, valtype)[0]);
|
200
|
+
}
|
201
|
+
|
202
|
+
log += ` ${x.name}: replaced ${targets.length} locals with consts\n`;
|
203
|
+
if (targets.length > 0) Havoc.localsToConsts(wasmFunc, targets, consts, { localKeys: x.localKeys });
|
204
|
+
}
|
205
|
+
|
206
|
+
time(3, 'optimized using PGO data' + log);
|
207
|
+
};
|
package/compiler/precompile.js
CHANGED
@@ -26,7 +26,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
26
26
|
|
27
27
|
const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
|
28
28
|
|
29
|
-
let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
|
29
|
+
let { funcs, globals, data, exceptions } = porfCompile(source, ['module', 'typed']);
|
30
30
|
|
31
31
|
const allocated = new Set();
|
32
32
|
|
@@ -38,11 +38,14 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
38
38
|
x.data[y].offset -= x.data[0].offset;
|
39
39
|
}
|
40
40
|
}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
|
42
|
+
if (x.exceptions) {
|
43
|
+
x.exceptions = x.exceptions.map(x => {
|
44
|
+
const obj = exceptions[x];
|
45
|
+
if (obj) obj.exceptId = x;
|
46
|
+
return obj;
|
47
|
+
}).filter(x => x);
|
48
|
+
}
|
46
49
|
|
47
50
|
const locals = Object.keys(x.locals).reduce((acc, y) => {
|
48
51
|
acc[x.locals[y].idx] = { ...x.locals[y], name: y };
|
@@ -87,6 +90,8 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
87
90
|
};
|
88
91
|
|
89
92
|
const precompile = async () => {
|
93
|
+
if (globalThis._porf_loadParser) await globalThis._porf_loadParser('@babel/parser');
|
94
|
+
|
90
95
|
const dir = join(__dirname, 'builtins');
|
91
96
|
|
92
97
|
let funcs = [], globals = [];
|
package/compiler/prefs.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused' ];
|
1
|
+
const onByDefault = [ 'bytestring', 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused', 'data', 'rmUnusedTypes' ];
|
2
2
|
|
3
3
|
let cache = {};
|
4
4
|
const obj = new Proxy({}, {
|
@@ -23,9 +23,14 @@ const obj = new Proxy({}, {
|
|
23
23
|
// do not cache in web demo as args are changed live
|
24
24
|
if (!globalThis.document) cache[p] = ret;
|
25
25
|
return ret;
|
26
|
+
},
|
27
|
+
|
28
|
+
set(_, p, v) {
|
29
|
+
cache[p] = v;
|
30
|
+
return true;
|
26
31
|
}
|
27
32
|
});
|
28
33
|
|
29
|
-
|
34
|
+
export const uncache = () => cache = {};
|
30
35
|
|
31
36
|
export default obj;
|
package/compiler/prototype.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import { Opcodes, Blocktype, Valtype, ValtypeSize } from './wasmSpec.js';
|
2
2
|
import { number } from './embedding.js';
|
3
|
-
import { unsignedLEB128 } from './encoding.js';
|
4
3
|
import { UNDEFINED } from './builtins.js';
|
5
4
|
import { TYPES } from './types.js';
|
6
5
|
import Prefs from './prefs.js';
|
@@ -55,10 +54,10 @@ export const PrototypeFuncs = function() {
|
|
55
54
|
|
56
55
|
// read from memory
|
57
56
|
[ Opcodes.local_get, iTmp ],
|
58
|
-
[ Opcodes.load, 0,
|
57
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
59
58
|
|
60
59
|
[ Opcodes.local_get, iTmp ],
|
61
|
-
[ Opcodes.i32_load8_u, 0,
|
60
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
62
61
|
],
|
63
62
|
|
64
63
|
// todo: only for 1 argument
|
@@ -74,12 +73,12 @@ export const PrototypeFuncs = function() {
|
|
74
73
|
// store value
|
75
74
|
[ Opcodes.local_get, iTmp ],
|
76
75
|
...wNewMember,
|
77
|
-
[ Opcodes.store, 0,
|
76
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
78
77
|
|
79
78
|
// store type
|
80
79
|
[ Opcodes.local_get, iTmp ],
|
81
80
|
...wType,
|
82
|
-
[ Opcodes.i32_store8, 0,
|
81
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
83
82
|
|
84
83
|
// bump array length by 1 and return it
|
85
84
|
...length.setI32([
|
@@ -137,10 +136,10 @@ export const PrototypeFuncs = function() {
|
|
137
136
|
[ Opcodes.local_set, iTmp ],
|
138
137
|
|
139
138
|
[ Opcodes.local_get, iTmp ],
|
140
|
-
[ Opcodes.load, 0,
|
139
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
141
140
|
|
142
141
|
[ Opcodes.local_get, iTmp ],
|
143
|
-
[ Opcodes.i32_load8_u, 0,
|
142
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
144
143
|
])
|
145
144
|
],
|
146
145
|
|
@@ -168,10 +167,10 @@ export const PrototypeFuncs = function() {
|
|
168
167
|
// load first element
|
169
168
|
// todo/perf: unusedValue opt
|
170
169
|
...pointer,
|
171
|
-
[ Opcodes.load, 0,
|
170
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
172
171
|
|
173
172
|
...pointer,
|
174
|
-
[ Opcodes.i32_load8_u, 0,
|
173
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
175
174
|
|
176
175
|
// offset page by -1 ind
|
177
176
|
// ...number(pointer + ValtypeSize.i32, Valtype.i32), // dst = base array index + length size
|
@@ -250,12 +249,12 @@ export const PrototypeFuncs = function() {
|
|
250
249
|
// store value
|
251
250
|
[ Opcodes.local_get, iTmp2 ],
|
252
251
|
[ Opcodes.local_get, iTmp ],
|
253
|
-
[ Opcodes.store, 0,
|
252
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
254
253
|
|
255
254
|
// store type
|
256
255
|
[ Opcodes.local_get, iTmp2 ],
|
257
256
|
...wType,
|
258
|
-
[ Opcodes.i32_store8, 0,
|
257
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
259
258
|
|
260
259
|
// pointer - sizeof value
|
261
260
|
...length.getCachedI32(),
|
@@ -292,11 +291,8 @@ export const PrototypeFuncs = function() {
|
|
292
291
|
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
293
292
|
|
294
293
|
return [
|
295
|
-
// setup new/out array
|
294
|
+
// setup new/out array and use pointer for store
|
296
295
|
...newOut,
|
297
|
-
[ Opcodes.drop ],
|
298
|
-
|
299
|
-
...number(0, Valtype.i32), // base 0 for store later
|
300
296
|
|
301
297
|
...wIndex,
|
302
298
|
Opcodes.i32_to_u,
|
@@ -335,13 +331,14 @@ export const PrototypeFuncs = function() {
|
|
335
331
|
[ Opcodes.i32_add ],
|
336
332
|
|
337
333
|
// load current string ind {arg}
|
338
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
334
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
339
335
|
|
340
336
|
// store to new string ind 0
|
341
|
-
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1,
|
337
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
342
338
|
|
343
339
|
// return new string (pointer)
|
344
|
-
...
|
340
|
+
...newPointer,
|
341
|
+
Opcodes.i32_from_u
|
345
342
|
];
|
346
343
|
},
|
347
344
|
|
@@ -350,11 +347,8 @@ export const PrototypeFuncs = function() {
|
|
350
347
|
const [ newOut, newPointer ] = arrayShell(1, 'i16');
|
351
348
|
|
352
349
|
return [
|
353
|
-
// setup new/out array
|
350
|
+
// setup new/out array and use as pointer for store
|
354
351
|
...newOut,
|
355
|
-
[ Opcodes.drop ],
|
356
|
-
|
357
|
-
...number(0, Valtype.i32), // base 0 for store later
|
358
352
|
|
359
353
|
...wIndex,
|
360
354
|
Opcodes.i32_to,
|
@@ -366,13 +360,14 @@ export const PrototypeFuncs = function() {
|
|
366
360
|
[ Opcodes.i32_add ],
|
367
361
|
|
368
362
|
// load current string ind {arg}
|
369
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
363
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
370
364
|
|
371
365
|
// store to new string ind 0
|
372
|
-
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1,
|
366
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
373
367
|
|
374
368
|
// return new string (page)
|
375
|
-
...
|
369
|
+
...newPointer,
|
370
|
+
Opcodes.i32_from_u
|
376
371
|
];
|
377
372
|
},
|
378
373
|
|
@@ -411,7 +406,7 @@ export const PrototypeFuncs = function() {
|
|
411
406
|
[ Opcodes.i32_add ],
|
412
407
|
|
413
408
|
// load current string ind {arg}
|
414
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
409
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
415
410
|
Opcodes.i32_from_u
|
416
411
|
],
|
417
412
|
|
@@ -433,7 +428,7 @@ export const PrototypeFuncs = function() {
|
|
433
428
|
[ Opcodes.block, Blocktype.void ],
|
434
429
|
|
435
430
|
[ Opcodes.local_get, iTmp ],
|
436
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
431
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
437
432
|
[ Opcodes.local_set, iTmp2 ],
|
438
433
|
|
439
434
|
// if not surrogate, continue
|
@@ -455,7 +450,7 @@ export const PrototypeFuncs = function() {
|
|
455
450
|
|
456
451
|
// if not followed by trailing surrogate, return false
|
457
452
|
[ Opcodes.local_get, iTmp ],
|
458
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
453
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 + ValtypeSize.i16 ],
|
459
454
|
...number(0xFC00, Valtype.i32),
|
460
455
|
[ Opcodes.i32_and ],
|
461
456
|
...number(0xDC00, Valtype.i32),
|
@@ -507,11 +502,8 @@ export const PrototypeFuncs = function() {
|
|
507
502
|
const [ newOut, newPointer ] = arrayShell(1, 'i8');
|
508
503
|
|
509
504
|
return [
|
510
|
-
// setup new/out array
|
505
|
+
// setup new/out array and use pointer for store later
|
511
506
|
...newOut,
|
512
|
-
[ Opcodes.drop ],
|
513
|
-
|
514
|
-
...number(0, Valtype.i32), // base 0 for store later
|
515
507
|
|
516
508
|
...wIndex,
|
517
509
|
Opcodes.i32_to_u,
|
@@ -548,13 +540,14 @@ export const PrototypeFuncs = function() {
|
|
548
540
|
[ Opcodes.i32_add ],
|
549
541
|
|
550
542
|
// load current string ind {arg}
|
551
|
-
[ Opcodes.i32_load8_u, 0,
|
543
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
552
544
|
|
553
545
|
// store to new string ind 0
|
554
|
-
[ Opcodes.i32_store8, 0,
|
546
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
|
555
547
|
|
556
548
|
// return new string (pointer)
|
557
|
-
...
|
549
|
+
...newPointer,
|
550
|
+
Opcodes.i32_from_u
|
558
551
|
];
|
559
552
|
},
|
560
553
|
|
@@ -563,11 +556,8 @@ export const PrototypeFuncs = function() {
|
|
563
556
|
const [ newOut, newPointer ] = arrayShell(1, 'i8');
|
564
557
|
|
565
558
|
return [
|
566
|
-
// setup new/out array
|
559
|
+
// setup new/out array and use pointer for later
|
567
560
|
...newOut,
|
568
|
-
[ Opcodes.drop ],
|
569
|
-
|
570
|
-
...number(0, Valtype.i32), // base 0 for store later
|
571
561
|
|
572
562
|
...wIndex,
|
573
563
|
Opcodes.i32_to,
|
@@ -576,13 +566,14 @@ export const PrototypeFuncs = function() {
|
|
576
566
|
[ Opcodes.i32_add ],
|
577
567
|
|
578
568
|
// load current string ind {arg}
|
579
|
-
[ Opcodes.i32_load8_u, 0,
|
569
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
580
570
|
|
581
571
|
// store to new string ind 0
|
582
|
-
[ Opcodes.i32_store8, 0,
|
572
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
|
583
573
|
|
584
574
|
// return new string (page)
|
585
|
-
...
|
575
|
+
...newPointer,
|
576
|
+
Opcodes.i32_from_u
|
586
577
|
];
|
587
578
|
},
|
588
579
|
|
@@ -618,7 +609,7 @@ export const PrototypeFuncs = function() {
|
|
618
609
|
[ Opcodes.i32_add ],
|
619
610
|
|
620
611
|
// load current string ind {arg}
|
621
|
-
[ Opcodes.i32_load8_u, 0,
|
612
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
622
613
|
Opcodes.i32_from_u
|
623
614
|
],
|
624
615
|
|
package/compiler/wrap.js
CHANGED
@@ -6,8 +6,25 @@ import { TYPES } from './types.js';
|
|
6
6
|
import { log } from './log.js';
|
7
7
|
import Prefs from './prefs.js';
|
8
8
|
|
9
|
+
const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs')) : undefined);
|
10
|
+
|
9
11
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
10
12
|
|
13
|
+
export const readByteStr = (memory, ptr) => {
|
14
|
+
const length = (new Int32Array(memory.buffer.slice(ptr, ptr + 4), 0, 1))[0];
|
15
|
+
return Array.from(new Uint8Array(memory.buffer, ptr + 4, length)).map(x => String.fromCharCode(x)).join('');
|
16
|
+
};
|
17
|
+
|
18
|
+
export const writeByteStr = (memory, ptr, str) => {
|
19
|
+
const length = str.length;
|
20
|
+
(new Int32Array(memory.buffer.slice(ptr, ptr + 4), 0, 1))[0] = length;
|
21
|
+
|
22
|
+
const arr = new Uint8Array(memory.buffer, ptr + 4, length);
|
23
|
+
for (let i = 0; i < length; i++) {
|
24
|
+
arr[i] = str.charCodeAt(i);
|
25
|
+
}
|
26
|
+
};
|
27
|
+
|
11
28
|
const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
12
29
|
switch (type) {
|
13
30
|
case TYPES.boolean: return Boolean(value);
|
@@ -29,17 +46,17 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
|
29
46
|
}
|
30
47
|
|
31
48
|
case TYPES.string: {
|
32
|
-
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
49
|
+
const length = (new Int32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
|
33
50
|
return Array.from(new Uint16Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
|
34
51
|
}
|
35
52
|
|
36
53
|
case TYPES.bytestring: {
|
37
|
-
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
54
|
+
const length = (new Int32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
|
38
55
|
return Array.from(new Uint8Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
|
39
56
|
}
|
40
57
|
|
41
58
|
case TYPES.array: {
|
42
|
-
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
59
|
+
const length = (new Int32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
|
43
60
|
|
44
61
|
const out = [];
|
45
62
|
for (let i = 0; i < length; i++) {
|
@@ -60,12 +77,12 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
|
60
77
|
}
|
61
78
|
|
62
79
|
case TYPES.date: {
|
63
|
-
const t = (new Float64Array(memory.buffer, value, 1))[0];
|
80
|
+
const t = (new Float64Array(memory.buffer.slice(value, value + 8), 0, 1))[0];
|
64
81
|
return new Date(t);
|
65
82
|
}
|
66
83
|
|
67
84
|
case TYPES.set: {
|
68
|
-
const size = (new Int32Array(memory.buffer, value, 1))[0];
|
85
|
+
const size = (new Int32Array(memory.buffer.slice(value, value + 4), 0, 1))[0];
|
69
86
|
|
70
87
|
const out = new Set();
|
71
88
|
for (let i = 0; i < size; i++) {
|
@@ -85,44 +102,47 @@ const porfToJSValue = ({ memory, funcs, pages }, value, type) => {
|
|
85
102
|
return out;
|
86
103
|
}
|
87
104
|
|
88
|
-
case TYPES.symbol: {
|
89
|
-
|
90
|
-
|
105
|
+
// case TYPES.symbol: {
|
106
|
+
// const descStore = pages.get('bytestring: __Porffor_symbol_descStore/ptr').ind * pageSize;
|
107
|
+
// const offset = descStore + 4 + ((value - 1) * 9);
|
91
108
|
|
92
|
-
|
93
|
-
|
109
|
+
// const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
|
110
|
+
// const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
|
94
111
|
|
95
|
-
|
112
|
+
// const desc = porfToJSValue({ memory, funcs, pages }, v, t);
|
96
113
|
|
97
|
-
|
98
|
-
}
|
114
|
+
// return Symbol(desc);
|
115
|
+
// }
|
99
116
|
|
100
117
|
default: return value;
|
101
118
|
}
|
102
119
|
};
|
103
120
|
|
104
|
-
export default
|
121
|
+
export default (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
105
122
|
const times = [];
|
106
123
|
|
107
124
|
const t1 = performance.now();
|
108
|
-
const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
|
125
|
+
const { wasm, funcs, globals, tags, exceptions, pages, c } = typeof source === 'object' ? source : compile(source, flags);
|
109
126
|
|
110
127
|
globalThis.porfDebugInfo = { funcs, globals };
|
111
128
|
|
112
|
-
if (source.includes('export
|
129
|
+
if (source.includes?.('export ')) flags.push('module');
|
113
130
|
|
114
|
-
//
|
131
|
+
// fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
115
132
|
|
116
133
|
times.push(performance.now() - t1);
|
117
134
|
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
118
135
|
|
119
136
|
const backtrace = (funcInd, blobOffset) => {
|
120
|
-
if (funcInd == null || blobOffset == null
|
137
|
+
if (funcInd == null || blobOffset == null ||
|
138
|
+
Number.isNaN(funcInd) || Number.isNaN(blobOffset)) return false;
|
121
139
|
|
122
140
|
// convert blob offset -> function wasm offset.
|
123
141
|
// this is not good code and is somewhat duplicated
|
124
142
|
// I just want it to work for debugging, I don't care about perf/yes
|
125
143
|
const func = funcs.find(x => x.index === funcInd);
|
144
|
+
if (!func) return false;
|
145
|
+
|
126
146
|
const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
|
127
147
|
|
128
148
|
let localDecl = [], typeCount = 0, lastType;
|
@@ -197,13 +217,15 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
197
217
|
|
198
218
|
let instance;
|
199
219
|
try {
|
200
|
-
let wasmEngine = WebAssembly;
|
201
|
-
if (Prefs.asur) {
|
202
|
-
|
203
|
-
|
204
|
-
}
|
205
|
-
|
206
|
-
0, { instance } = await wasmEngine.instantiate(wasm, {
|
220
|
+
// let wasmEngine = WebAssembly;
|
221
|
+
// if (Prefs.asur) {
|
222
|
+
// log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
|
223
|
+
// wasmEngine = await import('../asur/index.js');
|
224
|
+
// }
|
225
|
+
|
226
|
+
// 0, { instance } = await wasmEngine.instantiate(wasm, {
|
227
|
+
const module = new WebAssembly.Module(wasm);
|
228
|
+
instance = new WebAssembly.Instance(module, {
|
207
229
|
'': {
|
208
230
|
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
209
231
|
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
@@ -211,12 +233,31 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
211
233
|
u: () => performance.timeOrigin,
|
212
234
|
y: () => {},
|
213
235
|
z: () => {},
|
236
|
+
w: (ind, outPtr) => { // readArgv
|
237
|
+
const args = process.argv.slice(2).filter(x => !x.startsWith('-'));
|
238
|
+
const str = args[ind];
|
239
|
+
if (!str) return -1;
|
240
|
+
|
241
|
+
writeByteStr(memory, outPtr, str);
|
242
|
+
return str.length;
|
243
|
+
},
|
244
|
+
q: (pathPtr, outPtr) => { // readFile
|
245
|
+
try {
|
246
|
+
const path = readByteStr(memory, pathPtr);
|
247
|
+
const contents = fs.readFileSync(path, 'utf8');
|
248
|
+
writeByteStr(memory, outPtr, contents);
|
249
|
+
return contents.length;
|
250
|
+
} catch {
|
251
|
+
return -1;
|
252
|
+
}
|
253
|
+
},
|
214
254
|
...customImports
|
215
255
|
}
|
216
256
|
});
|
217
257
|
} catch (e) {
|
218
258
|
// only backtrace for runner, not test262/etc
|
219
259
|
if (!process.argv[1].includes('/runner')) throw e;
|
260
|
+
if (!(e instanceof WebAssembly.CompileError)) throw e;
|
220
261
|
|
221
262
|
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
222
263
|
const blobOffset = parseInt(e.message.split('@')?.[1]);
|