Compare commits
5 Commits
77f6d57e1b
...
5369a0969e
| Author | SHA1 | Date | |
|---|---|---|---|
|
5369a0969e
|
|||
|
118e6af896
|
|||
|
1c4b9f850a
|
|||
|
672a453f6c
|
|||
|
5a3084dd16
|
335
asm.js
335
asm.js
@@ -9,9 +9,11 @@ class Tokenizer {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.delims = new Set([" ", "\r", "\n", "\t"]);
|
this.delims = new Set([" ", "\r", "\n", "\t"]);
|
||||||
this.skips = new Set([" ", "\r", "\t"]);
|
this.skips = new Set([" ", "\r", "\t"]);
|
||||||
this.comment_start = ";"
|
this.comment_start = ";";
|
||||||
|
this.string_quote = '"';
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
this.comment = false;
|
this.comment = false;
|
||||||
|
this.string = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip() {
|
skip() {
|
||||||
@@ -19,11 +21,31 @@ class Tokenizer {
|
|||||||
this.buffer = idx == -1 ? [] : this.buffer.slice(idx);
|
this.buffer = idx == -1 ? [] : this.buffer.slice(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_string() {
|
||||||
|
const idx = this.buffer.findIndex((cp) => cp == this.string_quote);
|
||||||
|
if (idx == -1) {
|
||||||
|
this.string = true;
|
||||||
|
} else {
|
||||||
|
const string = this.buffer.slice(0, idx).join("");
|
||||||
|
this.buffer = this.buffer.slice(idx + 1);
|
||||||
|
this.string = false;
|
||||||
|
return { string: string };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
|
if (this.string)
|
||||||
|
return this.next_string();
|
||||||
|
|
||||||
this.skip();
|
this.skip();
|
||||||
if (this.buffer[0] == LINE_END)
|
if (this.buffer[0] == LINE_END)
|
||||||
return this.buffer.shift();
|
return this.buffer.shift();
|
||||||
|
|
||||||
|
if (this.buffer[0] == this.string_quote) {
|
||||||
|
this.buffer.shift();
|
||||||
|
return this.next_string();
|
||||||
|
}
|
||||||
|
|
||||||
const idx = this.buffer.findIndex((cp) => this.delims.has(cp));
|
const idx = this.buffer.findIndex((cp) => this.delims.has(cp));
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
const token = this.buffer.slice(0, idx).join("");
|
const token = this.buffer.slice(0, idx).join("");
|
||||||
@@ -55,6 +77,16 @@ const State = Object.freeze({
|
|||||||
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,
|
||||||
|
IMPORT_NAME: 12,
|
||||||
|
IMPORT_MOD: 13,
|
||||||
|
IMPORT_FIELD: 14,
|
||||||
|
GLOBAL_NAME: 15,
|
||||||
|
GLOBAL_TYPE: 16,
|
||||||
|
GLOBAL_INIT: 17,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Action = Object.freeze({
|
const Action = Object.freeze({
|
||||||
@@ -65,6 +97,9 @@ const Action = Object.freeze({
|
|||||||
PARAM: 4,
|
PARAM: 4,
|
||||||
SYMBOL: 5,
|
SYMBOL: 5,
|
||||||
LOCAL: 6,
|
LOCAL: 6,
|
||||||
|
MEM: 7,
|
||||||
|
IMPORT: 8,
|
||||||
|
GLOBAL: 9,
|
||||||
});
|
});
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
@@ -77,10 +112,22 @@ const opcodes = {
|
|||||||
"local.get": 0x20,
|
"local.get": 0x20,
|
||||||
"local.set": 0x21,
|
"local.set": 0x21,
|
||||||
"local.tee": 0x22,
|
"local.tee": 0x22,
|
||||||
|
"global.get": 0x23,
|
||||||
|
"global.set": 0x24,
|
||||||
"i32.const": 0x41,
|
"i32.const": 0x41,
|
||||||
"i32.mul": 0x6c,
|
"i32.mul": 0x6c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mem_flags = {
|
||||||
|
"max": 1,
|
||||||
|
"shared": 2,
|
||||||
|
"64": 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
const const_opcodes = {
|
||||||
|
[types["i32"]]: opcodes["i32.const"],
|
||||||
|
};
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tokens = [];
|
this.tokens = [];
|
||||||
@@ -92,6 +139,9 @@ 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,
|
||||||
|
".import": State.IMPORT_NAME,
|
||||||
|
".global": State.GLOBAL_NAME,
|
||||||
};
|
};
|
||||||
this.handlers = {
|
this.handlers = {
|
||||||
[State.TOP]: (token) => this.token_top(token),
|
[State.TOP]: (token) => this.token_top(token),
|
||||||
@@ -102,6 +152,16 @@ 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),
|
||||||
|
[State.IMPORT_NAME]: (token) => this.token_import_name(token),
|
||||||
|
[State.IMPORT_MOD]: (token) => this.token_import_mod(token),
|
||||||
|
[State.IMPORT_FIELD]: (token) => this.token_import_field(token),
|
||||||
|
[State.GLOBAL_NAME]: (token) => this.token_global_name(token),
|
||||||
|
[State.GLOBAL_TYPE]: (token) => this.token_global_type(token),
|
||||||
|
[State.GLOBAL_INIT]: (token) => this.token_global_init(token),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.results = [];
|
this.results = [];
|
||||||
@@ -215,6 +275,167 @@ 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_import_name(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected end of line in .import: expected name");
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.import = { name: token };
|
||||||
|
this.state = State.IMPORT_MOD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_import_mod(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected end of line in .import: expected name");
|
||||||
|
this.import = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else if (token.string == undefined) {
|
||||||
|
console.error(
|
||||||
|
`ERROR: Unexpected token {token} in .import: expected`
|
||||||
|
+ " module string");
|
||||||
|
this.import = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.import.mod = token.string;
|
||||||
|
this.state = State.IMPORT_FIELD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_import_field(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected end of line in .import: expected name");
|
||||||
|
this.import = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else if (token.string == undefined) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected token in .import: expected field string");
|
||||||
|
this.import = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.import.field = token.string;
|
||||||
|
const action = { type: Action.IMPORT, import: this.import };
|
||||||
|
this.import = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_global_name(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected end of line in .global: expected name");
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.global = {};
|
||||||
|
this.global_name = token;
|
||||||
|
this.state = State.GLOBAL_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_global_type(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected newline in .global: expected type");
|
||||||
|
this.global = undefined;
|
||||||
|
this.global_name = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
this.global.type = types[token] ?? console.error(
|
||||||
|
`ERROR: Unexpected token {token} in .global: expected type`);
|
||||||
|
this.state = State.GLOBAL_INIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_global_init(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
console.error(
|
||||||
|
"ERROR: Unexpected newline in .global: expected"
|
||||||
|
+ " initial value");
|
||||||
|
this.global = undefined;
|
||||||
|
this.global_name = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
} else {
|
||||||
|
const value = this.integer(token) ?? console.error(
|
||||||
|
`ERROR: Unexpected token {token} in .global: expected`
|
||||||
|
+ " initial value");
|
||||||
|
const const_opcode = const_opcodes[this.global.type];
|
||||||
|
this.global.init = [ const_opcode, value, opcodes["end"] ];
|
||||||
|
const action = {
|
||||||
|
type: Action.GLOBAL,
|
||||||
|
global: { [this.global_name]: this.global }
|
||||||
|
};
|
||||||
|
this.global = undefined;
|
||||||
|
this.global_name = undefined;
|
||||||
|
this.state = State.TOP;
|
||||||
|
return 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)) {
|
||||||
@@ -226,9 +447,17 @@ class Parser {
|
|||||||
|
|
||||||
const Section = Object.freeze({
|
const Section = Object.freeze({
|
||||||
TYPE: 0x01,
|
TYPE: 0x01,
|
||||||
|
IMPORT: 0x02,
|
||||||
FUNC: 0x03,
|
FUNC: 0x03,
|
||||||
|
MEM: 0x05,
|
||||||
|
GLOBAL: 0x06,
|
||||||
EXPORT: 0x07,
|
EXPORT: 0x07,
|
||||||
CODE: 0x0a
|
CODE: 0x0a,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Kind = Object.freeze({
|
||||||
|
FUNC: 0x00,
|
||||||
|
MEM: 0x02,
|
||||||
});
|
});
|
||||||
|
|
||||||
export class Assembler {
|
export class Assembler {
|
||||||
@@ -244,10 +473,16 @@ 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),
|
||||||
|
[Action.IMPORT]: (action) => this.action_import(action),
|
||||||
|
[Action.GLOBAL]: (action) => this.action_global(action),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.exports = [];
|
this.exports = [];
|
||||||
this.funcs = {}
|
this.funcs = {};
|
||||||
|
this.mems = {};
|
||||||
|
this.imports = [];
|
||||||
|
this.globals = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
action_append(action) {
|
action_append(action) {
|
||||||
@@ -256,7 +491,7 @@ export class Assembler {
|
|||||||
|
|
||||||
action_export(action) {
|
action_export(action) {
|
||||||
const index = Object.keys(this.funcs).indexOf(action.name);
|
const index = Object.keys(this.funcs).indexOf(action.name);
|
||||||
this.exports[action.name] = { kind: 0x00, index };
|
this.exports[action.name] = { kind: Kind.FUNC, index };
|
||||||
}
|
}
|
||||||
|
|
||||||
action_func(action) {
|
action_func(action) {
|
||||||
@@ -284,10 +519,36 @@ export class Assembler {
|
|||||||
action_symbol(action) {
|
action_symbol(action) {
|
||||||
const func = this.funcs[this.current_func];
|
const func = this.funcs[this.current_func];
|
||||||
const index = this.lookup_param(func, action.symbol)
|
const index = this.lookup_param(func, action.symbol)
|
||||||
?? this.lookup_local(func, action.symbol);
|
?? this.lookup_local(func, action.symbol)
|
||||||
|
?? this.lookup_global(action.symbol);
|
||||||
|
if (index == null) {
|
||||||
|
console.error(`ERROR: Unable to resolve symbol {action.symbol}`);
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
func.body.push(index);
|
func.body.push(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_mem(action) {
|
||||||
|
Object.assign(this.mems, action.mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
action_import(action) {
|
||||||
|
const mem = this.mems[action.import.name];
|
||||||
|
mem.imported = true;
|
||||||
|
this.imports.push({
|
||||||
|
mod: action.import.mod,
|
||||||
|
field: action.import.field,
|
||||||
|
kind: Kind.MEM,
|
||||||
|
flags: mem.flags,
|
||||||
|
init: mem.init,
|
||||||
|
max: mem.max,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
action_global(action) {
|
||||||
|
Object.assign(this.globals, action.global);
|
||||||
|
}
|
||||||
|
|
||||||
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))
|
||||||
@@ -305,6 +566,11 @@ export class Assembler {
|
|||||||
return index == -1 ? null : index;
|
return index == -1 ? null : index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookup_global(symbol) {
|
||||||
|
const index = Object.keys(this.globals).indexOf(symbol);
|
||||||
|
return index == -1 ? null : index;
|
||||||
|
}
|
||||||
|
|
||||||
wasm_section_type() {
|
wasm_section_type() {
|
||||||
const funcs = Object.values(this.funcs);
|
const funcs = Object.values(this.funcs);
|
||||||
const contents = funcs.map(({ params, results }) => {
|
const contents = funcs.map(({ params, results }) => {
|
||||||
@@ -320,11 +586,47 @@ export class Assembler {
|
|||||||
return [ contents.length ].concat(...contents);
|
return [ contents.length ].concat(...contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wasm_section_import() {
|
||||||
|
if (this.imports.length == 0)
|
||||||
|
return null;
|
||||||
|
const contents = this.imports.map((imp) => {
|
||||||
|
const mod_utf8 = this.encoder.encode(imp.mod);
|
||||||
|
const field_utf8 = this.encoder.encode(imp.field);
|
||||||
|
return [
|
||||||
|
mod_utf8.length,
|
||||||
|
...mod_utf8,
|
||||||
|
field_utf8.length,
|
||||||
|
...field_utf8,
|
||||||
|
imp.kind,
|
||||||
|
...this.mem_wasm(imp),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return [ this.imports.length ].concat(...contents);
|
||||||
|
}
|
||||||
|
|
||||||
wasm_section_func() {
|
wasm_section_func() {
|
||||||
const func_count = Object.entries(this.funcs).length;
|
const func_count = Object.entries(this.funcs).length;
|
||||||
return [ func_count, ...Array(func_count).keys() ];
|
return [ func_count, ...Array(func_count).keys() ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wasm_section_mem() {
|
||||||
|
const mems = Object.values(this.mems).filter(
|
||||||
|
({imported}) => !imported);
|
||||||
|
if (mems.length == 0)
|
||||||
|
return null;
|
||||||
|
const contents = mems.map((mem) => this.mem_wasm(mem));
|
||||||
|
return [ mems.length ].concat(...contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_section_global() {
|
||||||
|
const globals = Object.values(this.globals);
|
||||||
|
if (globals.length == 0)
|
||||||
|
return null;
|
||||||
|
const contents = globals.map(
|
||||||
|
({ type, init }) => [ type, 1, ...init ]);
|
||||||
|
return [ globals.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 }]) => {
|
||||||
@@ -344,6 +646,14 @@ export class Assembler {
|
|||||||
const contents = funcs.map(({ body, locals }) => {
|
const contents = funcs.map(({ body, locals }) => {
|
||||||
const local_types = Object.values(locals);
|
const local_types = Object.values(locals);
|
||||||
const local_count = local_types.length;
|
const local_count = local_types.length;
|
||||||
|
if (local_count == 0) {
|
||||||
|
return [
|
||||||
|
body.length + 2,
|
||||||
|
0,
|
||||||
|
...body,
|
||||||
|
opcodes["end"]
|
||||||
|
]
|
||||||
|
} else {
|
||||||
return [
|
return [
|
||||||
body.length + local_count + 3,
|
body.length + local_count + 3,
|
||||||
local_count,
|
local_count,
|
||||||
@@ -351,7 +661,8 @@ export class Assembler {
|
|||||||
...local_types,
|
...local_types,
|
||||||
...body,
|
...body,
|
||||||
opcodes["end"]
|
opcodes["end"]
|
||||||
]
|
];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return [ contents.length ].concat(...contents);
|
return [ contents.length ].concat(...contents);
|
||||||
}
|
}
|
||||||
@@ -359,15 +670,25 @@ export class Assembler {
|
|||||||
wasm() {
|
wasm() {
|
||||||
const template = [
|
const template = [
|
||||||
[ Section.TYPE, () => this.wasm_section_type() ],
|
[ Section.TYPE, () => this.wasm_section_type() ],
|
||||||
|
[ Section.IMPORT, () => this.wasm_section_import() ],
|
||||||
[ Section.FUNC, () => this.wasm_section_func() ],
|
[ Section.FUNC, () => this.wasm_section_func() ],
|
||||||
|
[ Section.MEM, () => this.wasm_section_mem() ],
|
||||||
|
[ Section.GLOBAL, () => this.wasm_section_global() ],
|
||||||
[ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem_wasm({ flags, init, max }) {
|
||||||
|
if (flags & mem_flags.max)
|
||||||
|
return [ flags, init, max ];
|
||||||
|
else
|
||||||
|
return [ flags, init ];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
wipforth.wat
23
wipforth.wat
@@ -542,20 +542,25 @@
|
|||||||
call $next)
|
call $next)
|
||||||
|
|
||||||
(func $copy (local $src i32) (local $dst i32) (local $n i32)
|
(func $copy (local $src i32) (local $dst i32) (local $n i32)
|
||||||
call $pop local.tee $dst
|
call $pop local.set $dst
|
||||||
call $pop local.tee $src
|
call $pop local.set $src
|
||||||
call $pop local.tee $n
|
call $pop local.set $n
|
||||||
|
|
||||||
block $done (param i32 i32 i32)
|
block $done
|
||||||
loop $loop (param i32 i32 i32) (result i32 i32 i32)
|
loop $loop
|
||||||
|
local.get $n
|
||||||
i32.eqz br_if $done
|
i32.eqz br_if $done
|
||||||
|
|
||||||
|
local.get $dst
|
||||||
|
local.get $src
|
||||||
i32.load8_u i32.store8
|
i32.load8_u i32.store8
|
||||||
local.get $dst i32.const 1 i32.add local.tee $dst
|
|
||||||
local.get $src i32.const 1 i32.add local.tee $src
|
local.get $dst i32.const 1 i32.add local.set $dst
|
||||||
local.get $n i32.const 1 i32.sub local.tee $n
|
local.get $src i32.const 1 i32.add local.set $src
|
||||||
|
local.get $n i32.const 1 i32.sub local.set $n
|
||||||
|
|
||||||
br $loop
|
br $loop
|
||||||
end
|
end
|
||||||
drop drop drop
|
|
||||||
end
|
end
|
||||||
|
|
||||||
call $next)
|
call $next)
|
||||||
|
|||||||
Reference in New Issue
Block a user