From e5e44a0152e1689de5cef69ec20b29c9f0c24d64 Mon Sep 17 00:00:00 2001 From: Igor Laevsky Date: Fri, 13 Aug 2021 18:35:25 +0300 Subject: [PATCH] WASM: Implement asm/disasm support for exception handling tags --- .../jetbrains/kotlin/wasm/ir/Declarations.kt | 12 ++++++++ .../org/jetbrains/kotlin/wasm/ir/Operators.kt | 8 +++++ .../src/org/jetbrains/kotlin/wasm/ir/Utils.kt | 2 ++ .../wasm/ir/convertors/WasmBinaryToIR.kt | 29 +++++++++++++++++-- .../wasm/ir/convertors/WasmIrToBinary.kt | 20 ++++++++++++- .../kotlin/wasm/ir/convertors/WasmIrToText.kt | 21 ++++++++++++-- 6 files changed, 87 insertions(+), 5 deletions(-) diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt index 3e995eead1c..c0492dead72 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt @@ -15,6 +15,7 @@ class WasmModule( val importedMemories: List = emptyList(), val importedTables: List = emptyList(), val importedGlobals: List = emptyList(), + val importedTags: List = emptyList(), val definedFunctions: List = emptyList(), val tables: List = emptyList(), @@ -22,6 +23,7 @@ class WasmModule( val globals: List = emptyList(), val exports: List> = emptyList(), val elements: List = emptyList(), + val tags: List = emptyList(), val startFunction: WasmFunction? = null, @@ -103,6 +105,15 @@ class WasmElement( } } +class WasmTag( + val type: WasmFunctionType, + val importPair: WasmImportPair? = null +) : WasmNamedModuleField() { + init { + assert(type.resultTypes.isEmpty()) { "Must have empty return as per current spec" } + } +} + class WasmLocal( val id: Int, val name: String, @@ -128,6 +139,7 @@ sealed class WasmExport( class Table(name: String, field: WasmTable) : WasmExport(name, field, 0x1, "table") class Memory(name: String, field: WasmMemory) : WasmExport(name, field, 0x2, "memory") class Global(name: String, field: WasmGlobal) : WasmExport(name, field, 0x3, "global") + class Tag(name: String, field: WasmTag) : WasmExport(name, field, 0x4, "tag") } sealed class WasmTypeDeclaration( diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt index f2c93754ce3..7db83235bc1 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt @@ -27,6 +27,7 @@ enum class WasmImmediateKind { DATA_IDX, TABLE_IDX, LABEL_IDX, + TAG_IDX, LABEL_IDX_VECTOR, ELEM_IDX, @@ -78,6 +79,7 @@ sealed class WasmImmediate { } class LabelIdx(val value: Int) : WasmImmediate() + class TagIdx(val value: Int) : WasmImmediate() class LabelIdxVector(val value: List) : WasmImmediate() class ElemIdx(val value: WasmElement) : WasmImmediate() @@ -306,6 +308,12 @@ enum class WasmOp( RETURN("return", 0x0F), CALL("call", 0x10, FUNC_IDX), CALL_INDIRECT("call_indirect", 0x11, listOf(TYPE_IDX, TABLE_IDX)), + TRY("try", 0x06, BLOCK_TYPE), + CATCH("catch", 0x07, TAG_IDX), + CATCH_ALL("catch_all", 0x19), + DELEGATE("delegate", 0x18, LABEL_IDX), + THROW("throw", 0x08, TAG_IDX), + RETHROW("rethrow", 0x09, LABEL_IDX), // Parametric DROP("drop", 0x1A), diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt index 911e8524a45..e4fde56df06 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt @@ -21,10 +21,12 @@ fun WasmModule.calculateIds() { importedMemories.calculateIds() importedTables.calculateIds() importedGlobals.calculateIds() + importedTags.calculateIds() elements.calculateIds() definedFunctions.calculateIds(startIndex = importedFunctions.size) globals.calculateIds(startIndex = importedGlobals.size) memories.calculateIds(startIndex = importedMemories.size) tables.calculateIds(startIndex = importedTables.size) + tags.calculateIds(startIndex = importedTags.size) } diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt index 298f38674ee..136991a214f 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt @@ -22,6 +22,7 @@ class WasmBinaryToIR(val b: MyByteReader) { val importedMemories: MutableList = mutableListOf() val importedTables: MutableList = mutableListOf() val importedGlobals: MutableList = mutableListOf() + val importedTags: MutableList = mutableListOf() val definedFunctions: MutableList = mutableListOf() val table: MutableList = mutableListOf() @@ -32,7 +33,7 @@ class WasmBinaryToIR(val b: MyByteReader) { val elements: MutableList = mutableListOf() val data: MutableList = mutableListOf() var dataCount: Boolean = false - + val tags: MutableList = mutableListOf() private fun byIdx(l1: List, l2: List, index: Int): T { if (index < l1.size) @@ -45,6 +46,7 @@ class WasmBinaryToIR(val b: MyByteReader) { private fun elemByIdx(index: Int) = elements[index] private fun tableByIdx(index: Int) = byIdx(importedTables, table, index) private fun globalByIdx(index: Int) = byIdx(importedGlobals, globals, index) + private fun tagByIdx(index: Int) = byIdx(importedTags, tags, index) fun parseModule(): WasmModule { if (b.readUInt32() != 0x6d736100u) @@ -119,6 +121,11 @@ class WasmBinaryToIR(val b: MyByteReader) { ).also { importsInOrder.add(it) } ) } + 4 -> { + val tag = readTag(importPair) + importedTags.add(tag) + importsInOrder.add(tag) + } else -> error( "Unsupported import kind $kind" ) @@ -162,6 +169,13 @@ class WasmBinaryToIR(val b: MyByteReader) { } } + // Tag section + 13 -> { + forEachVectorElement { + tags.add(readTag()) + } + } + // Globals section 6 -> { forEachVectorElement { @@ -190,6 +204,7 @@ class WasmBinaryToIR(val b: MyByteReader) { 1 -> WasmExport.Table(name, tableByIdx(index)) 2 -> WasmExport.Memory(name, memoryByIdx(index)) 3 -> WasmExport.Global(name, globalByIdx(index)) + 4 -> WasmExport.Tag(name, tagByIdx(index)) else -> error("Invalid export kind $kind") } ) @@ -319,6 +334,7 @@ class WasmBinaryToIR(val b: MyByteReader) { importedMemories = importedMemories, importedTables = importedTables, importedGlobals = importedGlobals, + importedTags = importedTags, definedFunctions = definedFunctions, tables = table, memories = memory, @@ -327,7 +343,8 @@ class WasmBinaryToIR(val b: MyByteReader) { startFunction = startFunction, elements = elements, data = data, - dataCount = dataCount + dataCount = dataCount, + tags = tags ).also { it.calculateIds() } @@ -341,6 +358,13 @@ class WasmBinaryToIR(val b: MyByteReader) { ) } + private fun readTag(importPair: WasmImportPair? = null): WasmTag { + val attribute = b.readByte() + check(attribute.toInt() == 0) { "as per spec" } + val type = functionTypes[b.readVarUInt32AsInt()] + return WasmTag(type, importPair) + } + private fun readExpression(): MutableList = mutableListOf().also { readExpression(it) } @@ -404,6 +428,7 @@ class WasmBinaryToIR(val b: MyByteReader) { WasmImmediateKind.DATA_IDX -> WasmImmediate.DataIdx(b.readVarUInt32AsInt()) WasmImmediateKind.TABLE_IDX -> WasmImmediate.TableIdx(b.readVarUInt32AsInt()) WasmImmediateKind.LABEL_IDX -> WasmImmediate.LabelIdx(b.readVarUInt32AsInt()) + WasmImmediateKind.TAG_IDX -> WasmImmediate.TagIdx(b.readVarUInt32AsInt()) WasmImmediateKind.LABEL_IDX_VECTOR -> WasmImmediate.LabelIdxVector(mapVector { b.readVarUInt32AsInt() }) WasmImmediateKind.ELEM_IDX -> WasmImmediate.ElemIdx(elemByIdx(b.readVarUInt32AsInt())) WasmImmediateKind.VAL_TYPE_VECTOR -> WasmImmediate.ValTypeVector(mapVector { readValueType() }) diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt index d80bd716e4c..41c6a656191 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt @@ -41,6 +41,7 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule) { is WasmMemory -> appendMemory(it) is WasmTable -> appendTable(it) is WasmGlobal -> appendGlobal(it) + is WasmTag -> appendTag(it) else -> error("Unknown import kind ${it::class}") } } @@ -64,7 +65,11 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule) { memories.forEach { appendMemory(it) } } - // Good + // tag section + appendSection(13u) { + appendVectorSize(tags.size) + tags.forEach { appendTag(it) } + } appendSection(6u) { appendVectorSize(globals.size) @@ -141,6 +146,7 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule) { is WasmImmediate.DataIdx -> b.writeVarUInt32(x.value) is WasmImmediate.TableIdx -> b.writeVarUInt32(x.value.owner) is WasmImmediate.LabelIdx -> b.writeVarUInt32(x.value) + is WasmImmediate.TagIdx -> b.writeVarUInt32(x.value) is WasmImmediate.LabelIdxVector -> { b.writeVarUInt32(x.value.size) for (target in x.value) { @@ -271,6 +277,18 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule) { appendExpr(c.init) } + private fun appendTag(t: WasmTag) { + if (t.importPair != null) { + b.writeString(t.importPair.moduleName) + b.writeString(t.importPair.declarationName) + b.writeByte(4) + return + } + b.writeByte(0) // attribute + assert(t.type.id != null) { "Unlinked tag id" } + b.writeVarUInt32(t.type.id!!) + } + private fun appendExpr(expr: Iterable) { expr.forEach { appendInstr(it) } appendInstr(WasmInstr(WasmOp.END)) diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt index ce67b2d91b5..3e80118dfdd 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt @@ -60,13 +60,13 @@ class WasmIrToText : SExpressionBuilder() { private fun appendInstr(wasmInstr: WasmInstr) { val op = wasmInstr.operator - if (op == WasmOp.END || op == WasmOp.ELSE) + if (op == WasmOp.END || op == WasmOp.ELSE || op == WasmOp.CATCH) indent-- newLine() stringBuilder.append(wasmInstr.operator.mnemonic) - if (op == WasmOp.BLOCK || op == WasmOp.LOOP || op == WasmOp.IF || op == WasmOp.ELSE) + if (op == WasmOp.BLOCK || op == WasmOp.LOOP || op == WasmOp.IF || op == WasmOp.ELSE || op == WasmOp.CATCH || op == WasmOp.TRY) indent++ if (wasmInstr.operator in setOf(WasmOp.CALL_INDIRECT, WasmOp.TABLE_INIT)) { @@ -100,6 +100,7 @@ class WasmIrToText : SExpressionBuilder() { is WasmImmediate.DataIdx -> appendElement(x.value.toString()) is WasmImmediate.TableIdx -> appendElement(x.value.toString()) is WasmImmediate.LabelIdx -> appendElement(x.value.toString()) + is WasmImmediate.TagIdx -> appendElement(x.value.toString()) is WasmImmediate.LabelIdxVector -> x.value.forEach { appendElement(it.toString()) } @@ -212,6 +213,7 @@ class WasmIrToText : SExpressionBuilder() { is WasmMemory -> appendMemory(it) is WasmTable -> appendTable(it) is WasmGlobal -> appendGlobal(it) + is WasmTag -> appendTag(it) else -> error("Unknown import kind ${it::class}") } } @@ -223,6 +225,7 @@ class WasmIrToText : SExpressionBuilder() { elements.forEach { appendWasmElement(it) } startFunction?.let { appendStartFunction(it) } data.forEach { appendData(it) } + tags.forEach { appendTag(it) } } } } @@ -400,6 +403,19 @@ class WasmIrToText : SExpressionBuilder() { } } + private fun appendTag(wasmTag: WasmTag) { + newLineList("tag") { + appendModuleFieldReference(wasmTag) + + wasmTag.importPair?.appendImportPair() + + sameLineList("param") { + wasmTag.type.parameterTypes.forEach { appendType(it) } + } + assert(wasmTag.type.resultTypes.isEmpty()) { "must be as per spec" } + } + } + private fun appendLocal(local: WasmLocal) { newLineList(if (local.isParameter) "param" else "local") { appendLocalReference(local) @@ -485,6 +501,7 @@ class WasmIrToText : SExpressionBuilder() { is WasmElement -> "elem" is WasmGlobal -> "g" is WasmTypeDeclaration -> "type" + is WasmTag -> "tag" } appendElement("\$${sanitizeWatIdentifier(field.name)}___${indexSpaceKind}_$id")