Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/WASTRunner/TestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,8 @@ extension WASTRunContext {
}
case .refExtern(let value): return .ref(.extern(Int(value)))
case .refFunc(let value): return .ref(.function(Int(value)))
case .refHost:
throw SpectestError("ref.host is not supported yet")
}
}
return try function.invoke(args)
Expand Down
117 changes: 117 additions & 0 deletions Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ protocol BinaryInstructionEncoder: InstructionVisitor {
mutating func encodeImmediates(value: V128) throws(VisitorError)
mutating func encodeImmediates(blockType: BlockType, tryCatch: TryCatch) throws(VisitorError)
mutating func encodeImmediates(dataIndex: UInt32, memory: UInt32) throws(VisitorError)
mutating func encodeImmediates(destType: UInt32, srcType: UInt32) throws(VisitorError)
mutating func encodeImmediates(dstMem: UInt32, srcMem: UInt32) throws(VisitorError)
mutating func encodeImmediates(dstTable: UInt32, srcTable: UInt32) throws(VisitorError)
mutating func encodeImmediates(elemIndex: UInt32, table: UInt32) throws(VisitorError)
mutating func encodeImmediates(memarg: MemArg, lane: UInt8) throws(VisitorError)
mutating func encodeImmediates(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError)
mutating func encodeImmediates(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError)
mutating func encodeImmediates(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError)
mutating func encodeImmediates(typeIndex: UInt32, size: UInt32) throws(VisitorError)
mutating func encodeImmediates(typeIndex: UInt32, tableIndex: UInt32) throws(VisitorError)
mutating func encodeImmediates(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError)
}

// BinaryInstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method.
Expand Down Expand Up @@ -934,4 +940,115 @@ extension BinaryInstructionEncoder {
try encodeInstruction(opcode)
try encodeImmediates(memarg: memarg, lane: lane)
}
mutating func visitRefI31() throws(VisitorError) { try encodeInstruction([0xFB, 0x1C]) }
mutating func visitI31GetS() throws(VisitorError) { try encodeInstruction([0xFB, 0x1D]) }
mutating func visitI31GetU() throws(VisitorError) { try encodeInstruction([0xFB, 0x1E]) }
mutating func visitStructNew(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x00])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitStructNewDefault(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x01])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x02])
try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex)
}
mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x03])
try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex)
}
mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x04])
try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex)
}
mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x05])
try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex)
}
mutating func visitArrayNew(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x06])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayNewDefault(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x07])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x08])
try encodeImmediates(typeIndex: typeIndex, size: size)
}
mutating func visitArrayGet(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x0B])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayGetS(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x0C])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayGetU(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x0D])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArraySet(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x0E])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayLen() throws(VisitorError) { try encodeInstruction([0xFB, 0x0F]) }
mutating func visitRefEq() throws(VisitorError) { try encodeInstruction([0xD3]) }
mutating func visitAnyConvertExtern() throws(VisitorError) { try encodeInstruction([0xFB, 0x1A]) }
mutating func visitExternConvertAny() throws(VisitorError) { try encodeInstruction([0xFB, 0x1B]) }
mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x09])
try encodeImmediates(typeIndex: typeIndex, dataIndex: dataIndex)
}
mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x0A])
try encodeImmediates(typeIndex: typeIndex, elemIndex: elemIndex)
}
mutating func visitArrayFill(typeIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x10])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x11])
try encodeImmediates(destType: destType, srcType: srcType)
}
mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x12])
try encodeImmediates(typeIndex: typeIndex, dataIndex: dataIndex)
}
mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0xFB, 0x13])
try encodeImmediates(typeIndex: typeIndex, elemIndex: elemIndex)
}
mutating func visitRefTest(_ refTest: Instruction.RefTest, type: HeapType) throws(VisitorError) {
let opcode: [UInt8]
switch refTest {
case .refTest: opcode = [0xFB, 0x14]
case .refTestNull: opcode = [0xFB, 0x15]
}

try encodeInstruction(opcode)
try encodeImmediates(type: type)
}
mutating func visitRefCast(_ refCast: Instruction.RefCast, type: HeapType) throws(VisitorError) {
let opcode: [UInt8]
switch refCast {
case .refCast: opcode = [0xFB, 0x16]
case .refCastNull: opcode = [0xFB, 0x17]
}

try encodeInstruction(opcode)
try encodeImmediates(type: type)
}
mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) {
try encodeInstruction([0xFB, 0x18])
try encodeImmediates(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo)
}
mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) {
try encodeInstruction([0xFB, 0x19])
try encodeImmediates(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo)
}
}
70 changes: 50 additions & 20 deletions Sources/WAT/BinaryEncoding/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,15 @@ extension TableType: WasmEncodable {
struct ElementExprCollector: AnyInstructionVisitor {
typealias VisitorError = WatParserError
var binaryOffset: Int = 0
var isAllRefFunc: Bool = true
var instructions: [Instruction] = []
private(set) var items: [[Instruction]] = []
private var currentItem: [Instruction] = []

var isAllRefFunc: Bool {
items.allSatisfy { item in
if item.count == 1, case .refFunc = item[0] { return true }
return false
}
}

mutating func parse(indices: WatParser.ElementDecl.Indices, wat: inout Wat) throws(WatParserError) {
switch indices {
Expand All @@ -271,7 +278,7 @@ struct ElementExprCollector: AnyInstructionVisitor {
}

private mutating func addFunctionIndex(_ index: UInt32) {
instructions.append(.refFunc(functionIndex: index))
items.append([.refFunc(functionIndex: index)])
}

private mutating func parseFunctionList(lexer: Lexer, wat: Wat) throws(WatParserError) {
Expand All @@ -283,11 +290,12 @@ struct ElementExprCollector: AnyInstructionVisitor {
}

mutating func visit(_ instruction: Instruction) {
if case .refFunc = instruction {
} else {
isAllRefFunc = false
}
instructions.append(instruction)
currentItem.append(instruction)
}

mutating func finishItem() {
items.append(currentItem)
currentItem = []
}
}

Expand Down Expand Up @@ -377,24 +385,17 @@ extension WAT.WatParser.ElementDecl {
}

if useExpression {
try encoder.encodeVector(collector.instructions) { instruction, encoder throws(WatParserError) in
try encoder.encodeVector(collector.items) { item, encoder throws(WatParserError) in
var exprEncoder = ExpressionEncoder()
switch instruction {
case .globalGet(let globalIndex):
try exprEncoder.visitGlobalGet(globalIndex: globalIndex)
case .refFunc(let functionIndex):
try exprEncoder.visitRefFunc(functionIndex: functionIndex)
case .refNull(let type):
try exprEncoder.visitRefNull(type: type)
default:
throw WatParserError("unexpected instruction in element expression \(instruction)", location: nil)
for instruction in item {
try exprEncoder.visit(instruction)
}
try exprEncoder.visitEnd()
encoder.output.append(contentsOf: exprEncoder.encoder.output)
}
} else {
encoder.encodeVector(collector.instructions) { instruction, encoder in
guard case .refFunc(let funcIndex) = instruction else { fatalError("non-ref.func instruction in non-expression mode") }
encoder.encodeVector(collector.items) { item, encoder in
guard item.count == 1, case .refFunc(let funcIndex) = item[0] else { fatalError("non-ref.func item in non-expression mode") }
encoder.writeUnsignedLEB128(funcIndex)
}
}
Expand Down Expand Up @@ -700,6 +701,35 @@ struct ExpressionEncoder: BinaryInstructionEncoder {
encodeUnsigned(typeIndex)
encodeUnsigned(tableIndex)
}
mutating func encodeImmediates(typeIndex: UInt32, fieldIndex: UInt32) {
encodeUnsigned(typeIndex)
encodeUnsigned(fieldIndex)
}
mutating func encodeImmediates(typeIndex: UInt32, size: UInt32) {
encodeUnsigned(typeIndex)
encodeUnsigned(size)
}
mutating func encodeImmediates(typeIndex: UInt32, dataIndex: UInt32) {
encodeUnsigned(typeIndex)
encodeImmediates(dataIndex: dataIndex)
}
mutating func encodeImmediates(typeIndex: UInt32, elemIndex: UInt32) {
encodeUnsigned(typeIndex)
encodeUnsigned(elemIndex)
}
mutating func encodeImmediates(destType: UInt32, srcType: UInt32) {
encodeUnsigned(destType)
encodeUnsigned(srcType)
}
mutating func encodeImmediates(relativeDepth: UInt32, castFrom: WasmTypes.ReferenceType, castTo: WasmTypes.ReferenceType) {
var flags: UInt8 = 0
if castFrom.isNullable { flags |= 0b01 }
if castTo.isNullable { flags |= 0b10 }
encodeByte(flags)
encodeUnsigned(relativeDepth)
encoder.encode(castFrom.heapType)
encoder.encode(castTo.heapType)
}
}

func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) -> [UInt8] {
Expand Down
70 changes: 70 additions & 0 deletions Sources/WAT/ParseTextInstruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,76 @@ func parseTextInstruction<V: InstructionVisitor>(
case "i16x8.relaxed_q15mulr_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i16x8RelaxedQ15MulrS) }
case "i16x8.relaxed_dot_i8x16_i7x16_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i16x8RelaxedDotI8X16I7X16S) }
case "i32x4.relaxed_dot_i8x16_i7x16_add_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i32x4RelaxedDotI8X16I7X16AddS) }
case "ref.i31": return { visitor throws(V.VisitorError) in return try visitor.visitRefI31() }
case "i31.get_s": return { visitor throws(V.VisitorError) in return try visitor.visitI31GetS() }
case "i31.get_u": return { visitor throws(V.VisitorError) in return try visitor.visitI31GetU() }
case "struct.new":
let (typeIndex) = try expressionParser.visitStructNew(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructNew(typeIndex: typeIndex) }
case "struct.new_default":
let (typeIndex) = try expressionParser.visitStructNewDefault(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructNewDefault(typeIndex: typeIndex) }
case "struct.get":
let (typeIndex, fieldIndex) = try expressionParser.visitStructGet(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructGet(typeIndex: typeIndex, fieldIndex: fieldIndex) }
case "struct.get_s":
let (typeIndex, fieldIndex) = try expressionParser.visitStructGetS(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructGetS(typeIndex: typeIndex, fieldIndex: fieldIndex) }
case "struct.get_u":
let (typeIndex, fieldIndex) = try expressionParser.visitStructGetU(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructGetU(typeIndex: typeIndex, fieldIndex: fieldIndex) }
case "struct.set":
let (typeIndex, fieldIndex) = try expressionParser.visitStructSet(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitStructSet(typeIndex: typeIndex, fieldIndex: fieldIndex) }
case "array.new":
let (typeIndex) = try expressionParser.visitArrayNew(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayNew(typeIndex: typeIndex) }
case "array.new_default":
let (typeIndex) = try expressionParser.visitArrayNewDefault(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewDefault(typeIndex: typeIndex) }
case "array.new_fixed":
let (typeIndex, size) = try expressionParser.visitArrayNewFixed(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewFixed(typeIndex: typeIndex, size: size) }
case "array.get":
let (typeIndex) = try expressionParser.visitArrayGet(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayGet(typeIndex: typeIndex) }
case "array.get_s":
let (typeIndex) = try expressionParser.visitArrayGetS(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayGetS(typeIndex: typeIndex) }
case "array.get_u":
let (typeIndex) = try expressionParser.visitArrayGetU(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayGetU(typeIndex: typeIndex) }
case "array.set":
let (typeIndex) = try expressionParser.visitArraySet(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArraySet(typeIndex: typeIndex) }
case "array.len": return { visitor throws(V.VisitorError) in return try visitor.visitArrayLen() }
case "ref.eq": return { visitor throws(V.VisitorError) in return try visitor.visitRefEq() }
case "any.convert_extern": return { visitor throws(V.VisitorError) in return try visitor.visitAnyConvertExtern() }
case "extern.convert_any": return { visitor throws(V.VisitorError) in return try visitor.visitExternConvertAny() }
case "array.new_data":
let (typeIndex, dataIndex) = try expressionParser.visitArrayNewData(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewData(typeIndex: typeIndex, dataIndex: dataIndex) }
case "array.new_elem":
let (typeIndex, elemIndex) = try expressionParser.visitArrayNewElem(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewElem(typeIndex: typeIndex, elemIndex: elemIndex) }
case "array.fill":
let (typeIndex) = try expressionParser.visitArrayFill(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayFill(typeIndex: typeIndex) }
case "array.copy":
let (destType, srcType) = try expressionParser.visitArrayCopy(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayCopy(destType: destType, srcType: srcType) }
case "array.init_data":
let (typeIndex, dataIndex) = try expressionParser.visitArrayInitData(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayInitData(typeIndex: typeIndex, dataIndex: dataIndex) }
case "array.init_elem":
let (typeIndex, elemIndex) = try expressionParser.visitArrayInitElem(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitArrayInitElem(typeIndex: typeIndex, elemIndex: elemIndex) }
case "br_on_cast":
let (relativeDepth, castFrom, castTo) = try expressionParser.visitBrOnCast(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitBrOnCast(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) }
case "br_on_cast_fail":
let (relativeDepth, castFrom, castTo) = try expressionParser.visitBrOnCastFail(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitBrOnCastFail(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) }
default: return nil
}
}
Loading
Loading