Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update spectest Part 2 #109

Merged
merged 10 commits into from
Jul 23, 2024
2 changes: 1 addition & 1 deletion CI/install-wabt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ install_tools() {
if ! which wat2wasm > /dev/null; then
local build_dir=$(mktemp -d /tmp/WasmKit-wabt.XXXXXX)
mkdir -p $build_dir
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.33/wabt-1.0.33.tar.xz | tar xJ --strip-components=1 -C $build_dir
curl -L https://github.com/WebAssembly/wabt/releases/download/1.0.35/wabt-1.0.35.tar.xz | tar xJ --strip-components=1 -C $build_dir
cmake -B $build_dir/build -GNinja -DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local $build_dir
cmake --build $build_dir/build --target install
fi
Expand Down
11 changes: 10 additions & 1 deletion Sources/WAT/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ struct ElementExprCollector: AnyInstructionVisitor {

extension WAT.WatParser.ElementDecl {
func encode(to encoder: inout Encoder, wat: inout Wat) throws {
func isMemory64(tableIndex: Int) -> Bool {
guard tableIndex < wat.tablesMap.count else { return false }
return wat.tablesMap[tableIndex].type.limits.isMemory64
}

var flags: UInt32 = 0
var tableIndex: UInt32? = nil
var isPassive = false
Expand Down Expand Up @@ -233,7 +238,11 @@ extension WAT.WatParser.ElementDecl {
try encoder.writeExpression(lexer: &lexer, wat: &wat)
case .synthesized(let offset):
var exprEncoder = ExpressionEncoder()
try exprEncoder.visitI32Const(value: Int32(offset))
if isMemory64(tableIndex: Int(tableIndex ?? 0)) {
try exprEncoder.visitI64Const(value: Int64(offset))
} else {
try exprEncoder.visitI32Const(value: Int32(offset))
}
try exprEncoder.visitEnd()
encoder.output.append(contentsOf: exprEncoder.encoder.output)
}
Expand Down
12 changes: 9 additions & 3 deletions Sources/WAT/Parser/ExpressionParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,15 @@ struct ExpressionParser<Visitor: InstructionVisitor> {

// Condition may be absent
if try !parser.takeParenBlockStart("then") {
// Visit condition expr
_ = try foldedInstruction(visitor: &visitor, wat: &wat)
try parser.expectParenBlockStart("then")
// Visit condition instructions
while true {
guard try foldedInstruction(visitor: &visitor, wat: &wat) else {
break
}
if try parser.takeParenBlockStart("then") {
break
}
}
}
// Visit "if"
_ = try visit(&visitor)
Expand Down
43 changes: 24 additions & 19 deletions Sources/WAT/Parser/WatParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ struct WatParser {
let importNames = try inlineImport()
let type: TableType
var inlineElement: ElementDecl?
let isMemory64 = try expectAddressSpaceType()

if let refType = try maybeRefType() {
guard try parser.takeParenBlockStart("elem") else {
throw WatParserError("expected elem", location: parser.lexer.location())
Expand Down Expand Up @@ -259,11 +261,11 @@ struct WatParser {
limits: Limits(
min: numberOfItems,
max: numberOfItems,
isMemory64: false
isMemory64: isMemory64
)
)
} else {
type = try tableType()
type = try tableType(isMemory64: isMemory64)
}
kind = .table(
TableDecl(
Expand All @@ -280,13 +282,7 @@ struct WatParser {
let exports = try inlineExports()
let importNames = try inlineImport()

let isMemory64: Bool
if try parser.takeKeyword("i64") {
isMemory64 = true
} else {
_ = try parser.takeKeyword("i32")
isMemory64 = false
}
let isMemory64 = try expectAddressSpaceType()
let type: MemoryType
var data: DataSegmentDecl?
if try parser.takeParenBlockStart("data") {
Expand Down Expand Up @@ -481,27 +477,36 @@ struct WatParser {
return data
}

/// Expect "i32", "i64", or any other
/// - Returns: `true` if "i64", otherwise `false`
mutating func expectAddressSpaceType() throws -> Bool {
let isMemory64: Bool
if try parser.takeKeyword("i64") {
isMemory64 = true
} else {
_ = try parser.takeKeyword("i32")
isMemory64 = false
}
return isMemory64
}

mutating func tableType() throws -> TableType {
return try tableType(isMemory64: expectAddressSpaceType())
}

mutating func tableType(isMemory64: Bool) throws -> TableType {
let limits: Limits
if try parser.takeKeyword("i64") {
if isMemory64 {
limits = try limit64()
} else {
_ = try parser.takeKeyword("i32")
limits = try limit32()
}
let elementType = try refType()
return TableType(elementType: elementType, limits: limits)
}

mutating func memoryType() throws -> MemoryType {
let isMemory64: Bool
if try parser.takeKeyword("i64") {
isMemory64 = true
} else {
_ = try parser.takeKeyword("i32")
isMemory64 = false
}
return try memoryType(isMemory64: isMemory64)
return try memoryType(isMemory64: expectAddressSpaceType())
}

mutating func memoryType(isMemory64: Bool) throws -> MemoryType {
Expand Down
14 changes: 9 additions & 5 deletions Sources/WAT/WAT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ public struct Wat {
/// ```
public func parseWAT(_ input: String) throws -> Wat {
var parser = Parser(input)
try parser.expect(.leftParen)
try parser.expectKeyword("module")
let watModule = try parseWAT(&parser)
try parser.skipParenBlock()
return watModule
let wat: Wat
if try parser.takeParenBlockStart("module") {
wat = try parseWAT(&parser)
try parser.skipParenBlock()
} else {
// The root (module) may be omitted
wat = try parseWAT(&parser)
}
return wat
}

/// A WAST script representation.
Expand Down
2 changes: 1 addition & 1 deletion Sources/WasmKit/Execution/Instructions/Control.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ extension ExecutionState {
let tableAddresses = moduleInstance.tableAddresses[Int(tableIndex)]
let tableInstance = runtime.store.tables[tableAddresses]
let expectedType = moduleInstance.types[Int(typeIndex)]
let value = stack.popValue().i32
let value = stack.popValue().asAddressOffset(tableInstance.limits.isMemory64)
let elementIndex = Int(value)
guard elementIndex < tableInstance.elements.count else {
throw Trap.undefinedElement
Expand Down
12 changes: 7 additions & 5 deletions Sources/WasmKit/Execution/Instructions/Memory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,10 @@ extension ExecutionState {

let memoryAddress = moduleInstance.memoryAddresses[0]
try store.withMemory(at: memoryAddress) { memoryInstance in
let copyCounter = stack.popValue().i32
let sourceIndex = stack.popValue().i32
let destinationIndex = stack.popValue().i32
let isMemory64 = memoryInstance.limit.isMemory64
let copyCounter = stack.popValue().asAddressOffset(isMemory64)
let sourceIndex = stack.popValue().asAddressOffset(isMemory64)
let destinationIndex = stack.popValue().asAddressOffset(isMemory64)

guard copyCounter > 0 else { return }

Expand Down Expand Up @@ -244,9 +245,10 @@ extension ExecutionState {
let store = runtime.store
let memoryAddress = moduleInstance.memoryAddresses[0]
try store.withMemory(at: memoryAddress) { memoryInstance in
let copyCounter = Int(stack.popValue().i32)
let isMemory64 = memoryInstance.limit.isMemory64
let copyCounter = Int(stack.popValue().asAddressOffset(isMemory64))
let value = stack.popValue()
let destinationIndex = Int(stack.popValue().i32)
let destinationIndex = Int(stack.popValue().asAddressOffset(isMemory64))

guard
!destinationIndex.addingReportingOverflow(copyCounter).overflow
Expand Down
60 changes: 29 additions & 31 deletions Sources/WasmKit/Execution/Instructions/Table.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,44 @@ extension ExecutionState {

let reference = stack.getReference()
let elementIndex = try getElementIndex(stack: &stack, table)
setTableElement(store: runtime.store, tableAddress: tableAddress, elementIndex, reference)
setTableElement(store: runtime.store, tableAddress: tableAddress, Int(elementIndex), reference)

}
mutating func tableSize(runtime: Runtime, stack: inout Stack, tableIndex: TableIndex) {
let (_, table) = getTable(tableIndex, stack: &stack, store: runtime.store)
stack.push(value: .i32(UInt32(table.elements.count)))
let elementsCount = table.elements.count
stack.push(value: table.limits.isMemory64 ? .i64(UInt64(elementsCount)) : .i32(UInt32(elementsCount)))
}
mutating func tableGrow(runtime: Runtime, stack: inout Stack, tableIndex: TableIndex) {
let (tableAddress, table) = getTable(tableIndex, stack: &stack, store: runtime.store)

let growthSize = stack.popValue()

guard case let .i32(growthSize) = growthSize else {
fatalError("invalid value at the top of the stack \(growthSize)")
}

let growthSize = stack.popValue().asAddressOffset(table.limits.isMemory64)
let growthValue = stack.getReference()

let oldSize = UInt32(table.elements.count)
let oldSize = table.elements.count
guard runtime.store.tables[tableAddress].grow(by: growthSize, value: growthValue) else {
stack.push(value: .i32(Int32(-1).unsigned))
return
}

stack.push(value: .i32(oldSize))
stack.push(value: table.limits.isMemory64 ? .i64(UInt64(oldSize)) : .i32(UInt32(oldSize)))
}
mutating func tableFill(runtime: Runtime, stack: inout Stack, tableIndex: TableIndex) throws {
let (tableAddress, table) = getTable(tableIndex, stack: &stack, store: runtime.store)
let fillCounter = stack.popValue().i32
let fillCounter = stack.popValue().asAddressOffset(table.limits.isMemory64)
let fillValue = stack.getReference()
let startIndex = stack.popValue().i32
let startIndex = stack.popValue().asAddressOffset(table.limits.isMemory64)

guard fillCounter > 0 else {
return
}

guard Int(startIndex + fillCounter) <= table.elements.count else {
throw Trap.outOfBoundsTableAccess(index: startIndex + fillCounter)
throw Trap.outOfBoundsTableAccess(Int(startIndex + fillCounter))
}

for i in 0..<fillCounter {
setTableElement(store: runtime.store, tableAddress: tableAddress, startIndex + i, fillValue)
setTableElement(store: runtime.store, tableAddress: tableAddress, Int(startIndex + i), fillValue)
}
}
mutating func tableCopy(runtime: Runtime, stack: inout Stack, dest: TableIndex, src: TableIndex) throws {
Expand All @@ -67,9 +63,11 @@ extension ExecutionState {
let (_, sourceTable) = getTable(sourceTableIndex, stack: &stack, store: runtime.store)
let (destinationTableAddress, destinationTable) = getTable(destinationTableIndex, stack: &stack, store: runtime.store)

let copyCounter = stack.popValue().i32
let sourceIndex = stack.popValue().i32
let destinationIndex = stack.popValue().i32
let copyCounter = stack.popValue().asAddressOffset(
sourceTable.limits.isMemory64 || destinationTable.limits.isMemory64
)
let sourceIndex = stack.popValue().asAddressOffset(sourceTable.limits.isMemory64)
let destinationIndex = stack.popValue().asAddressOffset(destinationTable.limits.isMemory64)

guard copyCounter > 0 else {
return
Expand All @@ -81,17 +79,17 @@ extension ExecutionState {
throw Trap.tableSizeOverflow
}
guard destinationIndex + copyCounter <= sourceTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
throw Trap.outOfBoundsTableAccess(Int(destinationIndex + copyCounter))
}
guard destinationIndex + copyCounter <= sourceTable.elements.count && sourceIndex + copyCounter <= destinationTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
throw Trap.outOfBoundsTableAccess(Int(destinationIndex + copyCounter))
}

for i in 0..<copyCounter {
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
Int(destinationIndex + i),
sourceTable.elements[Int(sourceIndex + i)]
)
}
Expand All @@ -101,9 +99,9 @@ extension ExecutionState {
let elementAddress = currentModule(store: runtime.store, stack: &stack).elementAddresses[Int(elementIndex)]
let sourceElement = runtime.store.elements[elementAddress]

let copyCounter = stack.popValue().i32
let sourceIndex = stack.popValue().i32
let destinationIndex = stack.popValue().i32
let copyCounter = UInt64(stack.popValue().i32)
let sourceIndex = UInt64(stack.popValue().i32)
let destinationIndex = stack.popValue().asAddressOffset(destinationTable.limits.isMemory64)

guard copyCounter > 0 else {
return
Expand All @@ -116,10 +114,10 @@ extension ExecutionState {
}

guard sourceIndex + copyCounter <= sourceElement.references.count else {
throw Trap.outOfBoundsTableAccess(index: sourceIndex + copyCounter)
throw Trap.outOfBoundsTableAccess(Int(sourceIndex + copyCounter))
}
guard destinationIndex + copyCounter <= destinationTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
throw Trap.outOfBoundsTableAccess(Int(destinationIndex + copyCounter))
}

for i in 0..<copyCounter {
Expand All @@ -128,7 +126,7 @@ extension ExecutionState {
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
Int(destinationIndex + i),
reference
)
}
Expand All @@ -141,10 +139,10 @@ extension ExecutionState {
fileprivate func setTableElement(
store: Store,
tableAddress: TableAddress,
_ elementIndex: ElementIndex,
_ elementIndex: Int,
_ reference: Reference?
) {
store.tables[tableAddress].elements[Int(elementIndex)] = reference
store.tables[tableAddress].elements[elementIndex] = reference
}
}

Expand All @@ -155,13 +153,13 @@ extension ExecutionState {
}

fileprivate mutating func getElementIndex(stack: inout Stack, _ table: TableInstance) throws -> ElementIndex {
let elementIndex = stack.popValue().i32
let elementIndex = stack.popValue().asAddressOffset(table.limits.isMemory64)

guard elementIndex < table.elements.count else {
throw Trap.outOfBoundsTableAccess(index: elementIndex)
throw Trap.outOfBoundsTableAccess(Int(elementIndex))
}

return elementIndex
return ElementIndex(elementIndex)
}
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/WasmKit/Execution/Runtime/Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ extension Runtime {
switch offsetExpression.first {
case .i32Const(let value):
instructions.append(.numericConst(.i32(UInt32(bitPattern: value))))
case .i64Const(let value):
instructions.append(.numericConst(.i64(UInt64(bitPattern: value))))
case .globalGet(let index):
instructions.append(.globalGet(index: index))
default:
Expand Down
2 changes: 1 addition & 1 deletion Sources/WasmKit/Execution/Runtime/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ extension Store {
result.append(external)

case let (.table(tableType), .table(tableAddress)):
if let max = tables[Int(tableAddress)].max, max < tableType.limits.min {
if let max = tables[Int(tableAddress)].limits.max, max < tableType.limits.min {
throw ImportError.incompatibleImportType
}
result.append(external)
Expand Down
2 changes: 1 addition & 1 deletion Sources/WasmKit/Execution/Types/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum Trap: Error {

// Store
/// Out of bounds table access
case outOfBoundsTableAccess(index: ElementIndex)
case outOfBoundsTableAccess(Int)
/// Reading a dropped reference
case readingDroppedReference(index: ElementIndex)

Expand Down
Loading