From c7f0ae6150df339794b038dec2e0119c23cbb0aa Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Thu, 29 Aug 2024 14:08:50 +1200 Subject: [PATCH 1/2] Instructions refactor --- Blockchain/Sources/Blockchain/Safrole.swift | 4 +- .../PolkaVM/Instructions/BranchCompare.swift | 39 ++ .../Instructions/BranchInstructionBase.swift | 83 ++++ .../Instructions/Instructions+Helpers.swift | 80 +++ .../{ => Instructions}/Instructions.swift | 457 ++++-------------- PolkaVM/Sources/PolkaVM/VMState.swift | 4 + .../Sources/Utils/Extensions/Data+Utils.swift | 7 + 7 files changed, 297 insertions(+), 377 deletions(-) create mode 100644 PolkaVM/Sources/PolkaVM/Instructions/BranchCompare.swift create mode 100644 PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift create mode 100644 PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift rename PolkaVM/Sources/PolkaVM/{ => Instructions}/Instructions.swift (73%) diff --git a/Blockchain/Sources/Blockchain/Safrole.swift b/Blockchain/Sources/Blockchain/Safrole.swift index 6257e314..285c74a9 100644 --- a/Blockchain/Sources/Blockchain/Safrole.swift +++ b/Blockchain/Sources/Blockchain/Safrole.swift @@ -197,9 +197,7 @@ func generateFallbackIndices(entropy: Data32, count: Int, length: Int) throws -> hasher.update(Data(bytes)) let hash = hasher.finalize() let hash4 = hash.data[0 ..< 4] - let idx: UInt32 = hash4.withUnsafeBytes { ptr in - ptr.loadUnaligned(as: UInt32.self) - } + let idx = hash4.decode(UInt32.self) return Int(idx % UInt32(length)) } } diff --git a/PolkaVM/Sources/PolkaVM/Instructions/BranchCompare.swift b/PolkaVM/Sources/PolkaVM/Instructions/BranchCompare.swift new file mode 100644 index 00000000..f0ae3bf7 --- /dev/null +++ b/PolkaVM/Sources/PolkaVM/Instructions/BranchCompare.swift @@ -0,0 +1,39 @@ +protocol BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool +} + +struct CompareEq: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + Int32(bitPattern: a) == Int32(bitPattern: b) + } +} + +struct CompareNe: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + Int32(bitPattern: a) != Int32(bitPattern: b) + } +} + +struct CompareLt: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + a < b + } +} + +struct CompareLe: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + a <= b + } +} + +struct CompareGe: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + a >= b + } +} + +struct CompareGt: BranchCompare { + static func compare(a: UInt32, b: UInt32) -> Bool { + a > b + } +} diff --git a/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift b/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift new file mode 100644 index 00000000..879e6b92 --- /dev/null +++ b/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift @@ -0,0 +1,83 @@ +import Foundation + +// for branch in A.5.7 +protocol BranchInstructionBase: Instruction { + associatedtype Compare: BranchCompare + + var register: Registers.Index { get set } + var value: UInt32 { get set } + var offset: UInt32 { get set } + + func _executeImpl(state _: VMState) throws -> ExecOutcome + func updatePC(state: VMState, skip: UInt32) -> ExecOutcome + func condition(state: VMState) -> Bool +} + +extension BranchInstructionBase { + public static func parse(data: Data) throws -> (Registers.Index, UInt32, UInt32) { + let register = try Registers.Index(ra: data.at(relative: 0)) + let (value, offset) = try Instructions.decodeImmediate2(data, divideBy: 16) + return (register, value, offset) + } + + public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } + + public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(state: state, offset: offset) else { + return .exit(.panic(.invalidBranch)) + } + if condition(state: state) { + state.increasePC(offset) + } else { + state.increasePC(skip + 1) + } + return .continued + } + + public func condition(state: VMState) -> Bool { + let regVal = state.readRegister(register) + return Compare.compare(a: regVal, b: value) + } +} + +// for branch in A.5.10 +protocol BranchInstructionBase2: Instruction { + associatedtype Compare: BranchCompare + + var r1: Registers.Index { get set } + var r2: Registers.Index { get set } + var offset: UInt32 { get set } + + func _executeImpl(state _: VMState) throws -> ExecOutcome + func updatePC(state: VMState, skip: UInt32) -> ExecOutcome + func condition(state: VMState) -> Bool +} + +extension BranchInstructionBase2 { + public static func parse(data: Data) throws -> (Registers.Index, Registers.Index, UInt32) { + let offset = try Instructions.decodeImmediate(data.at(relative: 1...)) + let r1 = try Registers.Index(ra: data.at(relative: 0)) + let r2 = try Registers.Index(rb: data.at(relative: 0)) + return (r1, r2, offset) + } + + public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } + + public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(state: state, offset: offset) else { + return .exit(.panic(.invalidBranch)) + } + if condition(state: state) { + state.increasePC(offset) + } else { + state.increasePC(skip + 1) + } + return .continued + } + + public func condition(state: VMState) -> Bool { + let r1Val = state.readRegister(r1) + let r2Val = state.readRegister(r2) + return Compare.compare(a: r1Val, b: r2Val) + } +} diff --git a/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift b/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift new file mode 100644 index 00000000..81f2216a --- /dev/null +++ b/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift @@ -0,0 +1,80 @@ +import Foundation + +extension Instructions { + enum Constants { + static let djumpHaltAddress: UInt32 = 0xFFFF_0000 + static let djumpAddressAlignmentFactor: Int = 2 + } + + static func decodeImmediate(_ data: Data) -> UInt32 { + let len = min(data.count, 4) + if len == 0 { + return 0 + } + var value: UInt32 = 0 + for i in 0 ..< len { + value = value | (UInt32(data[relative: i]) << (8 * i)) + } + let shift = (4 - len) * 8 + // shift left so that the MSB is the sign bit + // and then do signed shift right to fill the empty bits using the sign bit + // and then convert back to UInt32 + return UInt32(bitPattern: Int32(bitPattern: value << shift) >> shift) + } + + static func decodeImmediate2(_ data: Data, divideBy: UInt8 = 1, minus: Int = 1) throws -> (UInt32, UInt32) { + let lX1 = try Int((data.at(relative: 0) / divideBy) & 0b111) + let lX = min(4, lX1) + let lY = min(4, max(0, data.count - Int(lX) - minus)) + + let vX = try decodeImmediate(data.at(relative: 1 ..< 1 + lX)) + let vY = try decodeImmediate(data.at(relative: (1 + lX) ..< (1 + lX + lY))) + return (vX, vY) + } + + static func isBranchValid(state: VMState, offset: UInt32) -> Bool { + state.program.basicBlockIndices.contains(state.pc &+ offset) + } + + static func isDjumpValid(state: VMState, target a: UInt32, targetAligned: UInt32) -> Bool { + let za = Constants.djumpAddressAlignmentFactor + return !(a == 0 || + a > state.program.jumpTable.count * za || + Int(a) % za != 0 || + state.program.basicBlockIndices.contains(targetAligned)) + } + + static func djump(state: VMState, target: UInt32) -> ExecOutcome { + if target == Constants.djumpHaltAddress { + return .exit(.halt) + } + + let entrySize = Int(state.program.jumpTableEntrySize) + let start = ((Int(target) / Constants.djumpAddressAlignmentFactor) - 1) * entrySize + let end = start + entrySize + var targetAlignedData = state.program.jumpTable[relative: start ..< end] + guard let targetAligned = targetAlignedData.decode() else { + fatalError("unreachable: jump table entry should be valid") + } + + guard isDjumpValid(state: state, target: target, targetAligned: UInt32(truncatingIfNeeded: targetAligned)) else { + return .exit(.panic(.invalidDynamicJump)) + } + + state.updatePC(UInt32(targetAligned)) + return .continued + } + + static func deocdeRegisters(_ data: Data) throws -> (Registers.Index, Registers.Index) { + let ra = try Registers.Index(ra: data.at(relative: 0)) + let rb = try Registers.Index(rb: data.at(relative: 0)) + return (ra, rb) + } + + static func deocdeRegisters(_ data: Data) throws -> (Registers.Index, Registers.Index, Registers.Index) { + let ra = try Registers.Index(ra: data.at(relative: 0)) + let rb = try Registers.Index(rb: data.at(relative: 0)) + let rd = try Registers.Index(rd: data.at(relative: 1)) + return (ra, rb, rd) + } +} diff --git a/PolkaVM/Sources/PolkaVM/Instructions.swift b/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift similarity index 73% rename from PolkaVM/Sources/PolkaVM/Instructions.swift rename to PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift index 9fe0ebf2..78bd1eb1 100644 --- a/PolkaVM/Sources/PolkaVM/Instructions.swift +++ b/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift @@ -27,70 +27,6 @@ public let BASIC_BLOCK_INSTRUCTIONS: Set = [ ] public enum Instructions { - public enum Constants { - public static let djumpHaltAddress: UInt32 = 0xFFFF_0000 - public static let djumpAddressAlignmentFactor: Int = 2 - } - - static func decodeImmediate(_ data: Data) -> UInt32 { - let len = min(data.count, 4) - if len == 0 { - return 0 - } - var value: UInt32 = 0 - for i in 0 ..< len { - value = value | (UInt32(data[relative: i]) << (8 * i)) - } - let shift = (4 - len) * 8 - // shift left so that the MSB is the sign bit - // and then do signed shift right to fill the empty bits using the sign bit - // and then convert back to UInt32 - return UInt32(bitPattern: Int32(bitPattern: value << shift) >> shift) - } - - static func decodeImmediate2(_ data: Data, divideBy: UInt8 = 1, minus: Int = 1) throws -> (UInt32, UInt32) { - let lX1 = try Int((data.at(relative: 0) / divideBy) & 0b111) - let lX = min(4, lX1) - let lY = min(4, max(0, data.count - Int(lX) - minus)) - - let vX = try decodeImmediate(data.at(relative: 1 ..< 1 + lX)) - let vY = try decodeImmediate(data.at(relative: (1 + lX) ..< (1 + lX + lY))) - return (vX, vY) - } - - static func isBranchValid(state: VMState, offset: UInt32) -> Bool { - state.program.basicBlockIndices.contains(state.pc &+ offset) - } - - static func isDjumpValid(state: VMState, target a: UInt32, targetAligned: UInt32) -> Bool { - let za = Constants.djumpAddressAlignmentFactor - return !(a == 0 || - a > state.program.jumpTable.count * za || - Int(a) % za != 0 || - state.program.basicBlockIndices.contains(targetAligned)) - } - - static func djump(state: VMState, target: UInt32) -> ExecOutcome { - if target == Constants.djumpHaltAddress { - return .exit(.halt) - } - - let entrySize = Int(state.program.jumpTableEntrySize) - let start = ((Int(target) / Constants.djumpAddressAlignmentFactor) - 1) * entrySize - let end = start + entrySize - var targetAlignedData = state.program.jumpTable[relative: start ..< end] - guard let targetAligned = targetAlignedData.decode() else { - fatalError("unreachable: jump table entry should be valid") - } - - guard isDjumpValid(state: state, target: target, targetAligned: UInt32(truncatingIfNeeded: targetAligned)) else { - return .exit(.panic(.invalidDynamicJump)) - } - - state.updatePC(UInt32(targetAligned)) - return .continued - } - // MARK: Instructions without Arguments (5.1) public struct Trap: Instruction { @@ -297,10 +233,8 @@ public enum Instructions { } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: address, length: 2) - guard let value: UInt16 = data.decode(length: 2) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: address, length: 2) + let value = data.decode(UInt16.self) state.writeRegister(register, UInt32(value)) return .continued } @@ -318,10 +252,8 @@ public enum Instructions { } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: address, length: 2) - guard let value: UInt16 = data.decode(length: 2) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: address, length: 2) + let value = data.decode(UInt16.self) state.writeRegister(register, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) return .continued } @@ -339,10 +271,8 @@ public enum Instructions { } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: address, length: 4) - guard let value: UInt32 = data.decode(length: 4) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: address, length: 4) + let value = data.decode(UInt32.self) state.writeRegister(register, value) return .continued } @@ -601,8 +531,7 @@ public enum Instructions { public let dest: Registers.Index public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { @@ -618,8 +547,7 @@ public enum Instructions { public let dest: Registers.Index public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) throws -> ExecOutcome { @@ -641,8 +569,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - src = try Registers.Index(ra: data.at(relative: 0)) - dest = try Registers.Index(rb: data.at(relative: 0)) + (src, dest) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -661,8 +588,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - src = try Registers.Index(ra: data.at(relative: 0)) - dest = try Registers.Index(rb: data.at(relative: 0)) + (src, dest) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -681,8 +607,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - src = try Registers.Index(ra: data.at(relative: 0)) - dest = try Registers.Index(rb: data.at(relative: 0)) + (src, dest) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -701,8 +626,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -721,8 +645,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -741,16 +664,13 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) - guard let value: UInt16 = data.decode(length: 2) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) + let value = data.decode(UInt16.self) state.writeRegister(dest, UInt32(value)) return .continued } @@ -764,16 +684,13 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) - guard let value: UInt16 = data.decode(length: 2) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) + let value = data.decode(UInt16.self) state.writeRegister(dest, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) return .continued } @@ -787,16 +704,13 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - dest = try Registers.Index(ra: data.at(relative: 0)) - src = try Registers.Index(rb: data.at(relative: 0)) + (dest, src) = try Instructions.deocdeRegisters(data) offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } public func _executeImpl(state: VMState) throws -> ExecOutcome { - var data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 4) - guard let value: UInt32 = data.decode(length: 4) else { - fatalError("unreachable: value should be valid") - } + let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 4) + let value = data.decode(UInt32.self) state.writeRegister(dest, value) return .continued } @@ -810,8 +724,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -830,8 +743,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -850,8 +762,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -870,8 +781,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -890,8 +800,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -910,8 +819,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -930,8 +838,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -950,8 +857,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -970,8 +876,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -990,8 +895,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1011,8 +915,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1032,8 +935,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1053,8 +955,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1073,8 +974,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1093,8 +993,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1113,8 +1012,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1134,8 +1032,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1155,8 +1052,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1176,8 +1072,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1196,8 +1091,7 @@ public enum Instructions { public let value: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) value = try Instructions.decodeImmediate(data.at(relative: 1...)) } @@ -1281,8 +1175,7 @@ public enum Instructions { public let offset: UInt32 public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) + (ra, rb) = try Instructions.deocdeRegisters(data) (value, offset) = try Instructions.decodeImmediate2(data[relative: 1...], divideBy: 1, minus: 2) } @@ -1307,14 +1200,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal &+ rbVal) return .continued } @@ -1328,14 +1218,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal &- rbVal) return .continued } @@ -1349,14 +1236,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal & rbVal) return .continued } @@ -1370,14 +1254,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal ^ rbVal) return .continued } @@ -1391,14 +1272,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal | rbVal) return .continued } @@ -1412,14 +1290,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal &* rbVal) return .continued } @@ -1433,14 +1308,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32))) return .continued } @@ -1454,14 +1326,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, UInt32((UInt64(raVal) * UInt64(rbVal)) >> 32)) return .continued } @@ -1475,14 +1344,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32))) return .continued } @@ -1496,14 +1362,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal == 0 { state.writeRegister(rd, UInt32.max) } else { @@ -1521,14 +1384,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal == 0 { state.writeRegister(rd, UInt32.max) } else if Int32(bitPattern: raVal) == Int32.min, Int32(bitPattern: rbVal) == -1 { @@ -1548,14 +1408,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal == 0 { state.writeRegister(rd, raVal) } else { @@ -1573,14 +1430,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal == 0 { state.writeRegister(rd, raVal) } else if Int32(bitPattern: raVal) == Int32.min, Int32(bitPattern: rbVal) == -1 { @@ -1600,14 +1454,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, raVal < rbVal ? 1 : 0) return .continued } @@ -1621,14 +1472,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) state.writeRegister(rd, Int32(bitPattern: raVal) < Int32(bitPattern: rbVal) ? 1 : 0) return .continued } @@ -1642,14 +1490,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) let shift = rbVal & 0x20 state.writeRegister(rd, UInt32(truncatingIfNeeded: raVal << shift)) return .continued @@ -1664,14 +1509,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) let shift = rbVal & 0x20 state.writeRegister(rd, raVal >> shift) return .continued @@ -1686,14 +1528,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) let shift = rbVal & 0x20 state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) >> shift)) return .continued @@ -1708,14 +1547,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal == 0 { state.writeRegister(rd, raVal) } @@ -1731,14 +1567,11 @@ public enum Instructions { public let rd: Registers.Index public init(data: Data) throws { - ra = try Registers.Index(ra: data.at(relative: 0)) - rb = try Registers.Index(rb: data.at(relative: 0)) - rd = try Registers.Index(rd: data.at(relative: 1)) + (ra, rb, rd) = try Instructions.deocdeRegisters(data) } public func _executeImpl(state: VMState) -> ExecOutcome { - let raVal = state.readRegister(ra) - let rbVal = state.readRegister(rb) + let (raVal, rbVal) = state.readRegister(ra, rb) if rbVal != 0 { state.writeRegister(rd, raVal) } @@ -1746,127 +1579,3 @@ public enum Instructions { } } } - -// MARK: Branch Helpers - -protocol BranchCompare { - static func compare(a: UInt32, b: UInt32) -> Bool -} - -// for branch in A.5.7 -protocol BranchInstructionBase: Instruction { - associatedtype Compare: BranchCompare - - var register: Registers.Index { get set } - var value: UInt32 { get set } - var offset: UInt32 { get set } - - func _executeImpl(state _: VMState) throws -> ExecOutcome - func updatePC(state: VMState, skip: UInt32) -> ExecOutcome - func condition(state: VMState) -> Bool -} - -extension BranchInstructionBase { - public static func parse(data: Data) throws -> (Registers.Index, UInt32, UInt32) { - let register = try Registers.Index(ra: data.at(relative: 0)) - let (value, offset) = try Instructions.decodeImmediate2(data, divideBy: 16) - return (register, value, offset) - } - - public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } - - public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { - return .exit(.panic(.invalidBranch)) - } - if condition(state: state) { - state.increasePC(offset) - } else { - state.increasePC(skip + 1) - } - return .continued - } - - public func condition(state: VMState) -> Bool { - let regVal = state.readRegister(register) - return Compare.compare(a: regVal, b: value) - } -} - -// for branch in A.5.10 -protocol BranchInstructionBase2: Instruction { - associatedtype Compare: BranchCompare - - var r1: Registers.Index { get set } - var r2: Registers.Index { get set } - var offset: UInt32 { get set } - - func _executeImpl(state _: VMState) throws -> ExecOutcome - func updatePC(state: VMState, skip: UInt32) -> ExecOutcome - func condition(state: VMState) -> Bool -} - -extension BranchInstructionBase2 { - public static func parse(data: Data) throws -> (Registers.Index, Registers.Index, UInt32) { - let offset = try Instructions.decodeImmediate(data.at(relative: 1...)) - let r1 = try Registers.Index(ra: data.at(relative: 0)) - let r2 = try Registers.Index(rb: data.at(relative: 0)) - return (r1, r2, offset) - } - - public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } - - public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { - return .exit(.panic(.invalidBranch)) - } - if condition(state: state) { - state.increasePC(offset) - } else { - state.increasePC(skip + 1) - } - return .continued - } - - public func condition(state: VMState) -> Bool { - let r1Val = state.readRegister(r1) - let r2Val = state.readRegister(r2) - return Compare.compare(a: r1Val, b: r2Val) - } -} - -public struct CompareEq: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - Int32(bitPattern: a) == Int32(bitPattern: b) - } -} - -public struct CompareNe: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - Int32(bitPattern: a) != Int32(bitPattern: b) - } -} - -public struct CompareLt: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - a < b - } -} - -public struct CompareLe: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - a <= b - } -} - -public struct CompareGe: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - a >= b - } -} - -public struct CompareGt: BranchCompare { - public static func compare(a: UInt32, b: UInt32) -> Bool { - a > b - } -} diff --git a/PolkaVM/Sources/PolkaVM/VMState.swift b/PolkaVM/Sources/PolkaVM/VMState.swift index 01d68aa1..c06c7312 100644 --- a/PolkaVM/Sources/PolkaVM/VMState.swift +++ b/PolkaVM/Sources/PolkaVM/VMState.swift @@ -68,6 +68,10 @@ public class VMState { registers[index] } + public func readRegister(_ index: Registers.Index, _ index2: Registers.Index) -> (UInt32, UInt32) { + (registers[index], registers[index2]) + } + public func writeRegister(_ index: Registers.Index, _ value: UInt32) { registers[index] = value } diff --git a/Utils/Sources/Utils/Extensions/Data+Utils.swift b/Utils/Sources/Utils/Extensions/Data+Utils.swift index 773307ec..8fd7f9f0 100644 --- a/Utils/Sources/Utils/Extensions/Data+Utils.swift +++ b/Utils/Sources/Utils/Extensions/Data+Utils.swift @@ -28,6 +28,13 @@ extension Data { public func toHexString() -> String { map { String(format: "%02x", $0) }.joined() } + + public func decode(_: T.Type) -> T { + assert(MemoryLayout.size <= count) + return withUnsafeBytes { ptr in + ptr.loadUnaligned(as: T.self) + } + } } extension FixedSizeData { From 312fe9d7c5a4bfd3e9ecdb4b0b6e7d9b3d1713e0 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Thu, 29 Aug 2024 14:51:49 +1200 Subject: [PATCH 2/2] execution context --- .../Blockchain/Config/ProtocolConfig.swift | 7 + JAMTests/Tests/JAMTests/PVMTests.swift | 2 +- PolkaVM/Sources/PolkaVM/Engine.swift | 21 +- PolkaVM/Sources/PolkaVM/Instruction.swift | 26 +- .../Instructions/BranchInstructionBase.swift | 28 +- .../Instructions/Instructions+Helpers.swift | 25 +- .../PolkaVM/Instructions/Instructions.swift | 445 +++++++++--------- .../PolkaVM/{Config.swift => PvmConfig.swift} | 14 + 8 files changed, 304 insertions(+), 264 deletions(-) rename PolkaVM/Sources/PolkaVM/{Config.swift => PvmConfig.swift} (53%) diff --git a/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift b/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift index 5f074c3b..80180e7b 100644 --- a/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift +++ b/Blockchain/Sources/Blockchain/Config/ProtocolConfig.swift @@ -183,6 +183,13 @@ public struct ProtocolConfig: Sendable { public typealias ProtocolConfigRef = Ref extension ProtocolConfig: PvmConfig {} +// silence the warning about cross module conformances as we owns all the code +extension Ref: @retroactive PvmConfig where T == ProtocolConfig { + public var pvmDynamicAddressAlignmentFactor: Int { value.pvmDynamicAddressAlignmentFactor } + public var pvmProgramInitInputDataSize: Int { value.pvmProgramInitInputDataSize } + public var pvmProgramInitPageSize: Int { value.pvmProgramInitPageSize } + public var pvmProgramInitSegmentSize: Int { value.pvmProgramInitSegmentSize } +} extension ProtocolConfig { public enum AuditTranchePeriod: ReadInt { diff --git a/JAMTests/Tests/JAMTests/PVMTests.swift b/JAMTests/Tests/JAMTests/PVMTests.swift index 0bbfa69e..390140c4 100644 --- a/JAMTests/Tests/JAMTests/PVMTests.swift +++ b/JAMTests/Tests/JAMTests/PVMTests.swift @@ -82,7 +82,7 @@ struct PVMTests { gas: UInt64(testCase.initialGas), memory: memory ) - let engine = Engine() + let engine = Engine(config: DefaultPvmConfig()) let exitReason = engine.execute(program: program, state: vmState) let exitReason2: Status = switch exitReason { case .halt: diff --git a/PolkaVM/Sources/PolkaVM/Engine.swift b/PolkaVM/Sources/PolkaVM/Engine.swift index f9138f02..fb63dcbd 100644 --- a/PolkaVM/Sources/PolkaVM/Engine.swift +++ b/PolkaVM/Sources/PolkaVM/Engine.swift @@ -1,26 +1,27 @@ import Foundation public class Engine { - public enum Constants { - public static let exitAddress: UInt32 = 0xFFFF_0000 - } + let config: PvmConfig - public init() {} + public init(config: PvmConfig) { + self.config = config + } public func execute(program: ProgramCode, state: VMState) -> ExitReason { + let context = ExecutionContext(state: state, config: config) while true { guard state.getGas() > 0 else { return .outOfGas } - if case let .exit(reason) = step(program: program, state: state) { + if case let .exit(reason) = step(program: program, context: context) { return reason } } } - public func step(program: ProgramCode, state: VMState) -> ExecOutcome { - let pc = state.pc - let skip = program.skip(state.pc) + public func step(program: ProgramCode, context: ExecutionContext) -> ExecOutcome { + let pc = context.state.pc + let skip = program.skip(pc) let startIndex = program.code.startIndex + Int(pc) let endIndex = startIndex + 1 + Int(skip) let data = if endIndex <= program.code.endIndex { @@ -32,8 +33,6 @@ public class Engine { return .exit(.panic(.invalidInstruction)) } - let res = inst.execute(state: state, skip: skip) - - return res + return inst.execute(context: context, skip: skip) } } diff --git a/PolkaVM/Sources/PolkaVM/Instruction.swift b/PolkaVM/Sources/PolkaVM/Instruction.swift index 12acab98..d4b808a6 100644 --- a/PolkaVM/Sources/PolkaVM/Instruction.swift +++ b/PolkaVM/Sources/PolkaVM/Instruction.swift @@ -6,21 +6,31 @@ public protocol Instruction { init(data: Data) throws func gasCost() -> UInt64 - func updatePC(state: VMState, skip: UInt32) -> ExecOutcome + func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome // protected method - func _executeImpl(state: VMState) throws -> ExecOutcome + func _executeImpl(context: ExecutionContext) throws -> ExecOutcome +} + +public class ExecutionContext { + public let state: VMState + public let config: PvmConfig + + public init(state: VMState, config: PvmConfig) { + self.state = state + self.config = config + } } extension Instruction { - public func execute(state: VMState, skip: UInt32) -> ExecOutcome { - state.consumeGas(gasCost()) + public func execute(context: ExecutionContext, skip: UInt32) -> ExecOutcome { + context.state.consumeGas(gasCost()) do { - let execRes = try _executeImpl(state: state) + let execRes = try _executeImpl(context: context) if case .exit = execRes { return execRes } - return updatePC(state: state, skip: skip) + return updatePC(context: context, skip: skip) } catch let e as Memory.Error { return .exit(.pageFault(e.address)) } catch { @@ -34,8 +44,8 @@ extension Instruction { 1 } - public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { - state.increasePC(skip + 1) + public func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome { + context.state.increasePC(skip + 1) return .continued } } diff --git a/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift b/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift index 879e6b92..387b18ba 100644 --- a/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift +++ b/PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift @@ -8,8 +8,6 @@ protocol BranchInstructionBase: Instruction { var value: UInt32 { get set } var offset: UInt32 { get set } - func _executeImpl(state _: VMState) throws -> ExecOutcome - func updatePC(state: VMState, skip: UInt32) -> ExecOutcome func condition(state: VMState) -> Bool } @@ -20,16 +18,16 @@ extension BranchInstructionBase { return (register, value, offset) } - public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } + public func _executeImpl(context _: ExecutionContext) throws -> ExecOutcome { .continued } - public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { + public func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(context: context, offset: offset) else { return .exit(.panic(.invalidBranch)) } - if condition(state: state) { - state.increasePC(offset) + if condition(state: context.state) { + context.state.increasePC(offset) } else { - state.increasePC(skip + 1) + context.state.increasePC(skip + 1) } return .continued } @@ -48,8 +46,6 @@ protocol BranchInstructionBase2: Instruction { var r2: Registers.Index { get set } var offset: UInt32 { get set } - func _executeImpl(state _: VMState) throws -> ExecOutcome - func updatePC(state: VMState, skip: UInt32) -> ExecOutcome func condition(state: VMState) -> Bool } @@ -61,16 +57,16 @@ extension BranchInstructionBase2 { return (r1, r2, offset) } - public func _executeImpl(state _: VMState) throws -> ExecOutcome { .continued } + public func _executeImpl(context _: ExecutionContext) throws -> ExecOutcome { .continued } - public func updatePC(state: VMState, skip: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { + public func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(context: context, offset: offset) else { return .exit(.panic(.invalidBranch)) } - if condition(state: state) { - state.increasePC(offset) + if condition(state: context.state) { + context.state.increasePC(offset) } else { - state.increasePC(skip + 1) + context.state.increasePC(skip + 1) } return .continued } diff --git a/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift b/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift index 81f2216a..c9584fe5 100644 --- a/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift +++ b/PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift @@ -3,7 +3,6 @@ import Foundation extension Instructions { enum Constants { static let djumpHaltAddress: UInt32 = 0xFFFF_0000 - static let djumpAddressAlignmentFactor: Int = 2 } static func decodeImmediate(_ data: Data) -> UInt32 { @@ -32,36 +31,36 @@ extension Instructions { return (vX, vY) } - static func isBranchValid(state: VMState, offset: UInt32) -> Bool { - state.program.basicBlockIndices.contains(state.pc &+ offset) + static func isBranchValid(context: ExecutionContext, offset: UInt32) -> Bool { + context.state.program.basicBlockIndices.contains(context.state.pc &+ offset) } - static func isDjumpValid(state: VMState, target a: UInt32, targetAligned: UInt32) -> Bool { - let za = Constants.djumpAddressAlignmentFactor + static func isDjumpValid(context: ExecutionContext, target a: UInt32, targetAligned: UInt32) -> Bool { + let za = context.config.pvmDynamicAddressAlignmentFactor return !(a == 0 || - a > state.program.jumpTable.count * za || + a > context.state.program.jumpTable.count * za || Int(a) % za != 0 || - state.program.basicBlockIndices.contains(targetAligned)) + context.state.program.basicBlockIndices.contains(targetAligned)) } - static func djump(state: VMState, target: UInt32) -> ExecOutcome { + static func djump(context: ExecutionContext, target: UInt32) -> ExecOutcome { if target == Constants.djumpHaltAddress { return .exit(.halt) } - let entrySize = Int(state.program.jumpTableEntrySize) - let start = ((Int(target) / Constants.djumpAddressAlignmentFactor) - 1) * entrySize + let entrySize = Int(context.state.program.jumpTableEntrySize) + let start = ((Int(target) / context.config.pvmDynamicAddressAlignmentFactor) - 1) * entrySize let end = start + entrySize - var targetAlignedData = state.program.jumpTable[relative: start ..< end] + var targetAlignedData = context.state.program.jumpTable[relative: start ..< end] guard let targetAligned = targetAlignedData.decode() else { fatalError("unreachable: jump table entry should be valid") } - guard isDjumpValid(state: state, target: target, targetAligned: UInt32(truncatingIfNeeded: targetAligned)) else { + guard isDjumpValid(context: context, target: target, targetAligned: UInt32(truncatingIfNeeded: targetAligned)) else { return .exit(.panic(.invalidDynamicJump)) } - state.updatePC(UInt32(targetAligned)) + context.state.updatePC(UInt32(targetAligned)) return .continued } diff --git a/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift b/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift index 78bd1eb1..3979b4f4 100644 --- a/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift +++ b/PolkaVM/Sources/PolkaVM/Instructions/Instructions.swift @@ -34,7 +34,7 @@ public enum Instructions { public init(data _: Data) {} - public func _executeImpl(state _: VMState) -> ExecOutcome { + public func _executeImpl(context _: ExecutionContext) -> ExecOutcome { .exit(.panic(.trap)) } } @@ -44,7 +44,7 @@ public enum Instructions { public init(data _: Data) {} - public func _executeImpl(state _: VMState) -> ExecOutcome { .continued } + public func _executeImpl(context _: ExecutionContext) -> ExecOutcome { .continued } } // MARK: Instructions with Arguments of One Immediate (5.2) @@ -58,7 +58,7 @@ public enum Instructions { callIndex = Instructions.decodeImmediate(data) } - public func _executeImpl(state _: VMState) -> ExecOutcome { + public func _executeImpl(context _: ExecutionContext) -> ExecOutcome { .exit(.hostCall(callIndex)) } } @@ -77,8 +77,8 @@ public enum Instructions { value = UInt8(truncatingIfNeeded: y) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - try state.writeMemory(address: address, value: value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + try context.state.writeMemory(address: address, value: value) return .continued } } @@ -95,8 +95,8 @@ public enum Instructions { value = UInt16(truncatingIfNeeded: y) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - try state.writeMemory(address: address, values: value.encode(method: .fixedWidth(2))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + try context.state.writeMemory(address: address, values: value.encode(method: .fixedWidth(2))) return .continued } } @@ -113,8 +113,8 @@ public enum Instructions { value = y } - public func _executeImpl(state: VMState) -> ExecOutcome { - if (try? state.writeMemory( + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + if (try? context.state.writeMemory( address: address, values: value.encode(method: .fixedWidth(4)) )) != nil { return .continued @@ -136,13 +136,13 @@ public enum Instructions { offset = Instructions.decodeImmediate(data) } - public func _executeImpl(state _: VMState) -> ExecOutcome { .continued } + public func _executeImpl(context _: ExecutionContext) -> ExecOutcome { .continued } - public func updatePC(state: VMState, skip _: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { + public func updatePC(context: ExecutionContext, skip _: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(context: context, offset: offset) else { return .exit(.panic(.invalidBranch)) } - state.increasePC(offset) + context.state.increasePC(offset) return .continued } } @@ -160,11 +160,11 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state _: VMState) -> ExecOutcome { .continued } + public func _executeImpl(context _: ExecutionContext) -> ExecOutcome { .continued } - public func updatePC(state: VMState, skip _: UInt32) -> ExecOutcome { - let regVal = state.readRegister(register) - return Instructions.djump(state: state, target: regVal &+ offset) + public func updatePC(context: ExecutionContext, skip _: UInt32) -> ExecOutcome { + let regVal = context.state.readRegister(register) + return Instructions.djump(context: context, target: regVal &+ offset) } } @@ -179,8 +179,8 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - state.writeRegister(register, value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + context.state.writeRegister(register, value) return .continued } } @@ -196,9 +196,9 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = try state.readMemory(address: address) - state.writeRegister(register, UInt32(value)) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = try context.state.readMemory(address: address) + context.state.writeRegister(register, UInt32(value)) return .continued } } @@ -214,9 +214,9 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = try state.readMemory(address: address) - state.writeRegister(register, UInt32(bitPattern: Int32(Int8(bitPattern: value)))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = try context.state.readMemory(address: address) + context.state.writeRegister(register, UInt32(bitPattern: Int32(Int8(bitPattern: value)))) return .continued } } @@ -232,10 +232,10 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: address, length: 2) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: address, length: 2) let value = data.decode(UInt16.self) - state.writeRegister(register, UInt32(value)) + context.state.writeRegister(register, UInt32(value)) return .continued } } @@ -251,10 +251,10 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: address, length: 2) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: address, length: 2) let value = data.decode(UInt16.self) - state.writeRegister(register, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) + context.state.writeRegister(register, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) return .continued } } @@ -270,10 +270,10 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: address, length: 4) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: address, length: 4) let value = data.decode(UInt32.self) - state.writeRegister(register, value) + context.state.writeRegister(register, value) return .continued } } @@ -289,9 +289,9 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = UInt8(truncatingIfNeeded: state.readRegister(register)) - try state.writeMemory(address: address, value: value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = UInt8(truncatingIfNeeded: context.state.readRegister(register)) + try context.state.writeMemory(address: address, value: value) return .continued } } @@ -307,9 +307,9 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = UInt16(truncatingIfNeeded: state.readRegister(register)) - try state.writeMemory(address: address, values: value.encode(method: .fixedWidth(2))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = UInt16(truncatingIfNeeded: context.state.readRegister(register)) + try context.state.writeMemory(address: address, values: value.encode(method: .fixedWidth(2))) return .continued } } @@ -325,9 +325,9 @@ public enum Instructions { address = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = state.readRegister(register) - try state.writeMemory(address: address, values: value.encode(method: .fixedWidth(4))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = context.state.readRegister(register) + try context.state.writeMemory(address: address, values: value.encode(method: .fixedWidth(4))) return .continued } } @@ -348,8 +348,8 @@ public enum Instructions { value = UInt8(truncatingIfNeeded: y) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - try state.writeMemory(address: state.readRegister(register) &+ address, value: value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + try context.state.writeMemory(address: context.state.readRegister(register) &+ address, value: value) return .continued } } @@ -368,8 +368,11 @@ public enum Instructions { value = UInt16(truncatingIfNeeded: y) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - try state.writeMemory(address: state.readRegister(register) &+ address, values: value.encode(method: .fixedWidth(2))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + try context.state.writeMemory( + address: context.state.readRegister(register) &+ address, + values: value.encode(method: .fixedWidth(2)) + ) return .continued } } @@ -388,8 +391,11 @@ public enum Instructions { value = y } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - try state.writeMemory(address: state.readRegister(register) &+ address, values: value.encode(method: .fixedWidth(4))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + try context.state.writeMemory( + address: context.state.readRegister(register) &+ address, + values: value.encode(method: .fixedWidth(4)) + ) return .continued } } @@ -408,16 +414,16 @@ public enum Instructions { (value, offset) = try Instructions.decodeImmediate2(data, divideBy: 16) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - state.writeRegister(register, value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + context.state.writeRegister(register, value) return .continued } - public func updatePC(state: VMState, skip _: UInt32) -> ExecOutcome { - guard Instructions.isBranchValid(state: state, offset: offset) else { + public func updatePC(context: ExecutionContext, skip _: UInt32) -> ExecOutcome { + guard Instructions.isBranchValid(context: context, offset: offset) else { return .exit(.panic(.invalidBranch)) } - state.increasePC(offset) + context.state.increasePC(offset) return .continued } } @@ -534,8 +540,8 @@ public enum Instructions { (dest, src) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - state.writeRegister(dest, state.readRegister(src)) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + context.state.writeRegister(dest, context.state.readRegister(src)) return .continued } } @@ -550,10 +556,10 @@ public enum Instructions { (dest, src) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let increment = state.readRegister(src) - let startAddr = try state.sbrk(increment) - state.writeRegister(dest, startAddr) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let increment = context.state.readRegister(src) + let startAddr = try context.state.sbrk(increment) + context.state.writeRegister(dest, startAddr) return .continued } @@ -573,9 +579,9 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = UInt8(truncatingIfNeeded: state.readRegister(src)) - try state.writeMemory(address: state.readRegister(dest) &+ offset, value: value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = UInt8(truncatingIfNeeded: context.state.readRegister(src)) + try context.state.writeMemory(address: context.state.readRegister(dest) &+ offset, value: value) return .continued } } @@ -592,9 +598,9 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = UInt16(truncatingIfNeeded: state.readRegister(src)) - try state.writeMemory(address: state.readRegister(dest) &+ offset, values: value.encode(method: .fixedWidth(2))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = UInt16(truncatingIfNeeded: context.state.readRegister(src)) + try context.state.writeMemory(address: context.state.readRegister(dest) &+ offset, values: value.encode(method: .fixedWidth(2))) return .continued } } @@ -611,9 +617,9 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = state.readRegister(src) - try state.writeMemory(address: state.readRegister(dest) &+ offset, values: value.encode(method: .fixedWidth(4))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = context.state.readRegister(src) + try context.state.writeMemory(address: context.state.readRegister(dest) &+ offset, values: value.encode(method: .fixedWidth(4))) return .continued } } @@ -630,9 +636,9 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = try state.readMemory(address: state.readRegister(src) + offset) - state.writeRegister(dest, UInt32(value)) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = try context.state.readMemory(address: context.state.readRegister(src) + offset) + context.state.writeRegister(dest, UInt32(value)) return .continued } } @@ -649,9 +655,9 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let value = try state.readMemory(address: state.readRegister(src) + offset) - state.writeRegister(dest, UInt32(bitPattern: Int32(Int8(bitPattern: value)))) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let value = try context.state.readMemory(address: context.state.readRegister(src) + offset) + context.state.writeRegister(dest, UInt32(bitPattern: Int32(Int8(bitPattern: value)))) return .continued } } @@ -668,10 +674,10 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: context.state.readRegister(src) &+ offset, length: 2) let value = data.decode(UInt16.self) - state.writeRegister(dest, UInt32(value)) + context.state.writeRegister(dest, UInt32(value)) return .continued } } @@ -688,10 +694,10 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 2) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: context.state.readRegister(src) &+ offset, length: 2) let value = data.decode(UInt16.self) - state.writeRegister(dest, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) + context.state.writeRegister(dest, UInt32(bitPattern: Int32(Int16(bitPattern: value)))) return .continued } } @@ -708,10 +714,10 @@ public enum Instructions { offset = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - let data = try state.readMemory(address: state.readRegister(src) &+ offset, length: 4) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + let data = try context.state.readMemory(address: context.state.readRegister(src) &+ offset, length: 4) let value = data.decode(UInt32.self) - state.writeRegister(dest, value) + context.state.writeRegister(dest, value) return .continued } } @@ -728,9 +734,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal &+ value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal &+ value) return .continued } } @@ -747,9 +753,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal & value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal & value) return .continued } } @@ -766,9 +772,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal ^ value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal ^ value) return .continued } } @@ -785,9 +791,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal | value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal | value) return .continued } } @@ -804,9 +810,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal &* value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal &* value) return .continued } } @@ -823,9 +829,12 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, UInt32(bitPattern: Int32((Int64(Int32(bitPattern: regVal)) * Int64(Int32(bitPattern: value))) >> 32))) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister( + ra, + UInt32(bitPattern: Int32((Int64(Int32(bitPattern: regVal)) * Int64(Int32(bitPattern: value))) >> 32)) + ) return .continued } } @@ -842,9 +851,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, UInt32((UInt64(regVal) * UInt64(value)) >> 32)) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, UInt32((UInt64(regVal) * UInt64(value)) >> 32)) return .continued } } @@ -861,9 +870,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal < value ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal < value ? 1 : 0) return .continued } } @@ -880,9 +889,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, Int32(bitPattern: regVal) < Int32(bitPattern: value) ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, Int32(bitPattern: regVal) < Int32(bitPattern: value) ? 1 : 0) return .continued } } @@ -899,10 +908,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = value & 0x20 - state.writeRegister(ra, UInt32(truncatingIfNeeded: regVal << shift)) + context.state.writeRegister(ra, UInt32(truncatingIfNeeded: regVal << shift)) return .continued } } @@ -919,10 +928,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = value & 0x20 - state.writeRegister(ra, regVal >> shift) + context.state.writeRegister(ra, regVal >> shift) return .continued } } @@ -939,10 +948,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = value & 0x20 - state.writeRegister(ra, UInt32(bitPattern: Int32(bitPattern: regVal) >> shift)) + context.state.writeRegister(ra, UInt32(bitPattern: Int32(bitPattern: regVal) >> shift)) return .continued } } @@ -959,9 +968,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal &- value) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal &- value) return .continued } } @@ -978,9 +987,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal > value ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal > value ? 1 : 0) return .continued } } @@ -997,9 +1006,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, Int32(bitPattern: regVal) > Int32(bitPattern: value) ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, Int32(bitPattern: regVal) > Int32(bitPattern: value) ? 1 : 0) return .continued } } @@ -1016,10 +1025,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = regVal & 0x20 - state.writeRegister(ra, UInt32(truncatingIfNeeded: value << shift)) + context.state.writeRegister(ra, UInt32(truncatingIfNeeded: value << shift)) return .continued } } @@ -1036,10 +1045,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = regVal & 0x20 - state.writeRegister(ra, value >> shift) + context.state.writeRegister(ra, value >> shift) return .continued } } @@ -1056,10 +1065,10 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) let shift = regVal & 0x20 - state.writeRegister(ra, UInt32(bitPattern: Int32(bitPattern: value) >> shift)) + context.state.writeRegister(ra, UInt32(bitPattern: Int32(bitPattern: value) >> shift)) return .continued } } @@ -1076,9 +1085,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal == 0 ? value : regVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal == 0 ? value : regVal) return .continued } } @@ -1095,9 +1104,9 @@ public enum Instructions { value = try Instructions.decodeImmediate(data.at(relative: 1...)) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let regVal = state.readRegister(rb) - state.writeRegister(ra, regVal != 0 ? value : regVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let regVal = context.state.readRegister(rb) + context.state.writeRegister(ra, regVal != 0 ? value : regVal) return .continued } } @@ -1179,14 +1188,14 @@ public enum Instructions { (value, offset) = try Instructions.decodeImmediate2(data[relative: 1...], divideBy: 1, minus: 2) } - public func _executeImpl(state: VMState) throws -> ExecOutcome { - state.writeRegister(ra, value) + public func _executeImpl(context: ExecutionContext) throws -> ExecOutcome { + context.state.writeRegister(ra, value) return .continued } - public func updatePC(state: VMState, skip _: UInt32) -> ExecOutcome { - let rbVal = state.readRegister(rb) - return Instructions.djump(state: state, target: rbVal &+ offset) + public func updatePC(context: ExecutionContext, skip _: UInt32) -> ExecOutcome { + let rbVal = context.state.readRegister(rb) + return Instructions.djump(context: context, target: rbVal &+ offset) } } @@ -1203,9 +1212,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal &+ rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal &+ rbVal) return .continued } } @@ -1221,9 +1230,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal &- rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal &- rbVal) return .continued } } @@ -1239,9 +1248,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal & rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal & rbVal) return .continued } } @@ -1257,9 +1266,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal ^ rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal ^ rbVal) return .continued } } @@ -1275,9 +1284,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal | rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal | rbVal) return .continued } } @@ -1293,9 +1302,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal &* rbVal) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal &* rbVal) return .continued } } @@ -1311,9 +1320,12 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32))) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister( + rd, + UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32)) + ) return .continued } } @@ -1329,9 +1341,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, UInt32((UInt64(raVal) * UInt64(rbVal)) >> 32)) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, UInt32((UInt64(raVal) * UInt64(rbVal)) >> 32)) return .continued } } @@ -1347,9 +1359,12 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32))) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister( + rd, + UInt32(bitPattern: Int32((Int64(Int32(bitPattern: raVal)) * Int64(Int32(bitPattern: rbVal))) >> 32)) + ) return .continued } } @@ -1365,12 +1380,12 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal == 0 { - state.writeRegister(rd, UInt32.max) + context.state.writeRegister(rd, UInt32.max) } else { - state.writeRegister(rd, raVal / rbVal) + context.state.writeRegister(rd, raVal / rbVal) } return .continued } @@ -1387,14 +1402,14 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal == 0 { - state.writeRegister(rd, UInt32.max) + context.state.writeRegister(rd, UInt32.max) } else if Int32(bitPattern: raVal) == Int32.min, Int32(bitPattern: rbVal) == -1 { - state.writeRegister(rd, raVal) + context.state.writeRegister(rd, raVal) } else { - state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) / Int32(bitPattern: rbVal))) + context.state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) / Int32(bitPattern: rbVal))) } return .continued } @@ -1411,12 +1426,12 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal == 0 { - state.writeRegister(rd, raVal) + context.state.writeRegister(rd, raVal) } else { - state.writeRegister(rd, raVal % rbVal) + context.state.writeRegister(rd, raVal % rbVal) } return .continued } @@ -1433,14 +1448,14 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal == 0 { - state.writeRegister(rd, raVal) + context.state.writeRegister(rd, raVal) } else if Int32(bitPattern: raVal) == Int32.min, Int32(bitPattern: rbVal) == -1 { - state.writeRegister(rd, 0) + context.state.writeRegister(rd, 0) } else { - state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) % Int32(bitPattern: rbVal))) + context.state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) % Int32(bitPattern: rbVal))) } return .continued } @@ -1457,9 +1472,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, raVal < rbVal ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, raVal < rbVal ? 1 : 0) return .continued } } @@ -1475,9 +1490,9 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) - state.writeRegister(rd, Int32(bitPattern: raVal) < Int32(bitPattern: rbVal) ? 1 : 0) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) + context.state.writeRegister(rd, Int32(bitPattern: raVal) < Int32(bitPattern: rbVal) ? 1 : 0) return .continued } } @@ -1493,10 +1508,10 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) let shift = rbVal & 0x20 - state.writeRegister(rd, UInt32(truncatingIfNeeded: raVal << shift)) + context.state.writeRegister(rd, UInt32(truncatingIfNeeded: raVal << shift)) return .continued } } @@ -1512,10 +1527,10 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) let shift = rbVal & 0x20 - state.writeRegister(rd, raVal >> shift) + context.state.writeRegister(rd, raVal >> shift) return .continued } } @@ -1531,10 +1546,10 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) let shift = rbVal & 0x20 - state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) >> shift)) + context.state.writeRegister(rd, UInt32(bitPattern: Int32(bitPattern: raVal) >> shift)) return .continued } } @@ -1550,10 +1565,10 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal == 0 { - state.writeRegister(rd, raVal) + context.state.writeRegister(rd, raVal) } return .continued } @@ -1570,10 +1585,10 @@ public enum Instructions { (ra, rb, rd) = try Instructions.deocdeRegisters(data) } - public func _executeImpl(state: VMState) -> ExecOutcome { - let (raVal, rbVal) = state.readRegister(ra, rb) + public func _executeImpl(context: ExecutionContext) -> ExecOutcome { + let (raVal, rbVal) = context.state.readRegister(ra, rb) if rbVal != 0 { - state.writeRegister(rd, raVal) + context.state.writeRegister(rd, raVal) } return .continued } diff --git a/PolkaVM/Sources/PolkaVM/Config.swift b/PolkaVM/Sources/PolkaVM/PvmConfig.swift similarity index 53% rename from PolkaVM/Sources/PolkaVM/Config.swift rename to PolkaVM/Sources/PolkaVM/PvmConfig.swift index 8828483d..2c8e120c 100644 --- a/PolkaVM/Sources/PolkaVM/Config.swift +++ b/PolkaVM/Sources/PolkaVM/PvmConfig.swift @@ -11,3 +11,17 @@ public protocol PvmConfig { // ZQ = 2^16: The standard pvm program initialization segment size. var pvmProgramInitSegmentSize: Int { get } } + +public struct DefaultPvmConfig: PvmConfig { + public let pvmDynamicAddressAlignmentFactor: Int + public let pvmProgramInitInputDataSize: Int + public let pvmProgramInitPageSize: Int + public let pvmProgramInitSegmentSize: Int + + public init() { + pvmDynamicAddressAlignmentFactor = 2 + pvmProgramInitInputDataSize = 1 << 24 + pvmProgramInitPageSize = 1 << 14 + pvmProgramInitSegmentSize = 1 << 16 + } +}