From 91823a3f8365a1f94145004785e690485c4d55a9 Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Tue, 4 Feb 2025 18:05:14 +0000 Subject: [PATCH 1/3] RISC-V: implement Block for JIT with interpreted fallback --- src/riscv/lib/src/jit.rs | 9 ++ .../src/machine_state/block_cache/bcall.rs | 140 ++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index 3705c28f9e95..e041b3b9697b 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -244,6 +244,15 @@ impl JIT { } } +// TODO: https://linear.app/tezos/issue/RV-496 +// `Block::BlockBuilder` should not require Default, as it +// does not allow for potential fallilibility +impl Default for JIT { + fn default() -> Self { + Self::new().expect("JIT is supported on all octez-riscv supported platforms") + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/riscv/lib/src/machine_state/block_cache/bcall.rs b/src/riscv/lib/src/machine_state/block_cache/bcall.rs index b802a81a5698..7358bb0d6edb 100644 --- a/src/riscv/lib/src/machine_state/block_cache/bcall.rs +++ b/src/riscv/lib/src/machine_state/block_cache/bcall.rs @@ -10,6 +10,7 @@ use super::{CACHE_INSTR, ICallLayout, ICallPlaced, run_instr}; use crate::{ default::ConstDefault, + jit::{JCall, JIT, state_access::JitStateAccess}, machine_state::{ MachineCoreState, ProgramCounterUpdate, instruction::Instruction, @@ -259,6 +260,145 @@ impl Clone for Interpreted { } } +/// Blocks that are compiled to native code for execution, when possible. +/// +/// Not all instructions are currently supported, when a block contains +/// unsupported instructions, a fallback to [`Interpreted`] mode occurs. +/// +/// Blocks are compiled upon calling [`Block::complete_block`], in a *stop the world* fashion. +pub struct InlineJit { + fallback: Interpreted, + jit_fn: Option>, +} + +impl Block for InlineJit { + type BlockBuilder = JIT; + + fn start_block(&mut self) + where + M: ManagerWrite, + { + self.jit_fn = None; + self.fallback.start_block() + } + + fn invalidate(&mut self) + where + M: ManagerWrite, + { + self.jit_fn = None; + self.fallback.invalidate() + } + + fn reset(&mut self) + where + M: ManagerReadWrite, + { + self.jit_fn = None; + self.fallback.reset() + } + + fn push_instr(&mut self, instr: Instruction) + where + M: ManagerReadWrite, + { + self.jit_fn = None; + self.fallback.push_instr(instr) + } + + fn instr(&self) -> &[EnrichedCell, M>] + where + M: ManagerRead, + { + self.fallback.instr() + } + + /// Bind + fn bind(allocated: AllocatedOf, M>) -> Self { + Self { + fallback: Interpreted::bind(allocated), + jit_fn: None, + } + } + + fn struct_ref<'a, F: FnManager>>( + &'a self, + ) -> AllocatedOf, F::Output> { + self.fallback.struct_ref::() + } + + fn complete_block(&mut self, jit: &mut Self::BlockBuilder) { + if >::num_instr(self) > 0 { + self.fallback.complete_block(&mut InterpretedBlockBuilder); + + let instr = self + .fallback + .instr + .iter() + .take(>::num_instr(self)) + .map(|i| i.read_ref_stored()); + + let jitfn = jit.compile(instr); + + self.jit_fn = jitfn; + } + } + + /// Get a callable block from an entry. The entry must have passed the address and fence + /// checks. + fn callable(&mut self) -> Option<&mut (impl BCall + ?Sized)> + where + M: ManagerRead, + { + if self.fallback.callable().is_some() { + Some(self) + } else { + None + } + } + + /// The number of instructions contained in the block. + fn num_instr(&self) -> usize + where + M: ManagerRead, + { + self.fallback.num_instr() + } +} + +impl BCall for InlineJit { + /// The number of instructions contained in the block. + fn num_instr(&self) -> usize + where + M: ManagerRead, + { + self.fallback.num_instr() + } + + /// Run a block against the machine state. + /// + /// When calling this function, there must be no partial block in progress. To ensure + /// this, you must always run [`InterpretedCache::complete_current_block`] prior to fetching + /// and running a new block. + /// + /// There _must_ also be sufficient steps remaining, to execute the block in full. + fn run_block( + &self, + core: &mut MachineCoreState, + instr_pc: Address, + steps: &mut usize, + ) -> Result<(), EnvironException> + where + M: ManagerReadWrite, + { + match &self.jit_fn { + // # SAFETY: JIT is alive + Some(jcall) => unsafe { jcall.call(core, instr_pc, steps) }, + None => self.fallback.instr().run_block(core, instr_pc, steps), + } + } +} + fn run_block_inner( instr: &[EnrichedCell, M>], core: &mut MachineCoreState, -- GitLab From 5bca2d4eeba333bb902311127927a46901334cbb Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Tue, 18 Feb 2025 16:40:52 +0000 Subject: [PATCH 2/3] RISC-V: jit clears correctly on failure after first instruction --- src/riscv/lib/src/jit.rs | 77 ++++++++++++++++++-------------- src/riscv/lib/src/jit/builder.rs | 15 +++++-- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index e041b3b9697b..a4555107f8bd 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -489,51 +489,62 @@ mod tests { use crate::machine_state::registers::NonZeroXRegister::*; // Arrange - let failure: &[I] = &[ - // does not currently lowering - I::new_andi(x1, x1, 13, Uncompressed), + let failure_scenarios: &[&[I]] = &[ + &[ + // does not currently lowering + I::new_andi(x1, x1, 13, Uncompressed), + ], + &[ + I::new_nop(Uncompressed), + // does not currently lowering + I::new_andi(x1, x1, 13, Uncompressed), + ], ]; let success: &[I] = &[I::new_nop(Compressed)]; - let mut jit = JIT::::new().unwrap(); + for failure in failure_scenarios.iter() { + let mut jit = JIT::::new().unwrap(); - let mut jitted = create_state!(MachineCoreState, MachineCoreStateLayout, F, T1K); - let mut block = create_state!(Interpreted, BlockLayout, F, T1K); + let mut jitted = create_state!(MachineCoreState, MachineCoreStateLayout, F, T1K); + let mut block = create_state!(Interpreted, BlockLayout, F, T1K); - block.start_block(); - for instr in failure.iter() { - block.push_instr(*instr); - } + block.start_block(); + for instr in failure.iter() { + block.push_instr(*instr); + } - let mut jitted_steps = 0; + let mut jitted_steps = 0; - let initial_pc = 0; - jitted.hart.pc.write(initial_pc); + let initial_pc = 0; + jitted.hart.pc.write(initial_pc); - // Act - let res = jit.compile(instructions(&block).as_slice()); + jitted.hart.xregisters.write_nz(x1, 1); - assert!( - res.is_none(), - "Compilation of unsupported instruction should fail" - ); + // Act + let res = jit.compile(instructions(&block).as_slice()); - block.start_block(); - for instr in success.iter() { - block.push_instr(*instr); - } + assert!( + res.is_none(), + "Compilation of unsupported instruction should fail" + ); - let fun = jit - .compile(instructions(&block).as_slice()) - .expect("Compilation of subsequent functions should succeed"); - let jitted_res = unsafe { - // # Safety - the jit is not dropped until after we - // exit the block. - fun.call(&mut jitted, initial_pc, &mut jitted_steps) - }; + block.start_block(); + for instr in success.iter() { + block.push_instr(*instr); + } - assert!(jitted_res.is_ok()); - assert_eq!(jitted_steps, success.len()); + let fun = jit + .compile(instructions(&block).as_slice()) + .expect("Compilation of subsequent functions should succeed"); + let jitted_res = unsafe { + // # Safety - the jit is not dropped until after we + // exit the block. + fun.call(&mut jitted, initial_pc, &mut jitted_steps) + }; + + assert!(jitted_res.is_ok()); + assert_eq!(jitted_steps, success.len()); + } }); } diff --git a/src/riscv/lib/src/jit/builder.rs b/src/riscv/lib/src/jit/builder.rs index 8d5d8cab04f9..16637e4a5b24 100644 --- a/src/riscv/lib/src/jit/builder.rs +++ b/src/riscv/lib/src/jit/builder.rs @@ -75,9 +75,18 @@ impl<'a, ML: MainMemoryLayout, JSA: JitStateAccess> Builder<'a, ML, JSA> { } /// Clear the builder context on failure. - pub(super) fn fail(self) { - // On failure, the context must be cleared to ensure a clean context for the next - // block to be compiled. This is done via `finalize`, which internally clears the + pub(super) fn fail(mut self) { + // On failure, the context must be cleared to ensure a clean context for the next block to + // be compiled. + + // Before clearing the context, we need to ensure that + // the block compiled so far matches the ABI of the function + // + // In this case, we must ensure that we explicitly declare + // a lack of return values. + self.builder.ins().return_(&[]); + + // Clearing the context is done via `finalize`, which internally clears the // buffers to allow re-use. self.builder.finalize(); } -- GitLab From 45128b57d160f4dc7e6588a7ec0bdb522e751b9e Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Tue, 18 Feb 2025 16:40:52 +0000 Subject: [PATCH 3/3] RISC-V: switch to InlineJit with feature flag --- src/riscv/lib/src/jit.rs | 40 ++++++----- .../lib/src/machine_state/block_cache.rs | 6 +- .../src/machine_state/block_cache/bcall.rs | 72 ++++++++++++------- src/riscv/lib/src/stepper/pvm.rs | 4 +- src/riscv/lib/src/stepper/test.rs | 2 +- src/riscv/lib/tests/test_regression.rs | 22 ++++-- src/riscv/sandbox/Cargo.toml | 4 ++ .../src/commands/bench/commands/run.rs | 4 +- src/riscv/sandbox/src/commands/run.rs | 19 +++-- 9 files changed, 113 insertions(+), 60 deletions(-) diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index a4555107f8bd..030696fa1e46 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -256,7 +256,9 @@ impl Default for JIT { #[cfg(test)] mod tests { use super::*; - use crate::machine_state::block_cache::bcall::{BCall, Block, BlockLayout, Interpreted}; + use crate::machine_state::block_cache::bcall::{ + BCall, Block, BlockLayout, Interpreted, InterpretedBlockBuilder, + }; use crate::machine_state::main_memory::tests::T1K; use crate::machine_state::{MachineCoreState, MachineCoreStateLayout}; use crate::parser::instruction::InstrWidth::*; @@ -288,6 +290,7 @@ mod tests { ]; let mut jit = JIT::::new().unwrap(); + let interpreted_bb = InterpretedBlockBuilder; for scenario in scenarios { let mut interpreted = @@ -312,11 +315,12 @@ mod tests { .compile(instructions(&block).as_slice()) .expect("Compilation of CNop should succeed"); - let interpreted_res = block.callable().unwrap().run_block( - &mut interpreted, - initial_pc, - &mut interpreted_steps, - ); + let interpreted_res = unsafe { + // SAFETY: interpreted blocks are always callable + block.callable(&interpreted_bb) + } + .unwrap() + .run_block(&mut interpreted, initial_pc, &mut interpreted_steps); let jitted_res = unsafe { // # Safety - the jit is not dropped until after we // exit the for loop @@ -359,6 +363,7 @@ mod tests { ]; let mut jit = JIT::::new().unwrap(); + let interpreted_bb = InterpretedBlockBuilder; for scenario in scenarios { let mut interpreted = @@ -383,11 +388,12 @@ mod tests { .compile(instructions(&block).as_slice()) .expect("Compilation should succeed"); - let interpreted_res = block.callable().unwrap().run_block( - &mut interpreted, - initial_pc, - &mut interpreted_steps, - ); + let interpreted_res = unsafe { + // SAFETY: interpreted blocks are always callable + block.callable(&interpreted_bb) + } + .unwrap() + .run_block(&mut interpreted, initial_pc, &mut interpreted_steps); let jitted_res = unsafe { // # Safety - the jit is not dropped until after we // exit the for loop @@ -431,6 +437,7 @@ mod tests { ]; let mut jit = JIT::::new().unwrap(); + let interpreted_bb = InterpretedBlockBuilder; let mut interpreted = create_state!(MachineCoreState, MachineCoreStateLayout, F, T1K); let mut jitted = create_state!(MachineCoreState, MachineCoreStateLayout, F, T1K); @@ -453,11 +460,12 @@ mod tests { .compile(instructions(&block).as_slice()) .expect("Compilation should succeed"); - let interpreted_res = block.callable().unwrap().run_block( - &mut interpreted, - initial_pc, - &mut interpreted_steps, - ); + let interpreted_res = unsafe { + // SAFETY: interpreted blocks are always callable + block.callable(&interpreted_bb) + } + .unwrap() + .run_block(&mut interpreted, initial_pc, &mut interpreted_steps); let jitted_res = unsafe { // # Safety - the jit is not dropped until after we // exit the block diff --git a/src/riscv/lib/src/machine_state/block_cache.rs b/src/riscv/lib/src/machine_state/block_cache.rs index f95a64766c43..6e37b39fe7b5 100644 --- a/src/riscv/lib/src/machine_state/block_cache.rs +++ b/src/riscv/lib/src/machine_state/block_cache.rs @@ -738,7 +738,11 @@ impl< if entry.address.read() == phys_addr && self.fence_counter.read() == entry.fence_counter.read() { - entry.block.callable() + unsafe { + // SAFETY: the block builder given to this function is the same as was given to the + // 'compile' function of this block. + entry.block.callable(&self.block_builder) + } } else { None } diff --git a/src/riscv/lib/src/machine_state/block_cache/bcall.rs b/src/riscv/lib/src/machine_state/block_cache/bcall.rs index 7358bb0d6edb..bbad104730b2 100644 --- a/src/riscv/lib/src/machine_state/block_cache/bcall.rs +++ b/src/riscv/lib/src/machine_state/block_cache/bcall.rs @@ -39,12 +39,10 @@ pub trait BCall { /// Run a block against the machine state. /// /// When calling this function, there must be no partial block in progress. To ensure - /// this, you must always run [`complete_current_block`] prior to fetching + /// this, you must always run [`Block::complete_block`] prior to fetching /// and running a new block. /// /// There _must_ also be sufficient steps remaining, to execute the block in full. - /// - /// [`complete_current_block`]: super::BlockCache::complete_current_block fn run_block( &self, core: &mut MachineCoreState, @@ -116,9 +114,20 @@ pub trait Block { /// Get a callable block from an entry. The entry must have passed the address and fence /// checks. - fn callable(&mut self) -> Option<&mut (impl BCall + ?Sized)> + /// + /// # Safety + /// + /// The `block_builder` must be the same as the block builder given to the `compile` call that + /// (may) have natively compiled this block to machine code. + /// + /// This ensures that the builder in question is guaranteed to be alive, for at least as long + /// as this block may be run via `BCall::run_block`. + unsafe fn callable<'a>( + &mut self, + block_builder: &'a Self::BlockBuilder, + ) -> Option<&mut (impl BCall + ?Sized + 'a)> where - M: ManagerRead; + M: ManagerRead + 'a; } /// Interpreted blocks are built automatically, and require no additional context. @@ -237,10 +246,16 @@ impl Block for Interpreted { ) } + /// # SAFETY + /// + /// This function is always safe to call. #[inline] - fn callable(&mut self) -> Option<&mut (impl BCall + ?Sized)> + unsafe fn callable<'a>( + &mut self, + _bb: &'a Self::BlockBuilder, + ) -> Option<&mut (impl BCall + ?Sized + 'a)> where - M: ManagerRead, + M: ManagerRead + 'a, { let len = self.len_instr.read(); if len > 0 { @@ -272,7 +287,7 @@ pub struct InlineJit { } impl Block for InlineJit { - type BlockBuilder = JIT; + type BlockBuilder = (JIT, InterpretedBlockBuilder); fn start_block(&mut self) where @@ -313,7 +328,6 @@ impl Block for InlineJit self.fallback.instr() } - /// Bind fn bind(allocated: AllocatedOf, M>) -> Self { Self { fallback: Interpreted::bind(allocated), @@ -328,9 +342,9 @@ impl Block for InlineJit } fn complete_block(&mut self, jit: &mut Self::BlockBuilder) { - if >::num_instr(self) > 0 { - self.fallback.complete_block(&mut InterpretedBlockBuilder); + self.fallback.complete_block(&mut jit.1); + if >::num_instr(self) > 0 { let instr = self .fallback .instr @@ -338,26 +352,33 @@ impl Block for InlineJit .take(>::num_instr(self)) .map(|i| i.read_ref_stored()); - let jitfn = jit.compile(instr); + let jitfn = jit.0.compile(instr); self.jit_fn = jitfn; } } - /// Get a callable block from an entry. The entry must have passed the address and fence - /// checks. - fn callable(&mut self) -> Option<&mut (impl BCall + ?Sized)> + /// # SAFETY + /// + /// The `block_builder` must be the same as the block builder given to the `compile` call that + /// (may) have natively compiled this block to machine code. + /// + /// This ensures that the builder in question is guaranteed to be alive, for at least as long + /// as this block may be run via [`BCall::run_block`]. + unsafe fn callable<'a>( + &mut self, + block_builder: &'a Self::BlockBuilder, + ) -> Option<&mut (impl BCall + ?Sized + 'a)> where - M: ManagerRead, + M: ManagerRead + 'a, { - if self.fallback.callable().is_some() { + if self.fallback.callable(&block_builder.1).is_some() { Some(self) } else { None } } - /// The number of instructions contained in the block. fn num_instr(&self) -> usize where M: ManagerRead, @@ -367,7 +388,6 @@ impl Block for InlineJit } impl BCall for InlineJit { - /// The number of instructions contained in the block. fn num_instr(&self) -> usize where M: ManagerRead, @@ -375,13 +395,6 @@ impl BCall for InlineJit self.fallback.num_instr() } - /// Run a block against the machine state. - /// - /// When calling this function, there must be no partial block in progress. To ensure - /// this, you must always run [`InterpretedCache::complete_current_block`] prior to fetching - /// and running a new block. - /// - /// There _must_ also be sufficient steps remaining, to execute the block in full. fn run_block( &self, core: &mut MachineCoreState, @@ -392,7 +405,12 @@ impl BCall for InlineJit M: ManagerReadWrite, { match &self.jit_fn { - // # SAFETY: JIT is alive + // SAFETY: JIT is guaranteed to be alive here by the caller. + // this is due to the only way to run a block being + // by calling `Block::callable` first. That function + // requires the caller uphold the invariant that + // the builder be alive for the lifetime of the + // `BCall`. Some(jcall) => unsafe { jcall.call(core, instr_pc, steps) }, None => self.fallback.instr().run_block(core, instr_pc, steps), } diff --git a/src/riscv/lib/src/stepper/pvm.rs b/src/riscv/lib/src/stepper/pvm.rs index d973dcd6a35c..36e02b96d7cd 100644 --- a/src/riscv/lib/src/stepper/pvm.rs +++ b/src/riscv/lib/src/stepper/pvm.rs @@ -104,15 +104,15 @@ impl<'hooks, ML: MainMemoryLayout, B: Block, CL: CacheLayouts> reveal_request_response_map, }) } -} -impl<'hooks, ML: MainMemoryLayout, CL: CacheLayouts> PvmStepper<'hooks, ML, CL, Owned> { /// Obtain the root hash for the PVM state. pub fn hash(&self) -> Hash { let refs = self.pvm.struct_ref::(); PvmLayout::::state_hash(refs).unwrap() } +} +impl<'hooks, ML: MainMemoryLayout, CL: CacheLayouts> PvmStepper<'hooks, ML, CL, Owned> { /// Produce the Merkle proof for evaluating one step on the given PVM state. /// The given stepper takes one step. pub fn produce_proof(&mut self) -> Option { diff --git a/src/riscv/lib/src/stepper/test.rs b/src/riscv/lib/src/stepper/test.rs index 0004414a29a6..b2adad7b9808 100644 --- a/src/riscv/lib/src/stepper/test.rs +++ b/src/riscv/lib/src/stepper/test.rs @@ -160,7 +160,7 @@ impl> TestStepper Stepper for TestStepper { +impl> Stepper for TestStepper { type MainMemoryLayout = ML; type CacheLayouts = TestCacheLayouts; diff --git a/src/riscv/lib/tests/test_regression.rs b/src/riscv/lib/tests/test_regression.rs index f965e9cafeeb..a7f68e508c4c 100644 --- a/src/riscv/lib/tests/test_regression.rs +++ b/src/riscv/lib/tests/test_regression.rs @@ -5,8 +5,11 @@ use std::{fs, io::Write, ops::Bound}; use octez_riscv::{ + jit::JIT, machine_state::{ - DefaultCacheLayouts, block_cache::bcall::InterpretedBlockBuilder, main_memory::M64M, + DefaultCacheLayouts, + block_cache::bcall::{Block, InlineJit, Interpreted, InterpretedBlockBuilder}, + main_memory::M64M, }, pvm::PvmHooks, state_backend::owned_backend::Owned, @@ -23,7 +26,18 @@ fn capture_debug_log(mint: &mut goldenfile::Mint) -> PvmHooks<'_> { } #[test] -fn test_jstz_regression() { +fn test_jstz_regression_interpreted() { + let block_builder = InterpretedBlockBuilder; + test_jstz_regression::>(block_builder); +} + +#[test] +fn test_jstz_regression_inline_jit() { + let block_buider = (JIT::::new().unwrap(), InterpretedBlockBuilder); + test_jstz_regression::>(block_buider) +} + +fn test_jstz_regression>(block_builder: B::BlockBuilder) { let mut mint = goldenfile::Mint::new(GOLDEN_DIR); let (result, initial_hash, final_hash) = { @@ -43,9 +57,7 @@ fn test_jstz_regression() { const ROLLUP_ADDRESS: [u8; 20] = [0; 20]; const ORIGINATION_LEVEL: u32 = 1; - let block_builder = InterpretedBlockBuilder; - - let mut stepper = PvmStepper::<'_, M64M, DefaultCacheLayouts, Owned>::new( + let mut stepper = PvmStepper::<'_, M64M, DefaultCacheLayouts, Owned, B>::new( &boot_program, Some(&main_program), inbox, diff --git a/src/riscv/sandbox/Cargo.toml b/src/riscv/sandbox/Cargo.toml index 215773ca6ba8..fbbfe4b90f94 100644 --- a/src/riscv/sandbox/Cargo.toml +++ b/src/riscv/sandbox/Cargo.toml @@ -39,3 +39,7 @@ rustc-demangle.workspace = true [dependencies.octez-riscv] path = "../lib" + +[features] +default = [] +inline-jit = [] diff --git a/src/riscv/sandbox/src/commands/bench/commands/run.rs b/src/riscv/sandbox/src/commands/bench/commands/run.rs index bcba63b4f569..91ce40ecd30b 100644 --- a/src/riscv/sandbox/src/commands/bench/commands/run.rs +++ b/src/riscv/sandbox/src/commands/bench/commands/run.rs @@ -26,7 +26,7 @@ use crate::{ data::{BenchData, FineBenchData, InstrGetError, InstrType, SimpleBenchData}, save_to_file, show_results, }, - run::{UseStepper, general_run}, + run::{BlockImpl, UseStepper, general_run}, }, format_status, }; @@ -154,7 +154,7 @@ fn bench_iteration(path: &Path, opts: &BenchRunOptions) -> Result(&opts.common, program, initrd, Runner(opts)) } fn transform_folders(inputs: &[Box]) -> Result, Box> { diff --git a/src/riscv/sandbox/src/commands/run.rs b/src/riscv/sandbox/src/commands/run.rs index 3ae05c81ccb9..6910801dcfb1 100644 --- a/src/riscv/sandbox/src/commands/run.rs +++ b/src/riscv/sandbox/src/commands/run.rs @@ -9,7 +9,7 @@ use octez_riscv::{ machine_state::{DefaultCacheLayouts, main_memory::M1G}, machine_state::{ TestCacheLayouts, - block_cache::bcall::{Block, Interpreted, InterpretedBlockBuilder}, + block_cache::bcall::{self, Block}, }, pvm::PvmHooks, state_backend::owned_backend::Owned, @@ -22,6 +22,13 @@ use crate::{ cli::{CommonOptions, RunOptions}, posix_exit_mode, }; +/// Execution style of blocks +#[cfg(not(feature = "inline-jit"))] +pub type BlockImpl = bcall::Interpreted; + +/// Execution style of blocks +#[cfg(feature = "inline-jit")] +pub type BlockImpl = bcall::InlineJit; pub fn run(opts: RunOptions) -> Result<(), Box> { let program = fs::read(&opts.input)?; @@ -35,7 +42,7 @@ pub fn run(opts: RunOptions) -> Result<(), Box> { } } - let steps = general_run(&opts.common, program, initrd, Runner(&opts))??; + let steps = general_run::<_, _, BlockImpl>(&opts.common, program, initrd, Runner(&opts))??; if opts.print_steps { println!("Run consumed {steps} steps."); @@ -50,16 +57,16 @@ pub trait UseStepper { fn advance(self, stepper: S) -> R; } -pub fn general_run, R>( +pub fn general_run, R, B: Block>( common: &CommonOptions, program: Vec, initrd: Option>, f: F, ) -> Result> { - let block_builder = InterpretedBlockBuilder; + let block_builder = B::BlockBuilder::default(); if common.pvm { - run_pvm::<_, Interpreted<_, _>>( + run_pvm::<_, B>( program.as_slice(), initrd.as_deref(), common, @@ -67,7 +74,7 @@ pub fn general_run, R>( block_builder, ) } else { - run_test( + run_test::<_, B>( program.as_slice(), initrd.as_deref(), common, -- GitLab