Assemble kernel on the client #1
121
asm.js
121
asm.js
@@ -47,14 +47,18 @@ class Tokenizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const State = Object.freeze({
|
const State = Object.freeze({
|
||||||
TOP: 0,
|
TOP: 0,
|
||||||
EXPORT: 1,
|
EXPORT: 1,
|
||||||
FUNC: 2,
|
FUNC: 2,
|
||||||
RESULT: 3,
|
RESULT: 3,
|
||||||
PARAM_NAME: 4,
|
PARAM_NAME: 4,
|
||||||
PARAM_TYPE: 5,
|
PARAM_TYPE: 5,
|
||||||
LOCAL_NAME: 6,
|
LOCAL_NAME: 6,
|
||||||
LOCAL_TYPE: 7,
|
LOCAL_TYPE: 7,
|
||||||
|
MEM_NAME: 8,
|
||||||
|
MEM_INITIAL: 9,
|
||||||
|
MEM_MAX: 10,
|
||||||
|
MEM_FLAGS: 11,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Action = Object.freeze({
|
const Action = Object.freeze({
|
||||||
@@ -65,6 +69,7 @@ const Action = Object.freeze({
|
|||||||
PARAM: 4,
|
PARAM: 4,
|
||||||
SYMBOL: 5,
|
SYMBOL: 5,
|
||||||
LOCAL: 6,
|
LOCAL: 6,
|
||||||
|
MEM: 7,
|
||||||
});
|
});
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
@@ -81,6 +86,12 @@ const opcodes = {
|
|||||||
"i32.mul": 0x6c,
|
"i32.mul": 0x6c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mem_flags = {
|
||||||
|
"max": 1,
|
||||||
|
"shared": 2,
|
||||||
|
"64": 4,
|
||||||
|
};
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tokens = [];
|
this.tokens = [];
|
||||||
@@ -92,6 +103,7 @@ class Parser {
|
|||||||
".result": State.RESULT,
|
".result": State.RESULT,
|
||||||
".param": State.PARAM_NAME,
|
".param": State.PARAM_NAME,
|
||||||
".local": State.LOCAL_NAME,
|
".local": State.LOCAL_NAME,
|
||||||
|
".mem": State.MEM_NAME,
|
||||||
};
|
};
|
||||||
this.handlers = {
|
this.handlers = {
|
||||||
[State.TOP]: (token) => this.token_top(token),
|
[State.TOP]: (token) => this.token_top(token),
|
||||||
@@ -102,6 +114,10 @@ class Parser {
|
|||||||
[State.PARAM_TYPE]: (token) => this.token_param_type(token),
|
[State.PARAM_TYPE]: (token) => this.token_param_type(token),
|
||||||
[State.LOCAL_NAME]: (token) => this.token_local_name(token),
|
[State.LOCAL_NAME]: (token) => this.token_local_name(token),
|
||||||
[State.LOCAL_TYPE]: (token) => this.token_local_type(token),
|
[State.LOCAL_TYPE]: (token) => this.token_local_type(token),
|
||||||
|
[State.MEM_NAME]: (token) => this.token_mem_name(token),
|
||||||
|
[State.MEM_INIT]: (token) => this.token_mem_init(token),
|
||||||
|
[State.MEM_MAX]: (token) => this.token_mem_max(token),
|
||||||
|
[State.MEM_FLAGS]: (token) => this.token_mem_flags(token),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.results = [];
|
this.results = [];
|
||||||
@@ -215,6 +231,67 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token_mem_name(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected newline in .mem: expected name");
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.mem = { flags: 0 };
|
||||||
|
this.mem_name = token;
|
||||||
|
this.state = State.MEM_INIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_mem_init(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected newline in .mem: expected initial size");
|
||||||
|
this.mem = undefined;
|
||||||
|
this.mem_name = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.mem.init = this.integer(token) ?? console.error(
|
||||||
|
`ERROR: Invalid initial size {token} in .mem`);
|
||||||
|
this.state = State.MEM_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_mem_max(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
return this.mem_action();
|
||||||
|
} else {
|
||||||
|
this.mem.max = this.integer(token) ?? console.error(
|
||||||
|
`ERROR: Invalid maximum size {token} in .mem`);
|
||||||
|
this.mem.flags |= mem_flags.max;
|
||||||
|
this.state = State.MEM_FLAGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_mem_flags(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
return this.mem_action();
|
||||||
|
} else {
|
||||||
|
for (const flag of token.split(",")) {
|
||||||
|
this.mem.flags |= mem_flags[flag] ?? console.error(
|
||||||
|
`ERROR: Invalid flag {flag} in .mem`);
|
||||||
|
}
|
||||||
|
this.state = State.TOP;
|
||||||
|
return this.mem_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_action() {
|
||||||
|
const action = {
|
||||||
|
type: Action.MEM,
|
||||||
|
mem: { [this.mem_name]: { ...this.mem } }
|
||||||
|
};
|
||||||
|
this.mem = undefined;
|
||||||
|
this.mem_name = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
*handle(src) {
|
*handle(src) {
|
||||||
let action;
|
let action;
|
||||||
for (const token of this.tokenizer.handle(src)) {
|
for (const token of this.tokenizer.handle(src)) {
|
||||||
@@ -227,8 +304,9 @@ class Parser {
|
|||||||
const Section = Object.freeze({
|
const Section = Object.freeze({
|
||||||
TYPE: 0x01,
|
TYPE: 0x01,
|
||||||
FUNC: 0x03,
|
FUNC: 0x03,
|
||||||
|
MEM: 0x05,
|
||||||
EXPORT: 0x07,
|
EXPORT: 0x07,
|
||||||
CODE: 0x0a
|
CODE: 0x0a,
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Assembler {
|
export class Assembler {
|
||||||
@@ -244,10 +322,12 @@ export class Assembler {
|
|||||||
[Action.PARAM]: (action) => this.action_param(action),
|
[Action.PARAM]: (action) => this.action_param(action),
|
||||||
[Action.SYMBOL]: (action) => this.action_symbol(action),
|
[Action.SYMBOL]: (action) => this.action_symbol(action),
|
||||||
[Action.LOCAL]: (action) => this.action_local(action),
|
[Action.LOCAL]: (action) => this.action_local(action),
|
||||||
|
[Action.MEM]: (action) => this.action_mem(action),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.exports = [];
|
this.exports = [];
|
||||||
this.funcs = {}
|
this.funcs = {};
|
||||||
|
this.mems = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
action_append(action) {
|
action_append(action) {
|
||||||
@@ -288,6 +368,10 @@ export class Assembler {
|
|||||||
func.body.push(index);
|
func.body.push(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_mem(action) {
|
||||||
|
Object.assign(this.mems, action.mem);
|
||||||
|
}
|
||||||
|
|
||||||
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))
|
||||||
@@ -325,6 +409,20 @@ export class Assembler {
|
|||||||
return [ func_count, ...Array(func_count).keys() ];
|
return [ func_count, ...Array(func_count).keys() ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wasm_section_mem() {
|
||||||
|
const mems = Object.values(this.mems);
|
||||||
|
if (mems.length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const contents = mems.map(({ init, max, flags }) => {
|
||||||
|
if (flags & mem_flags.max)
|
||||||
|
return [ flags, init, max ];
|
||||||
|
else
|
||||||
|
return [ flags, init ];
|
||||||
|
});
|
||||||
|
return [ mems.length ].concat(...contents);
|
||||||
|
}
|
||||||
|
|
||||||
wasm_section_export() {
|
wasm_section_export() {
|
||||||
const exports = Object.entries(this.exports);
|
const exports = Object.entries(this.exports);
|
||||||
const contents = exports.map(([ name, { kind, index }]) => {
|
const contents = exports.map(([ name, { kind, index }]) => {
|
||||||
@@ -360,12 +458,13 @@ export class Assembler {
|
|||||||
const template = [
|
const template = [
|
||||||
[ Section.TYPE, () => this.wasm_section_type() ],
|
[ Section.TYPE, () => this.wasm_section_type() ],
|
||||||
[ Section.FUNC, () => this.wasm_section_func() ],
|
[ Section.FUNC, () => this.wasm_section_func() ],
|
||||||
|
[ Section.MEM, () => this.wasm_section_mem() ],
|
||||||
[ Section.EXPORT, () => this.wasm_section_export() ],
|
[ Section.EXPORT, () => this.wasm_section_export() ],
|
||||||
[ Section.CODE, () => this.wasm_section_code() ],
|
[ Section.CODE, () => this.wasm_section_code() ],
|
||||||
];
|
];
|
||||||
const sections = template.map(([ code, generator ]) => {
|
const sections = template.map(([ code, generator ]) => {
|
||||||
const body = generator();
|
const body = generator();
|
||||||
return [ code, body.length, ...body ];
|
return body == null ? [] : [ code, body.length, ...body ];
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Uint8Array(HEADER.concat(...sections));
|
return new Uint8Array(HEADER.concat(...sections));
|
||||||
|
|||||||
Reference in New Issue
Block a user