From 74af407e4780917070e45bdeb9a080e7d86c5c4d Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 25 Jul 2023 16:42:10 +0200 Subject: [PATCH] wat --- src/wasm/defs.ts | 23 +-- src/wasm/wat.ts | 358 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 368 insertions(+), 13 deletions(-) diff --git a/src/wasm/defs.ts b/src/wasm/defs.ts index e4972aa..bcbb5c0 100644 --- a/src/wasm/defs.ts +++ b/src/wasm/defs.ts @@ -82,9 +82,9 @@ export type NumericInstr = | { kind: `f${BitWidth}.${FUnOp}` } | { kind: `i${BitWidth}.${IBinOp}` } | { kind: `f${BitWidth}.${FBinOp}` } - | { kind: ITestOp } - | { kind: IRelOp } - | { kind: FRelOp } + | { kind: `i${BitWidth}.${ITestOp}` } + | { kind: `i${BitWidth}.${IRelOp}` } + | { kind: `f${BitWidth}.${FRelOp}` } | { kind: `i${BitWidth}.extend8_s` } | { kind: `i${BitWidth}.extend16_s` } | { kind: `i64.extend32_s` } @@ -191,8 +191,8 @@ export type TableInstr = // . memory export type MemArg = { - offset: u32; - align: u32; + offset?: u32; + align?: u32; }; export type MemoryInstr = @@ -289,7 +289,7 @@ export type Module = { start?: Start; imports: Vec; exports: Vec; - _name?: string, + _name?: string; }; export type TypeIdx = u32; @@ -306,20 +306,23 @@ export type Func = { type: TypeIdx; locals: Vec; body: Expr; - _name?: string, + _name?: string; }; export type Table = { type: TableType; + _name?: string; }; export type Mem = { type: MemType; + _name?: string; }; -export type GLobal = { +export type Global = { type: GlobalType; init: Expr; + _name?: string; }; export type Elem = unknown; @@ -327,6 +330,7 @@ export type Elem = unknown; export type Data = { init: VecByte; mode: Datamode; + _name?: string; }; export type Datamode = @@ -348,7 +352,8 @@ export type ExportDesc = idx: FuncIdx; } | { kind: "table"; idx: TableIdx } - | { kind: "mem"; idx: MemIdx | { kind: "global"; idx: GlobalIdx } }; + | { kind: "mem"; idx: MemIdx } + | { kind: "global"; idx: GlobalIdx }; export type Import = { module: Name; diff --git a/src/wasm/wat.ts b/src/wasm/wat.ts index 602b25c..f6b008b 100644 --- a/src/wasm/wat.ts +++ b/src/wasm/wat.ts @@ -2,18 +2,28 @@ // WebAssembly text format for easier debugging and inspection. import { + Blocktype, + Data, + Elem, + Export, Func, Functype as FuncType, + Global, GlobalType, Import, Instr, Limits, + Mem, + MemArg, Module, + Start, + Table, TableType as TableType, Valtype as ValType, } from "./defs"; const INLINE_SYM = Symbol.for("inline"); +const INLINE_OWN_LINE = Symbol.for("inline_own_line"); type Sexpr = string | number | Sexpr[] | { inline: Symbol; items: Sexpr[] }; @@ -22,6 +32,14 @@ export function writeModuleWat(module: Module) { console.dir(sexprs, { depth: 100 }); } +function inline(items: Sexpr[]): Sexpr { + return { inline: INLINE_SYM, items }; +} + +function inlineOwnLine(items: Sexpr[]): Sexpr { + return { inline: INLINE_OWN_LINE, items }; +} + // base function sexprString(s: string): Sexpr { @@ -29,6 +47,14 @@ function sexprString(s: string): Sexpr { return `"$${s}"`; } +function sexprBinaryString(buf: Uint8Array): Sexpr { + todo(); +} + +function optArr(elem?: T): T[] { + return elem !== undefined ? [elem] : []; +} + // types function sexprValtype(type: ValType): Sexpr { @@ -40,7 +66,7 @@ function sexprFuncType(type: FuncType): Sexpr { } function sexprLimits(limits: Limits): Sexpr { - return { inline: INLINE_SYM, items: [limits.min, limits.max] }; + return inline([limits.min, limits.max]); } function sexprTableType(type: TableType): Sexpr { @@ -55,8 +81,268 @@ function sexprGlobalType(type: GlobalType): Sexpr { // instrs +function sexprBlockType(type: Blocktype): Sexpr { + return type.kind === "typeidx" + ? type.idx + : type.type + ? sexprValtype(type.type) + : inline([]); +} + +function sexprMemarg(arg: MemArg): Sexpr { + const align = arg.align !== undefined ? `align=${arg.align}` : ""; + const offset = arg.offset /*0->false*/ ? `offset=${arg.offset}` : ""; + return inline([offset, align]); +} + function sexprInstr(instr: Instr): Sexpr { - todo(); + switch (instr.kind) { + case "block": + case "loop": + return inlineOwnLine([ + instr.kind, + sexprBlockType(instr.type), + ...instr.instr.map(sexprInstr), + "end", + ]); + case "if": + if (instr.else.length === 0) { + return inlineOwnLine([ + instr.kind, + sexprBlockType(instr.type), + ...instr.then.map(sexprInstr), + "end", + ]); + } else { + return inlineOwnLine([ + instr.kind, + sexprBlockType(instr.type), + ...instr.then.map(sexprInstr), + "else", + ...instr.else.map(sexprInstr), + "end", + ]); + } + case "unreachable": + case "nop": + case "return": + case "ref.is_null": + case "drop": + case "memory.size": + case "memory.grow": + case "memory.fill": + case "memory.copy": + // numeric i32 + case "i32.clz": + case "i32.ctz": + case "i32.popcnt": + case "i32.add": + case "i32.sub": + case "i32.mul": + case "i32.div_s": + case "i32.div_u": + case "i32.rem_s": + case "i32.rem_u": + case "i32.and": + case "i32.or": + case "i32.xor": + case "i32.shl": + case "i32.shr_s": + case "i32.shr_u": + case "i32.rotl": + case "i32.rotr": + // numeric i64 + case "i64.clz": + case "i64.ctz": + case "i64.popcnt": + case "i64.add": + case "i64.sub": + case "i64.mul": + case "i64.div_s": + case "i64.div_u": + case "i64.rem_s": + case "i64.rem_u": + case "i64.and": + case "i64.or": + case "i64.xor": + case "i64.shl": + case "i64.shr_s": + case "i64.shr_u": + case "i64.rotl": + case "i64.rotr": + // numeric f32 + case "f32.abs": + case "f32.neg": + case "f32.ceil": + case "f32.floor": + case "f32.trunc": + case "f32.nearest": + case "f32.sqrt": + case "f32.add": + case "f32.sub": + case "f32.mul": + case "f32.div": + case "f32.min": + case "f32.max": + case "f32.copysign": + // numeric f64 + case "f64.abs": + case "f64.neg": + case "f64.ceil": + case "f64.floor": + case "f64.trunc": + case "f64.nearest": + case "f64.sqrt": + case "f64.add": + case "f64.sub": + case "f64.mul": + case "f64.div": + case "f64.min": + case "f64.max": + case "f64.copysign": + // more numeric i32 + case "i32.eqz": + case "i32.eq": + case "i32.ne": + case "i32.lt_s": + case "i32.lt_u": + case "i32.gt_s": + case "i32.gt_u": + case "i32.le_s": + case "i32.le_u": + case "i32.ge_s": + case "i32.ge_u": + // more numeric i64 + case "i64.eqz": + case "i64.eq": + case "i64.ne": + case "i64.lt_s": + case "i64.lt_u": + case "i64.gt_s": + case "i64.gt_u": + case "i64.le_s": + case "i64.le_u": + case "i64.ge_s": + case "i64.ge_u": + // more numeric f32 + case "f32.eq": + case "f32.ne": + case "f32.lt": + case "f32.gt": + case "f32.le": + case "f32.ge": + // more numeric f64 + case "f64.eq": + case "f64.ne": + case "f64.lt": + case "f64.gt": + case "f64.le": + case "f64.ge": + // more numeric + case "i32.wrap_i64": + case "i32.trunc_f32_s": + case "i32.trunc_f32_u": + case "i32.trunc_f64_u": + case "i32.trunc_f64_s": + case "i32.trunc_sat_f32_s": + case "i32.trunc_sat_f32_u": + case "i32.trunc_sat_f64_u": + case "i32.trunc_sat_f64_s": + case "i64.extend_i32_s": + case "i64.extend_i32_u": + case "i64.trunc_f32_s": + case "i64.trunc_f32_u": + case "i64.trunc_f64_u": + case "i64.trunc_f64_s": + case "i64.trunc_sat_f32_s": + case "i64.trunc_sat_f32_u": + case "i64.trunc_sat_f64_u": + case "i64.trunc_sat_f64_s": + case "f32.convert_i32_s": + case "f32.convert_i32_u": + case "f32.convert_i64_s": + case "f32.convert_i64_u": + case "f32.demote_f64": + case "f64.convert_i32_s": + case "f64.convert_i32_u": + case "f64.convert_i64_s": + case "f64.convert_i64_u": + case "f64.promote_f32": + case "i32.reinterpret_f32": + case "i64.reinterpret_f64": + case "f32.reinterpret_i32": + case "f64.reinterpret_i64": + // int extend + case "i32.extend8_s": + case "i32.extend16_s": + case "i64.extend8_s": + case "i64.extend16_s": + case "i64.extend32_s": + return instr.kind; + case "br": + case "br_if": + return inlineOwnLine([instr.kind, instr.label]); + case "br_table": + return inlineOwnLine([instr.kind, ...instr.labels, instr.label]); + case "call": + return inlineOwnLine([instr.kind, instr.func]); + case "call_indirect": + return inlineOwnLine([instr.kind, instr.table, instr.type]); + case "i32.const": + case "i64.const": + case "f32.const": + case "f64.const": + case "ref.null": + case "ref.func": + case "local.get": + case "local.set": + case "local.tee": + case "global.get": + case "global.set": + case "table.get": + case "table.set": + case "table.size": + case "table.grow": + case "table.fill": + case "elem.drop": + case "memory.init": + case "data.drop": + return inlineOwnLine([instr.kind, instr.imm]); + case "select": + return inlineOwnLine([ + instr.kind, + ...optArr(instr.type?.map(sexprValtype)), + ]); + case "table.copy": + case "table.init": + return inlineOwnLine([instr.kind, instr.imm1, instr.imm2]); + case "i32.load": + case "i64.load": + case "f32.load": + case "f64.load": + case "i32.load8_s": + case "i32.load8_u": + case "i32.load16_s": + case "i32.load16_u": + case "i64.load8_s": + case "i64.load8_u": + case "i64.load16_s": + case "i64.load16_u": + case "i64.load32_s": + case "i64.load32_u": + case "i32.store": + case "i64.store": + case "f32.store": + case "f64.store": + case "i32.store8": + case "i32.store16": + case "i64.store8": + case "i64.store16": + case "i64.store32": + case "v128.load": + case "v128.store": + return inlineOwnLine([instr.kind, sexprMemarg(instr.imm)]); + } } // modules @@ -94,20 +380,84 @@ function sexprImport(import_: Import): Sexpr { function sexprFunction(func: Func): Sexpr { return [ "func", - ...(func._name ? [func._name] : []), + ...optArr(func._name), ["type", func.type], ...func.locals.map((local) => ["local", sexprValtype(local)]), ...func.body.map((instr) => sexprInstr(instr)), ]; } +function sexprTable(table: Table): Sexpr { + return ["table", ...optArr(table._name), sexprTableType(table.type)]; +} + +function sexprMem(mem: Mem): Sexpr { + return ["memory", ...optArr(mem._name), sexprLimits(mem.type)]; +} + +function sexprGlobal(global: Global): Sexpr { + return [ + "global", + ...optArr(global._name), + sexprGlobalType(global.type), + ...global.init.map(sexprInstr), + ]; +} +function sexprExport(export_: Export): Sexpr { + const desc = export_.desc; + let exportDesc; + switch (desc.kind) { + case "func": + exportDesc = ["func", desc.idx]; + break; + case "table": + exportDesc = ["table", desc.idx]; + break; + case "mem": + exportDesc = ["memory", desc.idx]; + break; + case "global": + exportDesc = ["global", desc.idx]; + break; + } + return ["export", export_.name, exportDesc]; +} + +function sexprStart(start: Start): Sexpr { + return ["start", start.func]; +} +function sexprElem(_elem: Elem): Sexpr { + todo(); +} +function sexprData(data: Data): Sexpr { + let mode = data.mode; + if (mode.kind === "passive") { + return ["data", ...optArr(data._name), sexprBinaryString(data.init)]; + } else { + return [ + "data", + ...optArr(data._name), + ...optArr(mode.memory === 0 ? undefined : ["memory", mode.memory]), + ["offset", ...mode.offset.map(sexprInstr)], + sexprBinaryString(data.init), + ]; + } +} + function sexprModule(module: Module): Sexpr { return [ "module", - ...(module._name ? [module._name] : []), + ...optArr(module._name), ...module.types.map(sexprType), ...module.imports.map(sexprImport), ...module.funcs.map(sexprFunction), + ...module.tables.map(sexprTable), + ...module.mems.map(sexprMem), + ...module.globals.map(sexprGlobal), + ...module.exports.map(sexprExport), + ...optArr(module.start && sexprStart(module.start)), + ...module.elems.map(sexprElem), + ...module.datas.map(sexprData), ]; }