Skip to content

Commit 9966354

Browse files
Fix encoding of heap types and leave concrete heap types unimplemented
1 parent b141c7d commit 9966354

File tree

18 files changed

+181
-83
lines changed

18 files changed

+181
-83
lines changed

Sources/WAT/BinaryInstructionEncoder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protocol BinaryInstructionEncoder: InstructionVisitor {
2323
mutating func encodeImmediates(relativeDepth: UInt32) throws
2424
mutating func encodeImmediates(table: UInt32) throws
2525
mutating func encodeImmediates(targets: BrTable) throws
26-
mutating func encodeImmediates(type: ReferenceType) throws
26+
mutating func encodeImmediates(type: HeapType) throws
2727
mutating func encodeImmediates(type: ValueType) throws
2828
mutating func encodeImmediates(typeIndex: UInt32) throws
2929
mutating func encodeImmediates(value: IEEE754.Float32) throws
@@ -172,7 +172,7 @@ extension BinaryInstructionEncoder {
172172
try encodeInstruction([0x44])
173173
try encodeImmediates(value: value)
174174
}
175-
mutating func visitRefNull(type: ReferenceType) throws {
175+
mutating func visitRefNull(type: HeapType) throws {
176176
try encodeInstruction([0xD0])
177177
try encodeImmediates(type: type)
178178
}

Sources/WAT/Encoder.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,33 @@ extension ValueType: WasmEncodable {
120120
case .f32: encoder.output.append(0x7D)
121121
case .f64: encoder.output.append(0x7C)
122122
case .v128: encoder.output.append(0x7B)
123-
case .ref(let refType): refType.encode(to: &encoder)
123+
case .ref(let refType): encoder.encode(refType)
124124
}
125125
}
126126
}
127127

