Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1c0839f
feat(mypyc integration): Refactor instruction tables and memory manag…
prasad-kumkar Sep 10, 2025
f785ad3
feat: program structure with basic blocks and improve type hints
prasad-kumkar Sep 10, 2025
f6139ba
feat: Improved mapper, implement Cython
prasad-kumkar Sep 13, 2025
f22add0
refactor: Update instruction handling and memory management for impro…
prasad-kumkar Sep 13, 2025
337c68f
Refactor recompiler for improved performance and maintainability
prasad-kumkar Sep 14, 2025
72446ae
refactor: exclude recompiler context from build
prasad-kumkar Sep 14, 2025
bb7b180
refactor: cleanup logs
prasad-kumkar Sep 15, 2025
e671540
refactor: update memory management to use bitarray for efficient page…
prasad-kumkar Sep 15, 2025
0e6170f
Merge branch 'feat/mypyc' of https://github.com/chainscore/tsrkit-pvm…
prasad-kumkar Sep 15, 2025
68d5cf9
refactor: add mypy as a requirement for type checking
prasad-kumkar Sep 16, 2025
a4220b0
fix: license on1
prasad-kumkar Sep 16, 2025
01d8acf
fix: license typo
prasad-kumkar Sep 16, 2025
826f6eb
fix: block based execution bug, optimised memory, started work on cython
prasad-kumkar Sep 17, 2025
23bd420
refactor: update type hints for mypyc build
prasad-kumkar Sep 17, 2025
b66ef0e
fix: uncomment core and recompiler files for MyPyC compilation
prasad-kumkar Sep 17, 2025
c37dd54
Refactor PVM instruction handling and memory management
prasad-kumkar Sep 18, 2025
1b1d9f9
CPVM: More shift towards C; refactor instruction handling and optimiz…
prasad-kumkar Sep 18, 2025
b817374
feat: more cythonization, instruction handling and optimize utility f…
prasad-kumkar Sep 20, 2025
1360c49
refactor: cythonise alter_acc
prasad-kumkar Sep 20, 2025
aa1e0bc
Refactor Cython instruction tables for performance improvements
prasad-kumkar Sep 20, 2025
af5385a
feat:iii_reg missed inline
prasad-kumkar Sep 20, 2025
d80ccda
Refactor instruction handling in Cython
prasad-kumkar Sep 20, 2025
e53fdf2
fix: accessibility compatibiloty
prasad-kumkar Sep 21, 2025
21e4d69
refactor: simplify memory read/write operations in instruction tables
prasad-kumkar Sep 21, 2025
2070b61
fix: remove mypyc from requirements
harsh-csl Sep 29, 2025
67c11b8
changed log level from info to debug
Kartik213 Oct 6, 2025
7ae1bff
feat: prepare tsrkit-pvm for PyPI publishing with multi-platform wheels
prasad-kumkar Jan 27, 2026
6a86522
Merge pull request #2 from Chainscore/feat/publish-package
prasad-kumkar Jan 27, 2026
0665f9f
Merge branch 'main' into feat/mypyc
prasad-kumkar Jan 27, 2026
446a6f7
fix: resolve PyPI packaging build issues
prasad-kumkar Jan 27, 2026
553991d
fix: Windows build failure in cibuildwheel
prasad-kumkar Jan 27, 2026
f5fdb17
fix: remove explicit empty parameters causing Windows build failure
prasad-kumkar Jan 27, 2026
78292d6
fix: add packaging>=24.2 dependency for setuptools compatibility
prasad-kumkar Jan 27, 2026
9c1e613
fix: resolve packaging version conflict by letting setuptools handle it
prasad-kumkar Jan 27, 2026
669d079
fix: use setuptools<77.0.0 to avoid packaging version conflicts
prasad-kumkar Jan 27, 2026
a5adbe5
fix: correct license field format to PEP 621 specification
prasad-kumkar Jan 27, 2026
1c0e0b6
fix: remove pip upgrade from CIBW_BEFORE_BUILD to fix Windows builds
prasad-kumkar Jan 27, 2026
c4cb8cb
fix: replace Unicode symbols with ASCII for Windows compatibility
prasad-kumkar Jan 27, 2026
c4bbc5a
fix: add tsrkit-asm dependency and ensure both interpreter and recomp…
prasad-kumkar Jan 27, 2026
30a7652
feat: add runtime mode tests for interpreter and recompiler
prasad-kumkar Jan 27, 2026
9f16bf7
fix: use macos-15-intel for x86_64 Intel builds
prasad-kumkar Jan 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ share/python-wheels/
*.egg
MANIFEST

