Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add run-wasm and --include-all-builtins #188

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
46 changes: 45 additions & 1 deletion compiler/assemble.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,48 @@ const encodeNames = funcs => {
];
};

export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) => {
const encodeDebugInfo = (funcs, globals, tags, exceptions, pages, data, excMode) => {
const encodeSection = (id, section) => [
id,
...unsignedLEB128(section.length),
...section
];
let exceptionSection = [];
switch (excMode) {
case 'lut':
exceptionSection.push(0);
unsignedLEB128_into(exceptions.length, exceptionSection);
for (let i = 0; i < exceptions.length; i++) {
unsignedLEB128_into(i, exceptionSection);
exceptionSection.push(...encodeString(exceptions[i].constructor ?? ""));
exceptionSection.push(...encodeString(exceptions[i].message));
}
break;
case 'stack':
exceptionSection.push(1);
break;
case 'stackest':
exceptionSection.push(2);
break;
case 'partial':
exceptionSection.push(3);
unsignedLEB128_into(exceptions.length, exceptionSection);
for (let i = 0; i < exceptions.length; i++) {
unsignedLEB128_into(i, exceptionSection);
exceptionSection.push(...encodeString(exceptions[i].constructor ?? ""));
}
break;
}
const typeSection = [ valtypeBinary ];

// TODO: add more information like where funcrefs are, etc.
return [
...encodeSection(0, exceptionSection),
...encodeSection(1, typeSection),
];
};

export default (funcs, globals, tags, exceptions, pages, data, flags, noTreeshake = false) => {
Rob23oba marked this conversation as resolved.
Show resolved Hide resolved
const types = [], typeCache = {};

const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
Expand Down Expand Up @@ -121,6 +162,8 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =

const nameSection = Prefs.d ? customSection('name', encodeNames(funcs)) : [];

const porfforDebugInfoSection = Prefs.d ? customSection('porffor_debug_info', encodeDebugInfo(funcs, globals, tags, exceptions, pages, data, Prefs.exceptionMode ?? 'lut')) : [];

const tableSection = !funcs.table ? [] : createSection(
Section.table,
encodeVector([ [ Reftype.funcref, 0x00, ...unsignedLEB128(funcs.length) ] ])
Expand Down Expand Up @@ -424,6 +467,7 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
...exportSection,
...elementSection,
...dataCountSection,
...porfforDebugInfoSection,
...codeSection,
...dataSection,
...nameSection
Expand Down
31 changes: 22 additions & 9 deletions compiler/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -1023,10 +1023,6 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
}

for (const x in _data) {
data.push({ page: x, bytes: _data[x] });
}

const func = {
name,
params,
Expand All @@ -1042,6 +1038,11 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
globalInits
};

for (const x in _data) {
allocPage(func, x);
data.push({ page: x, bytes: _data[x] });
}

funcs.push(func);
funcIndex[name] = func.index;

Expand All @@ -1050,19 +1051,26 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
else wasm = asmFuncToAsm(func, wasm);
}

let baseGlobalIdx, i = 0;
let globalRefs = [], i = 0;
for (const type of globalTypes) {
if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];

globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
let nm = globalNames[i] ?? `${name}_global_${i}`;
if (globals[nm]) {
globalRefs.push(globals[nm].idx);
continue;
}
globalRefs.push(globals['#ind']);
globals[nm] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
i++;
}

if (globalTypes.length !== 0) {
// offset global ops for base global idx
for (const inst of wasm) {
if (inst[0] === Opcodes.global_get || inst[0] === Opcodes.global_set) {
inst[1] += baseGlobalIdx;
if (inst[1] >= globalRefs.length) {
throw new Error('in builtin ' + name + ': global_get / global_set out of range');
}
inst[1] = globalRefs[inst[1]];
}
}
}
Expand Down Expand Up @@ -5944,6 +5952,11 @@ const generateFunc = (scope, decl) => {
}

const wasm = prelude.concat(generate(func, body));
if (Prefs.includeAllBuiltins) {
for (let x in builtinFuncs) {
includeBuiltin(func, x);
}
}

