Skip to content

JIT executor #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
583ae6e
Implement JIT Compilation Backend for PolkaVM
xlc May 9, 2025
b1ddfdf
Refactor JIT compilation logic for AArch64 and x86_64 in PolkaVM
xlc May 9, 2025
3529a23
Refactor JIT-related files for clarity and improved documentation
xlc May 9, 2025
a0d04eb
Enhance JIT error handling and performance metrics collection
xlc May 9, 2025
97d5fd7
Add JIT-specific panic reasons and improve error handling in JIT exec…
xlc May 9, 2025
0f9f523
Implement gas accounting for host function calls and JIT execution in…
xlc May 9, 2025
176036a
Enhance static register mapping for AArch64 and x86_64 in JIT compila…
xlc May 9, 2025
3db3ea1
Implement static register allocation and JIT execution framework for …
xlc May 11, 2025
6945062
Refactor instruction execution to use a context-aware execution flag …
xlc May 11, 2025
ff18d5f
Refactor VMState usage to VMStateInterpreter for improved clarity and…
xlc May 12, 2025
f71f1c5
Enhance JIT execution by adding invocation context support and improv…
xlc May 12, 2025
fb0f118
Refactor JIT compilation components: introduce JITPlatform enum, remo…
xlc May 12, 2025
f9e58ac
Remove gas cost checks and unused host call trampoline typealias from…
xlc May 12, 2025
50a4d88
Refactor JIT compilation for AArch64 and x86_64 architectures
xlc May 19, 2025
a7bee82
Fix initialization of compiledFuncPtr in JITCompiler
xlc May 19, 2025
d0d10ef
Refactor JIT execution and invocation context handling: replace VMSta…
xlc May 21, 2025
3efa12b
Update asmjit submodule and adjust C++ header includes: remove unused…
xlc May 21, 2025
09cb19b
Update PVMTests to use VMStateInterpreter instead of VMState for impr…
xlc May 21, 2025
d6a2fb7
Comment out unstable test cases for delayed and repeating tasks in Di…
xlc May 21, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,13 @@
}

let program = try ProgramCode(innerPvm.code)
let vm = VMState(program: program, pc: innerPvm.pc, registers: Registers(registers), gas: Gas(gas), memory: innerPvm.memory)
let vm = VMStateInterpreter(
program: program,
pc: innerPvm.pc,
registers: Registers(registers),
gas: Gas(gas),
memory: innerPvm.memory
)

Check warning on line 1125 in Blockchain/Sources/Blockchain/VMInvocations/HostCall/HostCalls.swift

View check run for this annotation

Codecov / codecov/patch

Blockchain/Sources/Blockchain/VMInvocations/HostCall/HostCalls.swift#L1119-L1125

Added lines #L1119 - L1125 were not covered by tests
let engine = Engine(config: DefaultPvmConfig())
let exitReason = await engine.execute(state: vm)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import TracingUtils

private let logger = Logger(label: "AccumulateContext")

