Skip to content

Commit

Permalink
Merge pull request #67 from MaxDesiatov/maxd/extract-execution-state
Browse files Browse the repository at this point in the history
Extract `ExecutionState` from the `Runtime` type
  • Loading branch information
kateinoigakukun committed Nov 15, 2023
2 parents ff44b0a + 7353d2b commit a5e045d
Show file tree
Hide file tree
Showing 12 changed files with 408 additions and 373 deletions.
1 change: 0 additions & 1 deletion Sources/Spectest/TestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ extension TestCase {
) { command, result in
handler(self, command, result)
}
assert(runtime.isStackEmpty)
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/WasmKit/Component/CanonicalCall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public struct CanonicalCallContext {
guard let realloc = options.realloc else {
throw CanonicalABIError(description: "Missing required \"cabi_realloc\" export")
}
let results = try runtime.invoke(realloc, with: [.i32(old), .i32(oldSize), .i32(oldAlign), .i32(newSize)])
let results = try realloc.invoke(
[.i32(old), .i32(oldSize), .i32(oldAlign), .i32(newSize)], runtime: runtime
)
guard results.count == 1 else {
throw CanonicalABIError(description: "\"cabi_realloc\" export should return a single value")
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/WasmKit/Component/CanonicalOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ public struct CanonicalOptions {
/// The string encoding used for lifting or lowering string values.
public let stringEncoding: StringEncoding
/// The realloc function address used for lifting or lowering values.
public let realloc: FunctionAddress?
public let realloc: Function?
/// The function address called when a lifted/lowered function returns.
public let postReturn: FunctionAddress?
public let postReturn: Function?

public init(
memory: MemoryAddress, stringEncoding: StringEncoding,
realloc: FunctionAddress?, postReturn: FunctionAddress?
realloc: Function?, postReturn: Function?
) {
self.memory = memory
self.stringEncoding = stringEncoding
Expand Down
60 changes: 30 additions & 30 deletions Sources/WasmKit/Execution/Instructions/Control.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ enum ControlInstruction: Equatable {
case call(functionIndex: UInt32)
case callIndirect(tableIndex: TableIndex, typeIndex: TypeIndex)

func execute(runtime: Runtime) throws {
func execute(runtime: Runtime, execution: inout ExecutionState) throws {
switch self {
case .unreachable:
throw Trap.unreachable

case .nop:
runtime.programCounter += 1
execution.programCounter += 1

case let .block(expression, type):
let (paramSize, resultSize) = type.arity(typeSection: { runtime.stack.currentFrame.module.types })
let values = try runtime.stack.popValues(count: paramSize)
runtime.enter(expression, continuation: runtime.programCounter + 1, arity: resultSize)
runtime.stack.push(values: values)
let (paramSize, resultSize) = type.arity(typeSection: { execution.stack.currentFrame.module.types })
let values = try execution.stack.popValues(count: paramSize)
execution.enter(expression, continuation: execution.programCounter + 1, arity: resultSize)
execution.stack.push(values: values)

case let .loop(expression, type):
let (paramSize, _) = type.arity(typeSection: { runtime.stack.currentFrame.module.types })
let values = try runtime.stack.popValues(count: paramSize)
runtime.enter(expression, continuation: runtime.programCounter, arity: paramSize)
runtime.stack.push(values: values)
let (paramSize, _) = type.arity(typeSection: { execution.stack.currentFrame.module.types })
let values = try execution.stack.popValues(count: paramSize)
execution.enter(expression, continuation: execution.programCounter, arity: paramSize)
execution.stack.push(values: values)

case let .if(then, `else`, type):
let isTrue = try runtime.stack.popValue().i32 != 0
let isTrue = try execution.stack.popValue().i32 != 0

let expression: Expression
if isTrue {
Expand All @@ -45,62 +45,62 @@ enum ControlInstruction: Equatable {

if !expression.instructions.isEmpty {
let derived = ControlInstruction.block(expression: expression, type: type)
try derived.execute(runtime: runtime)
try derived.execute(runtime: runtime, execution: &execution)
} else {
runtime.programCounter += 1
execution.programCounter += 1
}

case let .brIf(labelIndex):
guard try runtime.stack.popValue().i32 != 0 else {
runtime.programCounter += 1
guard try execution.stack.popValue().i32 != 0 else {
execution.programCounter += 1
return
}

fallthrough

case let .br(labelIndex):
try runtime.branch(labelIndex: Int(labelIndex))
try execution.branch(labelIndex: Int(labelIndex))

case let .brTable(labelIndices, defaultLabelIndex):
let value = try runtime.stack.popValue().i32
let value = try execution.stack.popValue().i32
let labelIndex: LabelIndex
if labelIndices.indices.contains(Int(value)) {
labelIndex = labelIndices[Int(value)]
} else {
labelIndex = defaultLabelIndex
}

try runtime.branch(labelIndex: Int(labelIndex))
try execution.branch(labelIndex: Int(labelIndex))

case .return:
let values = try runtime.stack.popValues(count: runtime.stack.currentFrame.arity)
let values = try execution.stack.popValues(count: execution.stack.currentFrame.arity)

let currentFrame = Stack.Element.frame(runtime.stack.currentFrame)
let currentFrame = Stack.Element.frame(execution.stack.currentFrame)
var lastLabel: Label?
while runtime.stack.top != currentFrame {
runtime.stack.discardTopValues()
lastLabel = try runtime.stack.popLabel()
while execution.stack.top != currentFrame {
execution.stack.discardTopValues()
lastLabel = try execution.stack.popLabel()
}
if let lastLabel {
runtime.programCounter = lastLabel.continuation
execution.programCounter = lastLabel.continuation
}
runtime.stack.push(values: values)
execution.stack.push(values: values)

case let .call(functionIndex):
let functionAddresses = runtime.stack.currentFrame.module.functionAddresses
let functionAddresses = execution.stack.currentFrame.module.functionAddresses

guard functionAddresses.indices.contains(Int(functionIndex)) else {
throw Trap.invalidFunctionIndex(functionIndex)
}

try runtime.invoke(functionAddress: functionAddresses[Int(functionIndex)])
try execution.invoke(functionAddress: functionAddresses[Int(functionIndex)], runtime: runtime)

case let .callIndirect(tableIndex, typeIndex):
let moduleInstance = runtime.stack.currentFrame.module
let moduleInstance = execution.stack.currentFrame.module
let tableAddresses = moduleInstance.tableAddresses[Int(tableIndex)]
let tableInstance = runtime.store.tables[tableAddresses]
let expectedType = moduleInstance.types[Int(typeIndex)]
let value = try runtime.stack.popValue().i32
let value = try execution.stack.popValue().i32
let elementIndex = Int(value)
guard elementIndex < tableInstance.elements.count else {
throw Trap.undefinedElement
Expand All @@ -114,7 +114,7 @@ enum ControlInstruction: Equatable {
throw Trap.callIndirectFunctionTypeMismatch(actual: function.type, expected: expectedType)
}

try runtime.invoke(functionAddress: functionAddress)
try execution.invoke(functionAddress: functionAddress, runtime: runtime)
}
}
}
Expand Down
91 changes: 51 additions & 40 deletions Sources/WasmKit/Execution/Instructions/Table.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,55 @@ enum TableInstruction: Equatable {
case `init`(TableIndex, ElementIndex)
case elementDrop(ElementIndex)

func execute(runtime: Runtime) throws {
func execute(runtime: Runtime, execution: inout ExecutionState) throws {
switch self {
case let .get(tableIndex):
let (_, table) = try runtime.getTable(tableIndex)
let (_, table) = try execution.getTable(tableIndex, store: runtime.store)

let elementIndex = try runtime.getElementIndex(table)
let elementIndex = try execution.getElementIndex(table)

guard let reference = table.elements[Int(elementIndex)] else {
throw Trap.readingDroppedReference(index: elementIndex)
}

runtime.stack.push(value: .ref(reference))
execution.stack.push(value: .ref(reference))

case let .set(tableIndex):
let (tableAddress, table) = try runtime.getTable(tableIndex)
let (tableAddress, table) = try execution.getTable(tableIndex, store: runtime.store)

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

case let .size(tableIndex):
let (_, table) = try runtime.getTable(tableIndex)
let (_, table) = try execution.getTable(tableIndex, store: runtime.store)

runtime.stack.push(value: .i32(UInt32(table.elements.count)))
execution.stack.push(value: .i32(UInt32(table.elements.count)))

case let .grow(tableIndex):
let (tableAddress, table) = try runtime.getTable(tableIndex)
let (tableAddress, table) = try execution.getTable(tableIndex, store: runtime.store)

let growthSize = try runtime.stack.popValue()
let growthSize = try execution.stack.popValue()

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

let growthValue = try runtime.stack.getReference()
let growthValue = try execution.stack.getReference()

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

runtime.stack.push(value: .i32(oldSize))
execution.stack.push(value: .i32(oldSize))

case let .fill(tableIndex):
let (tableAddress, table) = try runtime.getTable(tableIndex)
let fillCounter = try runtime.stack.popValue().i32
let fillValue = try runtime.stack.getReference()
let startIndex = try runtime.stack.popValue().i32
let (tableAddress, table) = try execution.getTable(tableIndex, store: runtime.store)
let fillCounter = try execution.stack.popValue().i32
let fillValue = try execution.stack.getReference()
let startIndex = try execution.stack.popValue().i32

guard fillCounter > 0 else {
break
Expand All @@ -69,16 +69,16 @@ enum TableInstruction: Equatable {
}

for i in 0..<fillCounter {
runtime.setTableElement(tableAddress: tableAddress, startIndex + i, fillValue)
setTableElement(store: runtime.store, tableAddress: tableAddress, startIndex + i, fillValue)
}

case let .copy(destinationTableIndex, sourceTableIndex):
let (_, sourceTable) = try runtime.getTable(sourceTableIndex)
let (destinationTableAddress, destinationTable) = try runtime.getTable(destinationTableIndex)
let (_, sourceTable) = try execution.getTable(sourceTableIndex, store: runtime.store)
let (destinationTableAddress, destinationTable) = try execution.getTable(destinationTableIndex, store: runtime.store)

let copyCounter = try runtime.stack.popValue().i32
let sourceIndex = try runtime.stack.popValue().i32
let destinationIndex = try runtime.stack.popValue().i32
let copyCounter = try execution.stack.popValue().i32
let sourceIndex = try execution.stack.popValue().i32
let destinationIndex = try execution.stack.popValue().i32

guard copyCounter > 0 else {
break
Expand All @@ -97,21 +97,22 @@ enum TableInstruction: Equatable {
}

for i in 0..<copyCounter {
runtime.setTableElement(
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
sourceTable.elements[Int(sourceIndex + i)]
)
}

case let .`init`(tableIndex, elementIndex):
let (destinationTableAddress, destinationTable) = try runtime.getTable(tableIndex)
let elementAddress = runtime.stack.currentFrame.module.elementAddresses[Int(elementIndex)]
let (destinationTableAddress, destinationTable) = try execution.getTable(tableIndex, store: runtime.store)
let elementAddress = execution.stack.currentFrame.module.elementAddresses[Int(elementIndex)]
let sourceElement = runtime.store.elements[elementAddress]

let copyCounter = try runtime.stack.popValue().i32
let sourceIndex = try runtime.stack.popValue().i32
let destinationIndex = try runtime.stack.popValue().i32
let copyCounter = try execution.stack.popValue().i32
let sourceIndex = try execution.stack.popValue().i32
let destinationIndex = try execution.stack.popValue().i32

guard copyCounter > 0 else {
break
Expand All @@ -133,23 +134,37 @@ enum TableInstruction: Equatable {
for i in 0..<copyCounter {
let reference = sourceElement.references[Int(sourceIndex + i)]

runtime.setTableElement(tableAddress: destinationTableAddress, destinationIndex + i, reference)
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
reference
)
}

case let .elementDrop(elementIndex):
let elementAddress = runtime.stack.currentFrame.module.elementAddresses[Int(elementIndex)]
let elementAddress = execution.stack.currentFrame.module.elementAddresses[Int(elementIndex)]
runtime.store.elements[elementAddress].drop()
}
}

fileprivate func setTableElement(
store: Store,
tableAddress: TableAddress,
_ elementIndex: ElementIndex,
_ reference: Reference?
) {
store.tables[tableAddress].elements[Int(elementIndex)] = reference
}
}

extension Runtime {
fileprivate func getTable(_ tableIndex: UInt32) throws -> (TableAddress, TableInstance) {
extension ExecutionState {
fileprivate func getTable(_ tableIndex: UInt32, store: Store) throws -> (TableAddress, TableInstance) {
let address = stack.currentFrame.module.tableAddresses[Int(tableIndex)]
return (address, store.tables[address])
}

fileprivate func getElementIndex(_ table: TableInstance) throws -> ElementIndex {
fileprivate mutating func getElementIndex(_ table: TableInstance) throws -> ElementIndex {
let elementIndex = try stack.popValue().i32

guard elementIndex < table.elements.count else {
Expand All @@ -158,10 +173,6 @@ extension Runtime {

return elementIndex
}

fileprivate func setTableElement(tableAddress: TableAddress, _ elementIndex: ElementIndex, _ reference: Reference?) {
store.tables[tableAddress].elements[Int(elementIndex)] = reference
}
}

extension Stack {
Expand Down
Loading

0 comments on commit a5e045d

Please sign in to comment.