Skip to content

Commit

Permalink
memory wip
Browse files Browse the repository at this point in the history
  • Loading branch information
xlc committed Jul 22, 2024
1 parent 217c624 commit dc7f255
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 55 deletions.
56 changes: 56 additions & 0 deletions PolkaVM/Sources/PolkaVM/Memory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import Foundation

public class Memory {
public enum Error: Swift.Error {
case pageFault
case notWritable
}

private let pageMap: [(address: UInt32, length: UInt32, writable: Bool)]
private var chunks: [(address: UInt32, data: Data)]

public init(pageMap: [(address: UInt32, length: UInt32, writable: Bool)], chunks: [(address: UInt32, data: Data)]) {
self.pageMap = pageMap
self.chunks = chunks
}

public func read(_ address: UInt32) throws(Memory.Error) -> UInt8 {
// TODO: optimize this
// check for chunks
for chunk in chunks {
if chunk.address <= address, address < chunk.address + UInt32(chunk.data.count) {
return chunk.data[Int(address - chunk.address)]
}
}
// check for page map
for page in pageMap {
if page.address <= address, address < page.address + page.length {
return 0
}
}
throw Error.pageFault
}

public func write(address: UInt32, value: UInt8) throws(Memory.Error) {
// TODO: optimize this
// check for chunks
for i in 0 ..< chunks.count {
var chunk = chunks[i]
if chunk.address <= address, address < chunk.address + UInt32(chunk.data.count) {
chunk.data[Int(address - chunk.address)] = value
chunks[i] = chunk
return
}
}
// check for page map
for page in pageMap {
if page.address <= address, address < page.address + page.length {
var newChunk = (address: address, data: Data(repeating: 0, count: Int(page.length)))
newChunk.data[Int(address - page.address)] = value
chunks.append(newChunk)
return
}
}
throw Error.notWritable
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
import Utils

public class Program {
public class ProgramCode {
public enum Error: Swift.Error {
case invalidJumpTableEntriesCount
case invalidJumpTableEncodeSize
Expand All @@ -13,6 +13,7 @@ public class Program {
public static let maxJumpTableEntriesCount: UInt64 = 0x100000
public static let maxEncodeSize: UInt8 = 8
public static let maxCodeLength: UInt64 = 0x400000
public static let maxInstructionLength: UInt = 24
}

public let blob: Data
Expand Down Expand Up @@ -61,10 +62,43 @@ public class Program {

bitmask = Slice(base: blob, bounds: codeEndIndex ..< slice.endIndex)
}

public static func skipOffset<D>(start: UInt, bitmask: D) -> UInt? where D: Collection, D.Element == UInt8, D.Index == Int, D: ContiguousBytes {
let start = start + 1
let beginIndex = Int(start / 8)
guard beginIndex < bitmask.endIndex else {
return nil
}

var value: UInt32 = 0
if (beginIndex + 4) < bitmask.endIndex { // if enough bytes
value = bitmask.withUnsafeBytes { $0.loadUnaligned(fromByteOffset: beginIndex, as: UInt32.self) }
} else {
let byte1 = UInt32(bitmask[beginIndex])
let byte2 = UInt32(bitmask[safe: beginIndex + 1] ?? 0)
let byte3 = UInt32(bitmask[safe: beginIndex + 2] ?? 0)
let byte4 = UInt32(bitmask[safe: beginIndex + 3] ?? 0)
value = byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)
}

let offsetBits = start % 8

let idx = min(UInt((value >> offsetBits).trailingZeroBitCount), Constants.maxInstructionLength)

return idx
}

public func skip(start: UInt) -> UInt? {
if let val = ProgramCode.skipOffset(start: start, bitmask: bitmask) {
val + start + 1
} else {
nil
}
}
}

extension Program: Equatable {
public static func == (lhs: Program, rhs: Program) -> Bool {
extension ProgramCode: Equatable {
public static func == (lhs: ProgramCode, rhs: ProgramCode) -> Bool {
lhs.blob == rhs.blob
}
}
13 changes: 13 additions & 0 deletions PolkaVM/Sources/PolkaVM/VMState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

public class VMState {
public let program: ProgramCode

public private(set) var instructionCounter: UInt32

public private(set) var registers: (
UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32
) // 13 registers
public private(set) var gas: UInt64
public private(set) var memory: Data
}
69 changes: 69 additions & 0 deletions PolkaVM/Tests/PolkaVMTests/ProgramCodeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation
import Testing
import Utils

@testable import PolkaVM

struct ProgramTests {
@Test func empty() {
let blob = Data()
#expect(throws: ProgramCode.Error.invalidJumpTableEntriesCount) { try ProgramCode(blob) }
}

@Test func invalidJumpTableEntriesCount() {
let highValue = Data(UInt64(0x1000000).encode(method: .variableWidth))
let data = highValue + Data([0, 0])
#expect(throws: ProgramCode.Error.invalidJumpTableEntriesCount) { try ProgramCode(data) }
}

@Test func invalidJumpTableEncodeSize() {
let data = Data([1, 0xFF, 0, 0])
#expect(throws: ProgramCode.Error.invalidJumpTableEncodeSize) { try ProgramCode(data) }
}

@Test func invalidCodeLength() {
let highValue = Data(UInt64(0x1000000).encode(method: .variableWidth))
let data = Data([0, 0]) + highValue
#expect(throws: ProgramCode.Error.invalidCodeLength) { try ProgramCode(data) }
}

@Test func tooMuchData() throws {
let data = Data([0, 0, 2, 1, 2, 0, 0])
#expect(throws: ProgramCode.Error.invalidDataLength) { try ProgramCode(data) }
}

@Test func tooLittleData() throws {
let data = Data([0, 0, 2, 1, 2])
#expect(throws: ProgramCode.Error.invalidDataLength) { try ProgramCode(data) }
}

@Test func minimal() throws {
let data = Data([0, 0, 0])
_ = try ProgramCode(data)
}

@Test func simple() throws {
let data = Data([0, 0, 2, 1, 2, 0])
_ = try ProgramCode(data)
}

// TODO: add more Program parsing tests

@Test(arguments: [
(Data(), 0, nil),
(Data([0]), 0, 24),
(Data([0]), 8, nil),
(Data([0b0010_0000]), 0, 4),
(Data([0b0010_0000]), 3, 1),
(Data([0b0010_0000]), 6, 24),
(Data([0b0010_0000]), 7, nil),
(Data([0, 0, 0b0010_0000, 0b0000_0010]), 0, 20),
(Data([0, 0, 0b0010_0000, 0b0000_0010]), 2, 18),
(Data([0, 0, 0b0010_0000, 0b0000_0010]), 10, 10),
(Data([0, 0, 0b0010_0000, 0b0000_0010]), 22, 2),
(Data([0, 0, 0, 0b0000_0010]), 5, 19),
] as[(Data, UInt, UInt?)])
func skip(testCase: (Data, UInt, UInt?)) {
#expect(ProgramCode.skipOffset(start: testCase.1, bitmask: testCase.0) == testCase.2)
}
}
51 changes: 0 additions & 51 deletions PolkaVM/Tests/PolkaVMTests/ProgramTests.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Utils/Sources/Utils/Collection+Utils.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
extension Collection {
subscript(safe index: Index) -> Element? {
public subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}

0 comments on commit dc7f255

Please sign in to comment.