-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into dev_networking
* master: Instructions refactor (#89)
- Loading branch information
Showing
12 changed files
with
546 additions
and
586 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
PolkaVM/Sources/PolkaVM/Instructions/BranchInstructionBase.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import Foundation | ||
|
||
// for branch in A.5.7 | ||
protocol BranchInstructionBase<Compare>: Instruction { | ||
associatedtype Compare: BranchCompare | ||
|
||
var register: Registers.Index { get set } | ||
var value: UInt32 { get set } | ||
var offset: UInt32 { get set } | ||
|
||
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(context _: ExecutionContext) throws -> ExecOutcome { .continued } | ||
|
||
public func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome { | ||
guard Instructions.isBranchValid(context: context, offset: offset) else { | ||
return .exit(.panic(.invalidBranch)) | ||
} | ||
if condition(state: context.state) { | ||
context.state.increasePC(offset) | ||
} else { | ||
context.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<Compare>: Instruction { | ||
associatedtype Compare: BranchCompare | ||
|
||
var r1: Registers.Index { get set } | ||
var r2: Registers.Index { get set } | ||
var offset: UInt32 { get set } | ||
|
||
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(context _: ExecutionContext) throws -> ExecOutcome { .continued } | ||
|
||
public func updatePC(context: ExecutionContext, skip: UInt32) -> ExecOutcome { | ||
guard Instructions.isBranchValid(context: context, offset: offset) else { | ||
return .exit(.panic(.invalidBranch)) | ||
} | ||
if condition(state: context.state) { | ||
context.state.increasePC(offset) | ||
} else { | ||
context.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) | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
PolkaVM/Sources/PolkaVM/Instructions/Instructions+Helpers.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import Foundation | ||
|
||
extension Instructions { | ||
enum Constants { | ||
static let djumpHaltAddress: UInt32 = 0xFFFF_0000 | ||
} | ||
|
||
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(context: ExecutionContext, offset: UInt32) -> Bool { | ||
context.state.program.basicBlockIndices.contains(context.state.pc &+ offset) | ||
} | ||
|
||
static func isDjumpValid(context: ExecutionContext, target a: UInt32, targetAligned: UInt32) -> Bool { | ||
let za = context.config.pvmDynamicAddressAlignmentFactor | ||
return !(a == 0 || | ||
a > context.state.program.jumpTable.count * za || | ||
Int(a) % za != 0 || | ||
context.state.program.basicBlockIndices.contains(targetAligned)) | ||
} | ||
|
||
static func djump(context: ExecutionContext, target: UInt32) -> ExecOutcome { | ||
if target == Constants.djumpHaltAddress { | ||
return .exit(.halt) | ||
} | ||
|
||
let entrySize = Int(context.state.program.jumpTableEntrySize) | ||
let start = ((Int(target) / context.config.pvmDynamicAddressAlignmentFactor) - 1) * entrySize | ||
let end = start + entrySize | ||
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(context: context, target: target, targetAligned: UInt32(truncatingIfNeeded: targetAligned)) else { | ||
return .exit(.panic(.invalidDynamicJump)) | ||
} | ||
|
||
context.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) | ||
} | ||
} |
Oops, something went wrong.