public class AccumulateContext: InvocationContext {
public final class AccumulateContext: InvocationContext {
public class AccumulateContextType {
var x: AccumlateResultContext
var y: AccumlateResultContext // only set in checkpoint host-call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import TracingUtils

private let logger = Logger(label: "IsAuthorizedContext")

public class IsAuthorizedContext: InvocationContext {
public final class IsAuthorizedContext: InvocationContext {
public typealias ContextType = Void

public var context: ContextType = ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public struct InnerPvm {
public var pc: UInt32
}

public class RefineContext: InvocationContext {
public final class RefineContext: InvocationContext {
public class RefineContextType {
var pvms: [UInt64: InnerPvm]
var exports: [Data4104]
Expand Down
100 changes: 50 additions & 50 deletions Blockchain/Tests/BlockchainTests/DispatchQueueSchedulerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,56 @@ struct DispatchQueueSchedulerTests {
}
}

@Test func scheduleDelayedTask() async throws {
await withKnownIssue("unstable when cpu is busy", isIntermittent: true) {
try await confirmation { confirm in
let delay = 0.5
let now = Date()
let end: ThreadSafeContainer<Date?> = .init(nil)
let cancel = scheduler.schedule(delay: delay, repeats: false) {
end.value = Date()
confirm()
}

try await Task.sleep(for: .seconds(1))

_ = cancel

let diff = try #require(end.value).timeIntervalSince(now) - delay
let diffAbs = abs(diff)
#expect(diffAbs < 0.5)
}
}
}

@Test func scheduleRepeatingTask() async throws {
await withKnownIssue("unstable when cpu is busy", isIntermittent: true) {
try await confirmation(expectedCount: 2) { confirm in
let delay = 1.5
let now = Date()
let executionTimes = ThreadSafeContainer<[Date]>([])
let expectedExecutions = 2

let cancel = scheduler.schedule(delay: delay, repeats: true) {
executionTimes.value.append(Date())
confirm()
}

try await Task.sleep(for: .seconds(3.1))

_ = cancel

#expect(executionTimes.value.count == expectedExecutions)

for (index, time) in executionTimes.value.enumerated() {
let expectedInterval = delay * Double(index + 1)
let actualInterval = time.timeIntervalSince(now)
let difference = abs(actualInterval - expectedInterval)
#expect(difference < 0.5)
}
}
}
}
// @Test func scheduleDelayedTask() async throws {
// await withKnownIssue("unstable when cpu is busy", isIntermittent: true) {
// try await confirmation { confirm in
// let delay = 0.5
// let now = Date()
// let end: ThreadSafeContainer<Date?> = .init(nil)
// let cancel = scheduler.schedule(delay: delay, repeats: false) {
// end.value = Date()
// confirm()
// }

// try await Task.sleep(for: .seconds(1))

// _ = cancel

// let diff = try #require(end.value).timeIntervalSince(now) - delay
// let diffAbs = abs(diff)
// #expect(diffAbs < 0.5)
// }
// }
// }

// @Test func scheduleRepeatingTask() async throws {
// await withKnownIssue("unstable when cpu is busy", isIntermittent: true) {
// try await confirmation(expectedCount: 2) { confirm in
// let delay = 1.5
// let now = Date()
// let executionTimes = ThreadSafeContainer<[Date]>([])
// let expectedExecutions = 2

// let cancel = scheduler.schedule(delay: delay, repeats: true) {
// executionTimes.value.append(Date())
// confirm()
// }

// try await Task.sleep(for: .seconds(3.1))

// _ = cancel

// #expect(executionTimes.value.count == expectedExecutions)

// for (index, time) in executionTimes.value.enumerated() {
// let expectedInterval = delay * Double(index + 1)
// let actualInterval = time.timeIntervalSince(now)
// let difference = abs(actualInterval - expectedInterval)
// #expect(difference < 0.5)
// }
// }
// }
// }

@Test func cancelTask() async throws {
await withKnownIssue("unstable when cpu is busy", isIntermittent: true) {
Expand Down
2 changes: 1 addition & 1 deletion JAMTests/Tests/JAMTests/w3f/PVMTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct PVMTests {
pageMap: testCase.initialPageMap.map { (address: $0.address, length: $0.length, writable: $0.isWritable) },
chunks: testCase.initialMemory.map { (address: $0.address, data: Data($0.contents)) }
)
let vmState = VMState(
let vmState = VMStateInterpreter(
program: program,
pc: testCase.initialPC,
registers: Registers(testCase.initialRegs),
Expand Down
5 changes: 4 additions & 1 deletion PolkaVM/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ let package = Package(
.target(
name: "asmjit",
sources: ["src/asmjit"],
publicHeadersPath: "src"
publicHeadersPath: "src",
cxxSettings: [
.unsafeFlags(["-Wno-incomplete-umbrella"]),
]
),
],
swiftLanguageModes: [.version("6")]
Expand Down
128 changes: 128 additions & 0 deletions PolkaVM/Sources/CppHelper/a64_helper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// generated by polka.codes
// AArch64-specific JIT compilation for PolkaVM

#include "a64_helper.hh"
#include <asmjit/asmjit.h>
#include <asmjit/a64.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdio.h>

using namespace asmjit;
using namespace asmjit::a64;

// Compiles PolkaVM bytecode to AArch64 machine code
int32_t compilePolkaVMCode_a64(
const uint8_t* _Nonnull codeBuffer,
size_t codeSize,
uint32_t initialPC,
uint32_t jitMemorySize,
void* _Nullable * _Nonnull funcOut)
{
// Validate input parameters
if (!codeBuffer || codeSize == 0) {
return 1; // Invalid input (null buffer or zero size)
}

if (!funcOut) {
return 2; // Invalid output parameter
}

// Initialize asmjit runtime for code generation
JitRuntime runtime;
CodeHolder code;
code.init(runtime.environment());

// Create AArch64 assembler
a64::Assembler a(&code);

// AArch64 callee-saved registers: x19-x28
// We'll save the ones we use
a.sub(sp, sp, 48); // Reserve stack space for saved registers
a.stp(x19, x20, ptr(sp, 0)); // Store pair at sp+0
a.stp(x21, x22, ptr(sp, 16)); // Store pair at sp+16
a.stp(x23, x24, ptr(sp, 32)); // Store pair at sp+32

// Setup VM environment registers:
// - x19: VM_REGISTERS_PTR - Guest VM registers array
// - x20: VM_MEMORY_PTR - Guest VM memory base
// - w21: VM_MEMORY_SIZE - Guest VM memory size
// - x22: VM_GAS_PTR - Guest VM gas counter
// - w23: VM_PC - Guest VM program counter
// - x24: VM_CONTEXT_PTR - Invocation context pointer

// Copy function arguments to VM registers
a.mov(x19, x0); // registers_ptr -> x19
a.mov(x20, x1); // memory_base_ptr -> x20
a.mov(w21, w2); // memory_size -> w21
a.mov(x22, x3); // gas_ptr -> x22
a.mov(w23, w4); // initial_pvm_pc -> w23 (PC)
a.mov(x24, x5); // invocation_context_ptr -> x24

// TODO: Full JIT implementation would go here
// This is a simplified stub implementation for now

// For demonstration purposes, create a simple gas check and a loop dispatcher
Label mainLoop = a.newLabel();
Label outOfGas = a.newLabel();
Label jumpTable = a.newLabel();
Label exitHalt = a.newLabel();
Label exitNoImpl = a.newLabel();

// Main execution loop
a.bind(mainLoop);

// Gas check (deduct a fixed amount per instruction)
a.ldr(x0, ptr(x22)); // Load gas value
a.sub(x0, x0, 1); // Subtract gas cost
a.str(x0, ptr(x22)); // Store updated gas
a.cmp(x0, 0); // Compare with 0
a.b_lt(outOfGas); // Branch if gas < 0

// Example opcode dispatch (simplified)
// In a real implementation, this would be a jump table based on opcodes
a.mov(w0, w23); // Load PC
a.cmp(w0, 0x1000); // Check if PC is out of range
a.b_hs(exitNoImpl); // Branch to unimplemented if too large

// Simulate a halt instruction at PC 0 (just for testing)
a.cmp(w0, 0);
a.b_eq(exitHalt);

// If we get here, go back to the main loop
a.add(w23, w23, 4); // Increment PC by instruction size
a.b(mainLoop); // Continue execution

// Out of gas handler
a.bind(outOfGas);
a.mov(w0, 1); // Exit reason: out of gas
a.b(jumpTable);

// Halt handler
a.bind(exitHalt);
a.mov(w0, 0); // Exit reason: halt
a.b(jumpTable);

// Not implemented handler
a.bind(exitNoImpl);
a.mov(w0, -1); // Exit reason: trap/panic
// Fall through to jumpTable

// Exit point - restore callee-saved registers and return
a.bind(jumpTable);
// Restore callee-saved registers
a.ldp(x23, x24, ptr(sp, 32)); // Load pair from sp+32
a.ldp(x21, x22, ptr(sp, 16)); // Load pair from sp+16
a.ldp(x19, x20, ptr(sp, 0)); // Load pair from sp+0
a.add(sp, sp, 48); // Restore stack pointer
a.ret(x30); // Return using the link register

// Generate the function code
Error err = runtime.add(funcOut, &code);
if (err) {
return int32_t(err); // Return asmjit error code
}

return 0; // Success
}
40 changes: 40 additions & 0 deletions PolkaVM/Sources/CppHelper/a64_helper.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// generated by polka.codes
// AArch64-specific JIT compilation for PolkaVM

#pragma once

#include <cstdint>
#include <cstddef>

// Compiles PolkaVM bytecode to AArch64 machine code
//
// Error codes:
// - 0: Success
// - 1: Invalid input (null buffer or zero size)
// - 2: Invalid output parameter
// - Other: AsmJit error codes
//
// Function parameters (AArch64 ABI):
// - x0: registers_ptr (guest VM registers array)
// - x1: memory_base_ptr (guest VM memory)
// - w2: memory_size (guest VM memory size)
// - x3: gas_ptr (guest VM gas counter)
// - w4: initial_pvm_pc (starting program counter)
// - x5: invocation_context_ptr (JITHostFunctionTable)
//
// Static register allocation (AArch64):
// - x19: VM_REGISTERS_PTR - Guest VM registers array
// - x20: VM_MEMORY_PTR - Guest VM memory base
// - w21: VM_MEMORY_SIZE - Guest VM memory size
// - x22: VM_GAS_PTR - Guest VM gas counter
// - w23: VM_PC - Guest VM program counter
// - x24: VM_CONTEXT_PTR - Invocation context pointer
// - x9-x15: Temporary registers for computation
// - x0-x7: Parameter passing for function calls
// - x0: Return value register
int32_t compilePolkaVMCode_a64(
const uint8_t* _Nonnull codeBuffer,
size_t codeSize,
uint32_t initialPC,
uint32_t jitMemorySize,
void* _Nullable * _Nonnull funcOut);
Loading