Assemble kernel on the client #1

Merged
cdo merged 72 commits from client-side-assembler into main 2026-03-18 15:21:33 +00:00
Showing only changes of commit 1452ffe615 - Show all commits

145
asm.js
View File

@@ -100,6 +100,10 @@ const State = Object.freeze({
TYPE_NAME: 28,
TYPE_PARAM: 29,
TYPE_RESULT: 30,
TABLE_NAME: 31,
TABLE_SIZE: 32,
ELEM_TABLE: 33,
ELEM_ELEM: 34,
});
const Action = Object.freeze({
@@ -122,6 +126,8 @@ const Action = Object.freeze({
EXIT: 17,
ELSE: 18,
TYPE: 19,
TABLE: 20,
ELEM: 21,
});
const types = {
@@ -214,6 +220,8 @@ class Parser {
".align": State.ALIGN,
".def": State.DEF_NAME,
".type": State.TYPE_NAME,
".table": State.TABLE_NAME,
".elem": State.ELEM_TABLE,
};
this.blocks = new Set(["block", "loop", "if"]);
this.handlers = {
@@ -248,6 +256,10 @@ class Parser {
[State.TYPE_NAME]: (token) => this.token_type_name(token),
[State.TYPE_PARAM]: (token) => this.token_type_param(token),
[State.TYPE_RESULT]: (token) => this.token_type_result(token),
[State.TABLE_NAME]: (token) => this.token_table_name(token),
[State.TABLE_SIZE]: (token) => this.token_table_size(token),
[State.ELEM_TABLE]: (token) => this.token_elem_table(token),
[State.ELEM_ELEM]: (token) => this.token_elem_elem(token),
};
this.results = [];
@@ -740,6 +752,73 @@ class Parser {
this.type.results.push(type);
}
token_table_name(token) {
if (token == LINE_END) {
console.error(
"ERROR: Unexpected end of line in .table, expected name");
this.state = State.TOP;
return;
}
this.table = { name: token };
this.state = State.TABLE_SIZE;
}
token_table_size(token) {
if (token == LINE_END) {
console.error(
"ERROR: Unexpected end of line in .table, expected size");
this.table = undefined;
this.state = State.TOP;
return;
}
const size = this.integer(token);
if (size == null) {
console.error(
`ERROR: Unexpected token ${token} in .table, expected size`);
this.table = undefined;
this.state = State.TOP;
return;
}
this.table.size = size;
const action = { type: Action.TABLE, table: this.table };
this.table = undefined;
this.state = State.TOP;
return action;
}
token_elem_table(token) {
if (token == LINE_END) {
console.error(
"ERROR: Unexpected end of line in .elem, expected "
+ "table name");
this.state = State.TOP;
return;
}
this.elem = { table: token };
this.state = State.ELEM_ELEM;
}
token_elem_elem(token) {
if (token == LINE_END) {
console.error(
"ERROR: Unexpected end of line in .elem, expected "
+ "element name");
this.state = State.TOP;
this.elem = undefined;
return;
}
this.elem.elem = token;
const action = { type: Action.ELEM, elem: this.elem };
this.elem = undefined
this.state = State.TOP;
return action;
}
mem_action() {
const action = {
type: Action.MEM,
@@ -771,9 +850,11 @@ const Section = Object.freeze({
TYPE: 0x01,
IMPORT: 0x02,
FUNC: 0x03,
TABLE: 0x04,
MEM: 0x05,
GLOBAL: 0x06,
EXPORT: 0x07,
ELEM: 0x09,
CODE: 0x0a,
DATA: 0x0b,
});
@@ -808,6 +889,8 @@ export class Assembler {
[Action.EXIT]: (action) => this.action_exit(action),
[Action.ELSE]: (action) => this.action_else(action),
[Action.TYPE]: (action) => this.action_type(action),
[Action.TABLE]: (action) => this.action_table(action),
[Action.ELEM]: (action) => this.action_elem(action),
};
this.exports = [];
@@ -821,6 +904,7 @@ export class Assembler {
this.blocks = [];
this.types = [];
this.type_bindings = {};
this.tables = {};
}
action_append(action) {
@@ -959,6 +1043,23 @@ export class Assembler {
this.type_bindings[action.the_type.name] = index;
}
action_table(action) {
this.tables[action.table.name] = {
size: action.table.size,
elems: [],
};
}
action_elem(action) {
const table = this.tables[action.elem.table];
const index = Object.keys(this.funcs).indexOf(action.elem.elem);
if (index == -1) {
console.error(`ERROR: ${action.elem.elem}: no such function`);
return;
}
table.elems.push(index);
}
push(chunk) {
const text = this.decoder.decode(chunk, { stream: true });
for (const action of this.parser.handle(text)) {
@@ -1009,7 +1110,6 @@ export class Assembler {
}
leb128(x) {
const orig = x;
const bytes = [];
while (true) {
const b = x & 0x7f;
@@ -1022,6 +1122,18 @@ export class Assembler {
}
}
uleb128(x) {
const bytes = [];
do {
const b = x & 0x7f;
x >>= 7;
if (x != 0)
b |= 0x80;
bytes.push(b);
} while (x != 0);
return bytes;
}
wasm_section_type() {
if (this.types.length == 0) return null;
return [ this.types.length ].concat(...this.types);
@@ -1052,6 +1164,17 @@ export class Assembler {
return [ count, ...types ];
}
wasm_section_table() {
const sizes = Object.values(this.tables).map(({size}) => size);
if (sizes.length == 0) return null;
const contents = sizes.map((size) => [
types.funcref,
0x00,
this.uleb128(size),
]);
return [ sizes.length, contents ].flat(Infinity)
}
wasm_section_mem() {
const mems = Object.values(this.mems).filter(
({imported}) => !imported);
@@ -1085,6 +1208,24 @@ export class Assembler {
return [ exports.length ].concat(...contents);
}
wasm_section_elem() {
const table_elems = Object.values(this.tables).map(
({elems}) => elems);
const count = table_elems.flat().length;
if (count == 0) return null;
const contents = table_elems.flatMap((elems, table) =>
elems.flatMap((fn, index) => [
table == 0 ? 0 : [ 2, table ],
opcodes["i32.const"],
index,
opcodes["end"],
1,
fn,
])
);
return [ count, contents ].flat();
}
wasm_section_code() {
const funcs = Object.values(this.funcs);
if (funcs.length == 0) return null;
@@ -1134,9 +1275,11 @@ export class Assembler {
[ Section.TYPE, () => this.wasm_section_type() ],
[ Section.IMPORT, () => this.wasm_section_import() ],
[ Section.FUNC, () => this.wasm_section_func() ],
[ Section.TABLE, () => this.wasm_section_table() ],
[ Section.MEM, () => this.wasm_section_mem() ],
[ Section.GLOBAL, () => this.wasm_section_global() ],
[ Section.EXPORT, () => this.wasm_section_export() ],
[ Section.ELEM, () => this.wasm_section_elem() ],
[ Section.CODE, () => this.wasm_section_code() ],
[ Section.DATA, () => this.wasm_section_data() ],
];