128128
extension ReferenceType: WasmEncodable {
129129
func encode(to encoder: inout Encoder) {
130-
switch self.heapType {
131-
case .funcRef: encoder.output.append(isNullable ? 0x70 : 0x71)
132-
case .externRef: encoder.output.append(0x6F)
130+
switch (isNullable, heapType) {
131+
// Use short form when available
132+
case (true, .externRef): encoder.output.append(0x6F)
133+
case (true, .funcRef): encoder.output.append(0x70)
134+
default:
135+
encoder.output.append(isNullable ? 0x63 : 0x64)
136+
encoder.encode(heapType)
137+
}
138+
}
139+
}
140+
141+
extension HeapType: WasmEncodable {
142+
func encode(to encoder: inout Encoder) {
143+
switch self {
144+
case .abstract(.externRef): encoder.output.append(0x6F)
145+
case .abstract(.funcRef): encoder.output.append(0x70)
146+
case .concrete(let typeIndex):
147+
// Note that the typeIndex is decoded as s33,
148+
// so we need to encode it as signed.
149+
encoder.writeSignedLEB128(Int64(typeIndex))
133150
}
134151
}
135152
}
@@ -511,7 +528,7 @@ struct ExpressionEncoder: BinaryInstructionEncoder {
511528
encodeUnsigned(targets.defaultIndex)
512529
}
513530
mutating func encodeImmediates(type: WasmTypes.ValueType) throws { encoder.encode(type) }
514-
mutating func encodeImmediates(type: WasmTypes.ReferenceType) throws { encoder.encode(type) }
531+
mutating func encodeImmediates(type: WasmTypes.HeapType) throws { encoder.encode(type) }
515532
mutating func encodeImmediates(value: Int32) throws { encodeSigned(value) }
516533
mutating func encodeImmediates(value: Int64) throws { encodeSigned(value) }
517534
mutating func encodeImmediates(value: WasmParser.IEEE754.Float32) throws { encodeFixedWidth(value.bitPattern) }

Sources/WAT/Parser.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ internal struct Parser {
111111
return String(decoding: bytes, as: UTF8.self)
112112
}
113113

114+
/// Parse a `u32` raw index or a symbolic `id` identifier.
115+
/// https://webassembly.github.io/function-references/core/text/modules.html#indices
114116
mutating func takeIndexOrId() throws -> IndexOrId? {
115117
let location = lexer.location()
116118
if let index: UInt32 = try takeUnsignedInt() {

Sources/WAT/Parser/ExpressionParser.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,15 +361,17 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
361361
return UInt32(index)
362362
}
363363

364-
private mutating func refKind() throws -> ReferenceType {
365-
if try parser.take(.id) {
366-
return .funcRef // not sure about this.
367-
} else if try parser.takeKeyword("func") {
364+
/// https://webassembly.github.io/function-references/core/text/types.html#text-heaptype
365+
private mutating func heapType(wat: inout Wat) throws -> HeapType {
366+
if try parser.takeKeyword("func") {
368367
return .funcRef
369368
} else if try parser.takeKeyword("extern") {
370369
return .externRef
370+
} else if let id = try parser.takeIndexOrId() {
371+
let (decl, index) = try wat.types.resolve(use: id)
372+
return .concrete(typeIndex: UInt32(index))
371373
}
372-
throw WatParserError("expected \"func\" or \"extern\"", location: parser.lexer.location())
374+
throw WatParserError("expected \"func\", \"extern\" or type index", location: parser.lexer.location())
373375
}
374376

375377
private mutating func memArg(defaultAlign: UInt32) throws -> MemArg {
@@ -513,8 +515,8 @@ extension ExpressionParser {
513515
mutating func visitF64Const(wat: inout Wat) throws -> IEEE754.Float64 {
514516
return try parser.expectFloat64()
515517
}
516-
mutating func visitRefNull(wat: inout Wat) throws -> ReferenceType {
517-
return try refKind()
518+
mutating func visitRefNull(wat: inout Wat) throws -> HeapType {
519+
return try heapType(wat: &wat)
518520
}
519521
mutating func visitRefFunc(wat: inout Wat) throws -> UInt32 {
520522
return try functionIndex(wat: &wat)

Sources/WAT/Parser/WastParser.swift

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,31 +54,25 @@ struct WastParser {
5454
}
5555

5656
struct ConstExpressionCollector: WastConstInstructionVisitor {
57-
let addValue: (Value) -> Void
57+
let addValue: (WastConstValue) -> Void
5858

5959
mutating func visitI32Const(value: Int32) throws { addValue(.i32(UInt32(bitPattern: value))) }
6060
mutating func visitI64Const(value: Int64) throws { addValue(.i64(UInt64(bitPattern: value))) }
6161
mutating func visitF32Const(value: IEEE754.Float32) throws { addValue(.f32(value.bitPattern)) }
6262
mutating func visitF64Const(value: IEEE754.Float64) throws { addValue(.f64(value.bitPattern)) }
6363
mutating func visitRefFunc(functionIndex: UInt32) throws {
64-
addValue(.ref(.function(FunctionAddress(functionIndex))))
64+
addValue(.refFunc(functionIndex: functionIndex))
6565
}
66-
mutating func visitRefNull(type: ReferenceType) throws {
67-
let value: Reference
68-
switch type.heapType {
69-
case .externRef: value = .extern(nil)
70-
case .funcRef: value = .function(nil)
71-
}
72-
addValue(.ref(value))
66+
mutating func visitRefNull(type: HeapType) throws {
67+
addValue(.refNull(type))
7368
}
74-
75-
mutating func visitRefExtern(value: UInt32) throws {
76-
addValue(.ref(.extern(ExternAddress(value))))
69+
func visitRefExtern(value: UInt32) throws {
70+
addValue(.refExtern(value: value))
7771
}
7872
}
7973

80-
mutating func constExpression() throws -> [Value] {
81-
var values: [Value] = []
74+
mutating func argumentValues() throws -> [WastConstValue] {
75+
var values: [WastConstValue] = []
8276
var collector = ConstExpressionCollector(addValue: { values.append($0) })
8377
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer, features: features)
8478
while try exprParser.parseWastConstInstruction(visitor: &collector) {}
@@ -137,16 +131,26 @@ public enum WastExecute {
137131
}
138132
}
139133

134+
public enum WastConstValue {
135+
case i32(UInt32)
136+
case i64(UInt64)
137+
case f32(UInt32)
138+
case f64(UInt64)
139+
case refNull(HeapType)
140+
case refFunc(functionIndex: UInt32)
141+
case refExtern(value: UInt32)
142+
}
143+
140144
public struct WastInvoke {
141145
public let module: String?
142146
public let name: String
143-
public let args: [Value]
147+
public let args: [WastConstValue]
144148

145149
static func parse(wastParser: inout WastParser) throws -> WastInvoke {
146150
try wastParser.parser.expectKeyword("invoke")
147151
let module = try wastParser.parser.takeId()
148152
let name = try wastParser.parser.expectString()
149-
let args = try wastParser.constExpression()
153+
let args = try wastParser.argumentValues()
150154
try wastParser.parser.expect(.rightParen)
151155
let invoke = WastInvoke(module: module?.value, name: name, args: args)
152156
return invoke
@@ -155,7 +159,7 @@ public struct WastInvoke {
155159

156160
public enum WastExpectValue {
157161
/// A concrete value that is expected to be returned.
158-
case value(Value)
162+
case value(WastConstValue)
159163
/// A value that is expected to be a canonical NaN.
160164
/// Corresponds to `f32.const nan:canonical` in WAST.
161165
case f32CanonicalNaN

Sources/WasmKit/Execution/Errors.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ extension TrapReason.Message {
139139
static func exportedFunctionNotFound(name: String, instance: Instance) -> Self {
140140
Self("exported function \(name) not found in instance \(instance)")
141141
}
142+
static func unimplemented(feature: String) -> Self {
143+
Self("\(feature) is not implemented yet")
144+
}
142145
}
143146

144147
struct ImportError: Error {

Sources/WasmKit/Execution/Instances.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,12 @@ struct TableEntity /* : ~Copyable */ {
275275
init(_ tableType: TableType, resourceLimiter: any ResourceLimiter) throws {
276276
let emptyElement: Reference
277277
switch tableType.elementType.heapType {
278-
case .funcRef:
278+
case .abstract(.funcRef):
279279
emptyElement = .function(nil)
280-
case .externRef:
280+
case .abstract(.externRef):
281281
emptyElement = .extern(nil)
282+
case .concrete:
283+
throw Trap(.unimplemented(feature: "heap type other than `func` and `extern`"))
282284
}
283285

284286
let numberOfElements = Int(tableType.limits.min)

Sources/WasmKit/Execution/Instructions/InstructionSupport.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ extension Int32: InstructionImmediate {
104104
// MARK: - Immediate type extensions
105105

106106
extension Instruction.RefNullOperand {
107-
init(result: VReg, type: ReferenceType) {
108-
self.init(result: result, rawType: type.heapType.rawValue) // need to figure out rawType here
107+
init(result: VReg, type: AbstractHeapType) {
108+
self.init(result: result, rawType: type.rawValue) // need to figure out rawType here
109109
}
110110

111-
var type: ReferenceType {
112-
ReferenceType(isNullable: true, heapType: HeapType(rawValue: rawType)!) // is this still a valid conversion?
111+
var type: AbstractHeapType {
112+
AbstractHeapType(rawValue: rawType).unsafelyUnwrapped
113113
}
114114
}
115115

Sources/WasmKit/Execution/Instructions/Misc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension Execution {
2121
extension Execution {
2222
mutating func refNull(sp: Sp, immediate: Instruction.RefNullOperand) {
2323
let value: Value
24-
switch immediate.type.heapType {
24+
switch immediate.type {
2525
case .externRef:
2626
value = .ref(.extern(nil))
2727
case .funcRef:

Sources/WasmKit/Execution/UntypedValue.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,12 @@ struct UntypedValue: Equatable, Hashable {
115115
return Int(storage)
116116
}
117117
switch type.heapType {
118-
case .funcRef:
118+
case .abstract(.funcRef):
119119
return .function(decodeOptionalInt())
120-
case .externRef:
120+
case .abstract(.externRef):
121121
return .extern(decodeOptionalInt())
122+
case .concrete:
123+
fatalError("heap type other than `func` and `extern` is not implemented yet")
122124
}
123125
}
124126

0 commit comments

Comments
 (0)