porffor 0.0.0-1b0a5c6 → 0.0.0-3f87ef7
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 +1 -1
- package/compiler/codeGen.js +40 -4
- package/compiler/embedding.js +9 -5
- package/compiler/index.js +2 -2
- package/compiler/sections.js +17 -4
- package/compiler/wrap.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
@@ -132,7 +132,6 @@ no particular order and no guarentees, just what could happen soon™
|
|
132
132
|
- rewrite local indexes per func for smallest local header and remove unused idxs
|
133
133
|
- smarter inline selection (snapshots?)
|
134
134
|
- remove const ifs (`if (true)`, etc)
|
135
|
-
- use data segments for initing arrays
|
136
135
|
|
137
136
|
## porfformance
|
138
137
|
*for the things it supports most of the time*, porffor is blazingly fast compared to most interpreters, and common engines running without JIT. for those with JIT, it is not that much slower like a traditional interpreter would be; mostly the same or a bit faster/slower depending on what.
|
@@ -158,6 +157,7 @@ mostly for reducing size. do not really care about compiler perf/time as long as
|
|
158
157
|
- remove unneeded single just used vars
|
159
158
|
- remove unneeded blocks (no `br`s inside)
|
160
159
|
- remove unused imports
|
160
|
+
- use data segments for initing arrays/strings
|
161
161
|
|
162
162
|
### wasm module
|
163
163
|
- type cache/index (no repeated types)
|
package/compiler/codeGen.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
3
3
|
import { operatorOpcode } from "./expression.js";
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./builtins.js";
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
6
|
-
import { number, i32x4 } from "./embedding.js";
|
6
|
+
import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enforceEightBytes } from "./embedding.js";
|
7
7
|
import parse from "./parse.js";
|
8
8
|
import * as Rhemyn from "../rhemyn/compile.js";
|
9
9
|
|
@@ -1978,10 +1978,25 @@ const StoreOps = {
|
|
1978
1978
|
i16: Opcodes.i32_store16
|
1979
1979
|
};
|
1980
1980
|
|
1981
|
+
let data = [];
|
1982
|
+
|
1983
|
+
const compileBytes = (val, itemType, signed = true) => {
|
1984
|
+
switch (itemType) {
|
1985
|
+
case 'i8': return enforceOneByte(unsignedLEB128(val));
|
1986
|
+
case 'i16': return enforceTwoBytes(unsignedLEB128(val));
|
1987
|
+
case 'i32': return enforceFourBytes(signedLEB128(val));
|
1988
|
+
case 'i64': return enforceEightBytes(signedLEB128(val));
|
1989
|
+
case 'f64': return enforceEightBytes(ieee754_binary64(val));
|
1990
|
+
}
|
1991
|
+
};
|
1992
|
+
|
1981
1993
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
1982
1994
|
const out = [];
|
1983
1995
|
|
1996
|
+
let firstAssign = false;
|
1984
1997
|
if (!arrays.has(name) || name === '$undeclared') {
|
1998
|
+
firstAssign = true;
|
1999
|
+
|
1985
2000
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1986
2001
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1987
2002
|
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
@@ -1992,8 +2007,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1992
2007
|
const useRawElements = !!decl.rawElements;
|
1993
2008
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
1994
2009
|
|
2010
|
+
const valtype = itemTypeToValtype[itemType];
|
1995
2011
|
const length = elements.length;
|
1996
2012
|
|
2013
|
+
if (firstAssign && useRawElements) {
|
2014
|
+
let bytes = compileBytes(length, 'i32');
|
2015
|
+
|
2016
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2017
|
+
if (elements[i] == null) continue;
|
2018
|
+
|
2019
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2020
|
+
}
|
2021
|
+
|
2022
|
+
data.push({
|
2023
|
+
offset: pointer,
|
2024
|
+
bytes
|
2025
|
+
});
|
2026
|
+
|
2027
|
+
// local value as pointer
|
2028
|
+
out.push(...number(pointer));
|
2029
|
+
|
2030
|
+
return [ out, pointer ];
|
2031
|
+
}
|
2032
|
+
|
1997
2033
|
// store length as 0th array
|
1998
2034
|
out.push(
|
1999
2035
|
...number(0, Valtype.i32),
|
@@ -2002,7 +2038,6 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2002
2038
|
);
|
2003
2039
|
|
2004
2040
|
const storeOp = StoreOps[itemType];
|
2005
|
-
const valtype = itemTypeToValtype[itemType];
|
2006
2041
|
|
2007
2042
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2008
2043
|
if (elements[i] == null) continue;
|
@@ -2379,6 +2414,7 @@ export default program => {
|
|
2379
2414
|
typeStates = {};
|
2380
2415
|
arrays = new Map();
|
2381
2416
|
pages = new Map();
|
2417
|
+
data = [];
|
2382
2418
|
currentFuncIndex = importedFuncs.length;
|
2383
2419
|
|
2384
2420
|
globalThis.valtype = 'f64';
|
@@ -2455,5 +2491,5 @@ export default program => {
|
|
2455
2491
|
// if blank main func and other exports, remove it
|
2456
2492
|
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(funcs.length - 1, 1);
|
2457
2493
|
|
2458
|
-
return { funcs, globals, tags, exceptions, pages };
|
2494
|
+
return { funcs, globals, tags, exceptions, pages, data };
|
2459
2495
|
};
|
package/compiler/embedding.js
CHANGED
@@ -9,11 +9,15 @@ export const number = (n, valtype = valtypeBinary) => {
|
|
9
9
|
}
|
10
10
|
};
|
11
11
|
|
12
|
-
const
|
12
|
+
export const enforceOneByte = arr => [ arr[0] ?? 0 ];
|
13
|
+
export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
|
14
|
+
export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
|
15
|
+
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 ];
|
16
|
+
|
13
17
|
export const i32x4 = (a, b, c, d) => [ [
|
14
18
|
...Opcodes.v128_const,
|
15
|
-
...
|
16
|
-
...
|
17
|
-
...
|
18
|
-
...
|
19
|
+
...enforceFourBytes(signedLEB128(a)),
|
20
|
+
...enforceFourBytes(signedLEB128(b)),
|
21
|
+
...enforceFourBytes(signedLEB128(c)),
|
22
|
+
...enforceFourBytes(signedLEB128(d))
|
19
23
|
] ];
|
package/compiler/index.js
CHANGED
@@ -59,7 +59,7 @@ export default (code, flags) => {
|
|
59
59
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
60
60
|
|
61
61
|
const t1 = performance.now();
|
62
|
-
const { funcs, globals, tags, exceptions, pages } = codeGen(program);
|
62
|
+
const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
|
63
63
|
if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
64
64
|
|
65
65
|
if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
|
@@ -71,7 +71,7 @@ export default (code, flags) => {
|
|
71
71
|
if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
|
72
72
|
|
73
73
|
const t3 = performance.now();
|
74
|
-
const sections = produceSections(funcs, globals, tags, pages, flags);
|
74
|
+
const sections = produceSections(funcs, globals, tags, pages, data, flags);
|
75
75
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
76
76
|
|
77
77
|
if (allocLog) {
|
package/compiler/sections.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
|
2
|
-
import { encodeVector, encodeString, encodeLocal } from './encoding.js';
|
2
|
+
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 } from './encoding.js';
|
3
3
|
import { number } from './embedding.js';
|
4
4
|
import { importedFuncs } from './builtins.js';
|
5
5
|
|
@@ -20,7 +20,7 @@ const chHint = (topTier, baselineTier, strategy) => {
|
|
20
20
|
return (strategy | (baselineTier << 2) | (topTier << 4));
|
21
21
|
};
|
22
22
|
|
23
|
-
export default (funcs, globals, tags, pages, flags) => {
|
23
|
+
export default (funcs, globals, tags, pages, data, flags) => {
|
24
24
|
const types = [], typeCache = {};
|
25
25
|
|
26
26
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
@@ -155,13 +155,24 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
155
155
|
encodeVector(types)
|
156
156
|
);
|
157
157
|
|
158
|
+
const dataSection = data.length === 0 ? [] : createSection(
|
159
|
+
Section.data,
|
160
|
+
encodeVector(data.map(x => [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ]))
|
161
|
+
);
|
162
|
+
|
163
|
+
const dataCountSection = data.length === 0 ? [] : createSection(
|
164
|
+
Section.data_count,
|
165
|
+
unsignedLEB128(data.length)
|
166
|
+
);
|
167
|
+
|
158
168
|
if (process.argv.includes('-sections')) console.log({
|
159
169
|
typeSection: typeSection.map(x => x.toString(16)),
|
160
170
|
importSection: importSection.map(x => x.toString(16)),
|
161
171
|
funcSection: funcSection.map(x => x.toString(16)),
|
162
172
|
globalSection: globalSection.map(x => x.toString(16)),
|
163
173
|
exportSection: exportSection.map(x => x.toString(16)),
|
164
|
-
codeSection: codeSection.map(x => x.toString(16))
|
174
|
+
codeSection: codeSection.map(x => x.toString(16)),
|
175
|
+
dataSection: dataSection.map(x => x.toString(16)),
|
165
176
|
});
|
166
177
|
|
167
178
|
return Uint8Array.from([
|
@@ -175,6 +186,8 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
175
186
|
...tagSection,
|
176
187
|
...globalSection,
|
177
188
|
...exportSection,
|
178
|
-
...
|
189
|
+
...dataCountSection,
|
190
|
+
...codeSection,
|
191
|
+
...dataSection
|
179
192
|
]);
|
180
193
|
};
|
package/compiler/wrap.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
|
-
|
3
|
+
import fs from 'node:fs';
|
4
4
|
|
5
5
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
6
6
|
|
@@ -29,7 +29,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
29
29
|
|
30
30
|
if (source.includes('export function')) flags.push('module');
|
31
31
|
|
32
|
-
|
32
|
+
fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
33
33
|
|
34
34
|
times.push(performance.now() - t1);
|
35
35
|
if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
package/package.json
CHANGED