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 4f878fdbab - Show all commits

130
asm.js
View File

@@ -78,7 +78,7 @@ const State = Object.freeze({
LOCAL_NAME: 6, LOCAL_NAME: 6,
LOCAL_TYPE: 7, LOCAL_TYPE: 7,
MEM_NAME: 8, MEM_NAME: 8,
MEM_INITIAL: 9, MEM_INIT: 9,
MEM_MAX: 10, MEM_MAX: 10,
MEM_FLAGS: 11, MEM_FLAGS: 11,
IMPORT_NAME: 12, IMPORT_NAME: 12,
@@ -95,6 +95,8 @@ const State = Object.freeze({
ALIGN: 23, ALIGN: 23,
DEF_NAME: 24, DEF_NAME: 24,
DEF_VALUE: 25, DEF_VALUE: 25,
BLOCK_NAME: 26,
BLOCK_TYPE: 27,
}); });
const Action = Object.freeze({ const Action = Object.freeze({
@@ -113,21 +115,32 @@ const Action = Object.freeze({
ALIGN: 12, ALIGN: 12,
DEF: 13, DEF: 13,
LABEL: 14, LABEL: 14,
ENTER: 16,
EXIT: 17,
ELSE: 18,
}); });
const types = { const types = {
"void": 0x40,
"func": 0x60, "func": 0x60,
"i32": 0x7f, "i32": 0x7f,
}; };
const opcodes = { const opcodes = {
"block": 0x02,
"loop": 0x03,
"if": 0x04,
"else": 0x05,
"end": 0x0b, "end": 0x0b,
"br": 0x0c,
"br_if": 0x0d,
"local.get": 0x20, "local.get": 0x20,
"local.set": 0x21, "local.set": 0x21,
"local.tee": 0x22, "local.tee": 0x22,
"global.get": 0x23, "global.get": 0x23,
"global.set": 0x24, "global.set": 0x24,
"i32.const": 0x41, "i32.const": 0x41,
"i32.gt_u": 0x4b,
"i32.mul": 0x6c, "i32.mul": 0x6c,
}; };
@@ -163,6 +176,7 @@ class Parser {
".align": State.ALIGN, ".align": State.ALIGN,
".def": State.DEF_NAME, ".def": State.DEF_NAME,
}; };
this.blocks = new Set(["block", "loop", "if"]);
this.handlers = { this.handlers = {
[State.TOP]: (token) => this.token_top(token), [State.TOP]: (token) => this.token_top(token),
[State.EXPORT]: (token) => this.token_export(token), [State.EXPORT]: (token) => this.token_export(token),
@@ -190,6 +204,8 @@ class Parser {
[State.ALIGN]: (token) => this.token_align(token), [State.ALIGN]: (token) => this.token_align(token),
[State.DEF_NAME]: (token) => this.token_def_name(token), [State.DEF_NAME]: (token) => this.token_def_name(token),
[State.DEF_VALUE]: (token) => this.token_def_value(token), [State.DEF_VALUE]: (token) => this.token_def_value(token),
[State.BLOCK_NAME]: (token) => this.token_block_name(token),
[State.BLOCK_TYPE]: (token) => this.token_block_type(token),
}; };
this.results = []; this.results = [];
@@ -220,18 +236,34 @@ class Parser {
token_top(token) { token_top(token) {
if (token == LINE_END) if (token == LINE_END)
return; return;
let state; const state = this.directives[token];
if (state = this.directives[token]) { if (state) {
this.state = state; this.state = state;
return; return;
} }
if (token.endsWith(":")) if (token.endsWith(":"))
return { type: Action.LABEL, name: token.slice(0, -1) }; return { type: Action.LABEL, name: token.slice(0, -1) };
const code = this.translate_code(token);
if (code) const opcode = opcodes[token];
return { type: Action.APPEND, code };
else if (this.blocks.has(token)) {
return { type: Action.SYMBOL, symbol: token }; this.state = State.BLOCK_NAME;
this.block = { code: opcode };
return;
}
if (token == "else")
return { type: Action.ELSE }
if (token == "end")
return { type: Action.EXIT };
if (opcode)
return { type: Action.APPEND, opcode };
const literal = this.integer(token);
if (literal)
return { type: Action.APPEND, literal };
return { type: Action.SYMBOL, symbol: token };
} }
token_export(token) { token_export(token) {
@@ -574,6 +606,36 @@ class Parser {
return action; return action;
} }
token_block_name(token) {
if (token == LINE_END) {
this.block.type = types["void"];
const action = { type: Action.ENTER, block: this.block };
this.state = State.TOP;
this.block = undefined;
return action;
} else {
this.block.label = token;
this.state = State.BLOCK_TYPE;
return;
}
}
token_block_type(token) {
if (token == LINE_END) {
this.block.type = types["void"];
} else {
this.block.type = types[token];
if (this.block.type == undefined) {
console.error(
`ERROR: Unexpected token ${token}, expected type`);
}
}
const action = { type: Action.ENTER, block: this.block };
this.state = State.TOP;
this.block = undefined;
return action;
}
mem_action() { mem_action() {
const action = { const action = {
type: Action.MEM, type: Action.MEM,
@@ -588,8 +650,9 @@ class Parser {
*handle(src) { *handle(src) {
let action; let action;
for (const token of this.tokenizer.handle(src)) { for (const token of this.tokenizer.handle(src)) {
if (action = this.handlers[this.state](token)) if (action = this.handlers[this.state](token)) {
yield action; yield action;
}
} }
} }
} }
@@ -631,6 +694,9 @@ export class Assembler {
[Action.ALIGN]: (action) => this.action_align(action), [Action.ALIGN]: (action) => this.action_align(action),
[Action.DEF]: (action) => this.action_def(action), [Action.DEF]: (action) => this.action_def(action),
[Action.LABEL]: (action) => this.action_label(action), [Action.LABEL]: (action) => this.action_label(action),
[Action.ENTER]: (action) => this.action_enter(action),
[Action.EXIT]: (action) => this.action_exit(action),
[Action.ELSE]: (action) => this.action_else(action),
}; };
this.exports = []; this.exports = [];
@@ -641,10 +707,14 @@ export class Assembler {
this.pos = { mem: 0, addr: 0 }; this.pos = { mem: 0, addr: 0 };
this.data = []; this.data = [];
this.defs = {}; this.defs = {};
this.blocks = [];
} }
action_append(action) { action_append(action) {
this.funcs[this.current_func].body.push(action.code); const code = action.opcode != undefined
? [ action.opcode ]
: this.leb128(action.literal);
this.funcs[this.current_func].body.push(...code);
} }
action_export(action) { action_export(action) {
@@ -676,14 +746,14 @@ export class Assembler {
action_symbol(action) { action_symbol(action) {
const func = this.funcs[this.current_func]; const func = this.funcs[this.current_func];
const value = this.lookup_param(func, action.symbol) const value = this.lookup_block(action.symbol)
?? this.lookup_param(func, action.symbol)
?? this.lookup_local(func, action.symbol) ?? this.lookup_local(func, action.symbol)
?? this.lookup_global(action.symbol) ?? this.lookup_global(action.symbol)
?? this.lookup_def(action.symbol); ?? this.lookup_def(action.symbol);
if (value == null) { if (value == null) {
console.error( console.error(
`ERROR: Unable to resolve symbol ${action.symbol}`); `ERROR: Unable to resolve symbol ${action.symbol}`);
value = 0;
} }
func.body.push(value); func.body.push(value);
} }
@@ -748,12 +818,34 @@ export class Assembler {
this.defs[action.name] = this.pos.addr; this.defs[action.name] = this.pos.addr;
} }
action_enter(action) {
this.funcs[this.current_func].body.push(action.block.code);
this.funcs[this.current_func].body.push(action.block.type);
this.blocks.push(action.block.label);
}
action_exit(action) {
this.funcs[this.current_func].body.push(opcodes.end);
this.blocks.pop();
}
action_else(action) {
this.blocks.pop();
this.funcs[this.current_func].body.push(opcodes.else);
this.blocks.push(undefined);
}
push(chunk) { push(chunk) {
const text = this.decoder.decode(chunk, { stream: true }); const text = this.decoder.decode(chunk, { stream: true });
for (const action of this.parser.handle(text)) for (const action of this.parser.handle(text))
this.handlers[action.type](action); this.handlers[action.type](action);
} }
lookup_block(symbol) {
const index = this.blocks.toReversed().indexOf(symbol);
return index == -1 ? null : index;
}
lookup_param(func, symbol) { lookup_param(func, symbol) {
const index = Object.keys(func.params).indexOf(symbol); const index = Object.keys(func.params).indexOf(symbol);
return index == -1 ? null : index; return index == -1 ? null : index;
@@ -921,4 +1013,18 @@ export class Assembler {
else else
return [ flags, init ]; return [ flags, init ];
} }
leb128(x) {
const orig = x;
const bytes = [];
while (true) {
const b = x & 0x7f;
x >>= 7;
if (x == 0 && (b & 0x40) == 0 || x == -1 && (b & 0x40) != 0) {
bytes.push(b);
return bytes;
}
bytes.push(b | 0x80);
}
}
} }