WASM: Implement asm/disasm support for exception handling tags

This commit is contained in:
Igor Laevsky
2021-08-13 18:35:25 +03:00
committed by TeamCityServer
parent 2ef5fd95db
commit e5e44a0152
6 changed files with 87 additions and 5 deletions
@@ -15,6 +15,7 @@ class WasmModule(
val importedMemories: List<WasmMemory> = emptyList(),
val importedTables: List<WasmTable> = emptyList(),
val importedGlobals: List<WasmGlobal> = emptyList(),
val importedTags: List<WasmTag> = emptyList(),
val definedFunctions: List<WasmFunction.Defined> = emptyList(),
val tables: List<WasmTable> = emptyList(),
@@ -22,6 +23,7 @@ class WasmModule(
val globals: List<WasmGlobal> = emptyList(),
val exports: List<WasmExport<*>> = emptyList(),
val elements: List<WasmElement> = emptyList(),
val tags: List<WasmTag> = 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<T : WasmNamedModuleField>(
class Table(name: String, field: WasmTable) : WasmExport<WasmTable>(name, field, 0x1, "table")
class Memory(name: String, field: WasmMemory) : WasmExport<WasmMemory>(name, field, 0x2, "memory")
class Global(name: String, field: WasmGlobal) : WasmExport<WasmGlobal>(name, field, 0x3, "global")
class Tag(name: String, field: WasmTag) : WasmExport<WasmTag>(name, field, 0x4, "tag")
}
sealed class WasmTypeDeclaration(
@@ -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<Int>) : 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),
@@ -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)
}
@@ -22,6 +22,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
val importedMemories: MutableList<WasmMemory> = mutableListOf()
val importedTables: MutableList<WasmTable> = mutableListOf()
val importedGlobals: MutableList<WasmGlobal> = mutableListOf()
val importedTags: MutableList<WasmTag> = mutableListOf()
val definedFunctions: MutableList<WasmFunction.Defined> = mutableListOf()
val table: MutableList<WasmTable> = mutableListOf()
@@ -32,7 +33,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
val elements: MutableList<WasmElement> = mutableListOf()
val data: MutableList<WasmData> = mutableListOf()
var dataCount: Boolean = false
val tags: MutableList<WasmTag> = mutableListOf()
private fun <T> byIdx(l1: List<T>, l2: List<T>, 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<WasmInstr> =
mutableListOf<WasmInstr>().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() })
@@ -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<WasmInstr>) {
expr.forEach { appendInstr(it) }
appendInstr(WasmInstr(WasmOp.END))
@@ -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")