if (decl.async) {
// make promise at the start
Expand Down
21 changes: 21 additions & 0 deletions compiler/encoding.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,25 @@ export const ieee754_binary64_into = (value, buffer) => {
const data = new Uint8Array(new Float64Array([ value ]).buffer);
for (let i = 0; i < 8; i++) buffer.push(data[i]);
// buffer.push(...new Uint8Array(new Float64Array([ value ]).buffer));
};

export const readUnsignedLEB128FromBuffer = (buffer, index) => {
let value = 0;
let shift = 0;
while (true) {
let b = buffer[index];
value |= (b & 0x7F) << shift;
if ((value & 0x80) == 0) {
return [ value, index + 1 ];
}
shift += 7;
}
};

export const readStringFromBuffer = (buffer, index) => {
let length;
[ length, index ] = readUnsignedLEB128FromBuffer(buffer, index);
let slice = buffer.subarray(index, index + length);
let decoder = new TextDecoder();
return [ decoder.decode(slice), index + length ];
};
19 changes: 14 additions & 5 deletions compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ export default (code, flags) => {

if (Prefs.pgo) {
if (Prefs.pgoLog) {
const oldSize = assemble(funcs, globals, tags, pages, data, flags, true).byteLength;
const oldSize = assemble(funcs, globals, tags, exceptions, pages, data, flags, true).byteLength;
const t = performance.now();

pgo.run({ funcs, globals, tags, exceptions, pages, data });
opt(funcs, globals, pages, tags, exceptions);

console.log(`PGO total time: ${(performance.now() - t).toFixed(2)}ms`);

const newSize = assemble(funcs, globals, tags, pages, data, flags, true).byteLength;
const newSize = assemble(funcs, globals, tags, exceptions, pages, data, flags, true).byteLength;
console.log(`PGO size diff: ${oldSize - newSize} bytes (${oldSize} -> ${newSize})\n`);
} else {
pgo.run({ funcs, globals, tags, exceptions, pages, data });
Expand All @@ -116,7 +116,7 @@ export default (code, flags) => {

if (Prefs.cyclone) {
if (Prefs.cycloneLog) {
const oldSize = assemble(funcs, globals, tags, pages, data, flags, true).byteLength;
const oldSize = assemble(funcs, globals, tags, exceptions, pages, data, flags, true).byteLength;
const t = performance.now();

for (const x of funcs) {
Expand All @@ -129,7 +129,7 @@ export default (code, flags) => {

console.log(`cyclone total time: ${(performance.now() - t).toFixed(2)}ms`);

const newSize = assemble(funcs, globals, tags, pages, data, flags, true).byteLength;
const newSize = assemble(funcs, globals, tags, exceptions, pages, data, flags, true).byteLength;
console.log(`cyclone size diff: ${oldSize - newSize} bytes (${oldSize} -> ${newSize})\n`);
} else {
for (const x of funcs) {
Expand Down Expand Up @@ -171,11 +171,20 @@ export default (code, flags) => {
const out = { funcs, globals, tags, exceptions, pages, data, times: [ t0, t1, t2, t3 ] };
if (globalThis.precompile) return out;

const wasm = out.wasm = assemble(funcs, globals, tags, pages, data, flags);
const wasm = out.wasm = assemble(funcs, globals, tags, exceptions, pages, data, flags);
if (logProgress) progressDone('assembled', t3);

if (Prefs.optFuncs || Prefs.f) logFuncs(funcs, globals, exceptions);

if (Prefs.sizeLog) {
console.log('Functions ordered by size:');
const sortedFuncs = funcs.toSorted((x, y) => y.wasm.length - x.wasm.length);
const toShow = Math.min(funcs.length, Prefs.sizeLogCount ?? 10);
for (let i = 0; i < toShow; i++) {
console.log((i + 1) + '. ' + sortedFuncs[i].name + ' with ' + sortedFuncs[i].wasm.length + ' instructions');
}
}

if (Prefs.compileAllocLog) {
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
const bytes = wasmPages * 65536;
Expand Down
2 changes: 1 addition & 1 deletion compiler/pgo.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const run = obj => {

let activeFunc = null, abort = false;
try {
obj.wasm = assemble(obj.funcs, obj.globals, obj.tags, obj.pages, obj.data, obj.flags, true);
obj.wasm = assemble(obj.funcs, obj.globals, obj.tags, obj.exceptions, obj.pages, obj.data, obj.flags, true);

Prefs._profileCompiler = Prefs.profileCompiler;
Prefs.profileCompiler = false;
Expand Down
4 changes: 2 additions & 2 deletions compiler/precompile.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const compile = async (file, _funcs) => {
let first = source.slice(0, source.indexOf('\n'));

if (first.startsWith('export default')) {
source = await (await import(file)).default();
source = await (await import('file://' + file)).default();
first = source.slice(0, source.indexOf('\n'));
}

Expand Down Expand Up @@ -178,7 +178,7 @@ const precompile = async () => {

let funcs = [];
let fileCount = 0;
for (const file of fs.readdirSync(dir)) {
for (const file of fs.readdirSync(dir).toSorted()) {
if (file.endsWith('.d.ts')) continue;
fileCount++;

Expand Down
Loading