*darwin.so

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.1.0"
description = "Polkadot Virtual Machine (PVM) implementation with interpreter and recompiler as specified in GP"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "MIT"}
license = "MIT AND (Apache-2.0 OR BSD-2-Clause)"
authors = [
{name = "Chainscore Labs", email = "hello@chainscore.finance"},
]
Expand All @@ -33,7 +33,6 @@ keywords = [
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
Expand Down
65 changes: 65 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from setuptools import setup, find_packages

if __name__ == "__main__":
from mypyc.build import mypycify
from pathlib import Path

print("MyPyC compilation requested...")

# Get current directory
current_dir = Path(__file__).parent

# List all Python files to compile
python_files = []

target_files = [
"tsrkit_pvm/common/utils.py",
"tsrkit_pvm/common/status.py",
"tsrkit_pvm/common/constants.py",
"tsrkit_pvm/core/code.py",
"tsrkit_pvm/core/mapper.py",
"tsrkit_pvm/interpreter/pvm.py",
"tsrkit_pvm/interpreter/program.py",
"tsrkit_pvm/interpreter/memory.py",
"tsrkit_pvm/interpreter/instructions/tables/wo_args.py",
"tsrkit_pvm/interpreter/instructions/tables/i_imm.py",
"tsrkit_pvm/interpreter/instructions/tables/i_offset.py",
"tsrkit_pvm/interpreter/instructions/tables/i_reg_i_ewimm.py",
"tsrkit_pvm/interpreter/instructions/tables/i_reg_i_imm.py",
"tsrkit_pvm/interpreter/instructions/tables/i_reg_i_imm_i_offset.py",
"tsrkit_pvm/interpreter/instructions/tables/ii_imm.py",
"tsrkit_pvm/interpreter/instructions/tables/ii_reg.py",
"tsrkit_pvm/interpreter/instructions/tables/ii_reg_i_imm.py",
"tsrkit_pvm/interpreter/instructions/tables/ii_reg_i_offset.py",
"tsrkit_pvm/interpreter/instructions/tables/ii_reg_ii_imm.py",
"tsrkit_pvm/interpreter/instructions/tables/iii_reg.py",
]

# Try MyPyC compilation
ext_modules = []
compiled_count = 0
failed_count = 0

for py_file in target_files:
try:
print(f"Compiling {py_file}...")
mypycified = mypycify([py_file], opt_level="3")
Comment thread
prasad-kumkar marked this conversation as resolved.
Outdated
if mypycified:
ext_modules.extend(mypycified)
compiled_count += 1
print(f"✓ Successfully compiled {py_file}")
else:
failed_count += 1
print(f"✗ Failed to compile {py_file}")
except Exception as e:
failed_count += 1
print(f"✗ Error compiling {py_file}: {e}")

print(f"\nCompilation summary: {compiled_count} succeeded, {failed_count} failed")

setup(
name="tsrkit_pvm",
packages=find_packages(),
ext_modules=ext_modules,
zip_safe=False,
)
62 changes: 6 additions & 56 deletions tsrkit_pvm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,59 +37,9 @@
from .interpreter.program import INT_Program
from .interpreter.pvm import Interpreter

# Lazy import for recompiler to avoid loading Linux-specific native libraries
# unless explicitly requested on a compatible platform
import platform
import sys

def _get_recompiler_classes():
"""Lazy loader for recompiler classes that require platform-specific native libraries."""
if platform.system() != "Linux" or platform.machine() not in ["x86_64", "AMD64"]:
raise ImportError(
f"Recompiler is only supported on Linux x86_64, current platform: "
f"{platform.system()} {platform.machine()}"
)

try:
from .recompiler.memory import REC_Memory
from .recompiler.program import REC_Program
from .recompiler.pvm import Recompiler
return REC_Memory, REC_Program, Recompiler
except OSError as e:
if "libsegwrap" in str(e):
raise ImportError(
f"Recompiler native library not available: {e}\n"
"The recompiler requires Linux-specific native libraries that are not "
"available on this platform."
) from e
raise

# Create lazy properties for recompiler classes
class _LazyRecompilerModule:
"""Module-like object that provides lazy loading for recompiler classes."""

def __getattr__(self, name):
if name in ("REC_Memory", "REC_Program", "Recompiler"):
try:
REC_Memory, REC_Program, Recompiler = _get_recompiler_classes()
# Cache the imported classes
self.REC_Memory = REC_Memory
self.REC_Program = REC_Program
self.Recompiler = Recompiler
return getattr(self, name)
except ImportError as e:
raise ImportError(f"Cannot import {name}: {e}") from e
raise AttributeError(f"module has no attribute '{name}'")

# Create lazy loader instance
_recompiler_loader = _LazyRecompilerModule()

# Create module-level properties that delegate to the lazy loader
def __getattr__(name):
if name in ("REC_Memory", "REC_Program", "Recompiler"):
return getattr(_recompiler_loader, name)
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

# from .recompiler.memory import REC_Memory
# from .recompiler.program import REC_Program
# from .recompiler.pvm import Recompiler

__all__ = [
# Core
Expand All @@ -102,9 +52,9 @@ def __getattr__(name):
"INT_Memory",
"INT_Program",
"Interpreter",
"REC_Memory",
"REC_Program",
"Recompiler",
# "REC_Memory",
# "REC_Program",
# "Recompiler",
# Common constants
"PVM_ADDR_ALIGNMENT",
"PVM_INIT_DATA_SIZE",
Expand Down
24 changes: 16 additions & 8 deletions tsrkit_pvm/common/extended.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
from typing import Any
from typing import Any, Union, overload, SupportsIndex


class ExtendedList(list):
"""List which allows accessing elements out of bound, returns a default value if so"""

def __init__(self, *args, default=None):
def __init__(self, *args: Any, default: Any = None) -> None:
super().__init__(*args)
self.DEFAULT = default

def __getitem__(self, index: int | slice):
if isinstance(index, int):
if index >= len(self) or index < -len(self):
return self.DEFAULT
return super().__getitem__(index)
elif isinstance(index, slice):
@overload
def __getitem__(self, index: SupportsIndex) -> Any: ...

@overload
def __getitem__(self, index: slice) -> list[Any]: ...

def __getitem__(self, index: Union[SupportsIndex, slice]) -> Any:
if isinstance(index, slice):
# Handle slice objects properly
start, stop, step = index.indices(len(self))
res = []
for i in range(start, stop, step):
res.append(self.__getitem__(i))
return res
else:
# Handle int index
int_index = int(index)
if int_index >= len(self) or int_index < -len(self):
return self.DEFAULT
return super().__getitem__(int_index)
30 changes: 15 additions & 15 deletions tsrkit_pvm/common/status.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Optional
from tsrkit_types.enum import Enum
from tsrkit_types.struct import structure
from dataclasses import dataclass
from enum import Enum
from typing import Any, Optional


@structure
@dataclass
class ExecValue:
name: str
code: int
Expand Down Expand Up @@ -34,37 +34,37 @@ class HostStatus(Enum):

# Constructured statuses to use directly
# Panic
PANIC = ExecutionStatus.PANIC
PANIC: ExecutionStatus = ExecutionStatus(ExecutionStatus.PANIC)


# Page fault with a register value
def PAGE_FAULT(register: int) -> ExecutionStatus:
result = ExecutionStatus.PAGE_FAULT
result: Any = ExecutionStatus.PAGE_FAULT
result.value.register = register
return result
return ExecutionStatus(result)
Comment thread
prasad-kumkar marked this conversation as resolved.


# Halt
HALT = ExecutionStatus.HALT
HALT: ExecutionStatus = ExecutionStatus(ExecutionStatus.HALT)


# Host call with a register value
def HOST(register: int) -> ExecutionStatus:
result = ExecutionStatus.HOST
result: Any = ExecutionStatus.HOST
result.value.register = register
return result
return ExecutionStatus(result)
Comment thread
prasad-kumkar marked this conversation as resolved.


# Out of gas
OUT_OF_GAS = ExecutionStatus.OUT_OF_GAS
OUT_OF_GAS: ExecutionStatus = ExecutionStatus(ExecutionStatus.OUT_OF_GAS)
# Continue
CONTINUE = ExecutionStatus.CONTINUE
CONTINUE: ExecutionStatus = ExecutionStatus(ExecutionStatus.CONTINUE)


class PvmError(Exception):
def __init__(self, code: ExecutionStatus, message: str = ""):
def __init__(self, code: ExecutionStatus, message: str = "") -> None:
self.code = code
self.message = message

def __str__(self):
return f"{self.code.value.name}: {self.message}"
def __str__(self) -> str:
return f"{self.code.name}: {self.message}"
10 changes: 5 additions & 5 deletions tsrkit_pvm/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .constants import PVM_INIT_ZONE_SIZE, PVM_MEMORY_PAGE_SIZE
from math import ceil
import math
from typing import List, Union
from typing import List, Union, Any

_PVM_MEMORY_PAGE_SHIFT = 12 # log2(PVM_MEMORY_PAGE_SIZE) = log2(4096) = 12
_PVM_INIT_ZONE_SHIFT = 16 # log2(PVM_INIT_ZONE_SIZE) = log2(65536) = 16
Expand Down Expand Up @@ -90,7 +90,7 @@ def z_inv(x: int, n: int) -> int:
return (modulus + x) & (modulus - 1) # equivalent to % 2**(8*n)


def b(value: int, byte_size: int, is_reversed=False) -> List[int]:
def b(value: int, byte_size: int, is_reversed: bool = False) -> List[int]:
"""Convert integer to list of bits."""
# Handle edge case where byte_size is 0
if byte_size <= 0:
Expand All @@ -103,7 +103,7 @@ def b(value: int, byte_size: int, is_reversed=False) -> List[int]:
return result


def b_inv(value: List[int], is_reversed=False) -> int:
def b_inv(value: List[int], is_reversed: bool = False) -> int:
"""Convert list of bits to integer."""
# avoid repeated list operations and use enumerate
if is_reversed:
Expand Down Expand Up @@ -131,7 +131,7 @@ def b_inv(value: List[int], is_reversed=False) -> int:
'xor': lambda a, b: a ^ b,
}

def compare(a: Union[int, bool], b: Union[int, bool], op: str) -> bool:
def compare(a: Union[int, bool], b: Union[int, bool], op: str) -> Any:
"""Compare two values using the specified operation."""
# Use lookup table for common operations (much faster than getattr)
if op in _COMPARISON_OPS:
Expand All @@ -141,7 +141,7 @@ def compare(a: Union[int, bool], b: Union[int, bool], op: str) -> bool:
return getattr(a, f"__{op}__")(b)


def compare_bits_vectorized(bits_a: List[int], bits_b: List[int], op: str) -> List[int]:
def compare_bits_vectorized(bits_a: List[Union[int, bool]], bits_b: List[int], op: str) -> List[int]:
"""Vectorized bit comparison for 64-bit operations - much faster than loop."""
if op == 'and':
return [a & b for a, b in zip(bits_a, bits_b)]
Expand Down
4 changes: 2 additions & 2 deletions tsrkit_pvm/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
from .ipvm import PVM
from .program_base import Program
from .memory import Memory
from .code import Code
# from .code import Code # Excluded due to recompiler dependencies

__all__ = ["PVM", "Program", "Memory", "Code"]
__all__ = ["PVM", "Program", "Memory"] # Removed "Code"
14 changes: 8 additions & 6 deletions tsrkit_pvm/core/code.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Any, Self, Tuple, Type
from typing import Tuple, Type, Union

from tsrkit_types.integers import Uint
from tsrkit_types.itf.codable import Codable
Expand Down Expand Up @@ -28,7 +28,7 @@ class Code(Codable):
s: int

@classmethod
def decode_from(cls, pc: bytes) -> None | Self:
def decode_from(cls, pc: bytes) -> Union[None, "Code"]:
offset = 0
o_len, decoded = Uint[24].decode_from(pc, offset)
offset += decoded
Expand All @@ -51,7 +51,7 @@ def decode_from(cls, pc: bytes) -> None | Self:
offset += c_len
return cls(read=o, r_write=w, z=z, s=s, code=c)

def encode_size(self):
def encode_size(self) -> int:
return 3 + 3 + 2 + 3 + len(self.read) + len(self.r_write) + 4 + len(self.code)

def encode_into(self, buffer: bytearray, offset: int = 0) -> int:
Expand Down Expand Up @@ -79,7 +79,7 @@ def encode_into(self, buffer: bytearray, offset: int = 0) -> int:
return offset - start


def regs_from_pc(args) -> list:
def regs_from_pc(args: bytes) -> list:
result = [0] * 13
result[0] = 2**32 - 2**16
result[1] = 2**32 - 2 * PVM_INIT_ZONE_SIZE - PVM_INIT_DATA_SIZE
Expand All @@ -89,14 +89,16 @@ def regs_from_pc(args) -> list:


def y_function(
bytecode: bytes, args: bytes, mode="recompiler"
) -> Tuple[bytes, list, Memory]:
bytecode: bytes, args: bytes, mode: str = "recompiler"
) -> Union[Tuple[bytes, list, Memory], None]:
"""Extract program components from bytecode.

Returns:
Tuple of (program_code, registers, memory_data)
"""
code = Code.decode_from(bytecode)
if not code:
return None

program_ = program_base.Program.decode_from(code.code)[0]

Expand Down
Loading