From a70670657261ee3d34c51a18923f8fc5d4623847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 7 Dec 2020 09:49:55 +0100 Subject: [PATCH] Validator CLI option to enable just-in-time compilation of BPF (#13789) * Adds a CLI option to the validator to enable just-in-time compilation of BPF. * Refactoring to use bpf_loader_program instead of feature_set to pass JIT flag from the validator CLI to the executor. --- core/src/validator.rs | 3 ++ ledger/src/bank_forks_utils.rs | 5 ++- ledger/src/blockstore_processor.rs | 6 +++- ledger/src/builtins.rs | 42 +++++++++++----------- programs/bpf_loader/src/lib.rs | 54 ++++++++++++++++++++++++----- programs/bpf_loader/src/with_jit.rs | 5 +++ runtime/src/bank.rs | 1 + sdk/src/process_instruction.rs | 1 + validator/src/main.rs | 7 ++++ 9 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 programs/bpf_loader/src/with_jit.rs diff --git a/core/src/validator.rs b/core/src/validator.rs index 88a997d1ab..c5e2cd8e39 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -105,6 +105,7 @@ pub struct ValidatorConfig { pub require_tower: bool, pub debug_keys: Option>>, pub contact_debug_interval: u64, + pub bpf_jit: bool, } impl Default for ValidatorConfig { @@ -141,6 +142,7 @@ impl Default for ValidatorConfig { require_tower: false, debug_keys: None, contact_debug_interval: DEFAULT_CONTACT_DEBUG_INTERVAL, + bpf_jit: false, } } } @@ -857,6 +859,7 @@ fn new_banks_from_ledger( } let process_options = blockstore_processor::ProcessOptions { + bpf_jit: config.bpf_jit, poh_verify, dev_halt_at_slot: config.dev_halt_at_slot, new_hard_forks: config.new_hard_forks.clone(), diff --git a/ledger/src/bank_forks_utils.rs b/ledger/src/bank_forks_utils.rs index 7c9bc131cf..337fba5397 100644 --- a/ledger/src/bank_forks_utils.rs +++ b/ledger/src/bank_forks_utils.rs @@ -66,7 +66,10 @@ pub fn load( compression, genesis_config, process_options.debug_keys.clone(), - Some(&crate::builtins::get(genesis_config.cluster_type)), + Some(&crate::builtins::get( + genesis_config.cluster_type, + process_options.bpf_jit, + )), ) .expect("Load from snapshot failed"); diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index fd001af7c2..c5e55ea343 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -311,6 +311,7 @@ pub type ProcessCallback = Arc; #[derive(Default, Clone)] pub struct ProcessOptions { + pub bpf_jit: bool, pub poh_verify: bool, pub full_leader_cache: bool, pub dev_halt_at_slot: Option, @@ -342,7 +343,10 @@ pub fn process_blockstore( account_paths, &opts.frozen_accounts, opts.debug_keys.clone(), - Some(&crate::builtins::get(genesis_config.cluster_type)), + Some(&crate::builtins::get( + genesis_config.cluster_type, + opts.bpf_jit, + )), ); let bank0 = Arc::new(bank0); info!("processing ledger for slot 0..."); diff --git a/ledger/src/builtins.rs b/ledger/src/builtins.rs index f08c167d47..6151ae6f38 100644 --- a/ledger/src/builtins.rs +++ b/ledger/src/builtins.rs @@ -4,42 +4,42 @@ use solana_runtime::{ }; use solana_sdk::{feature_set, genesis_config::ClusterType, pubkey::Pubkey}; +macro_rules! to_builtin { + ($b:expr) => { + Builtin::new(&$b.0, $b.1, $b.2) + }; +} + /// Builtin programs that are always available -fn genesis_builtins(cluster_type: ClusterType) -> Vec { - let builtins = if cluster_type != ClusterType::MainnetBeta { +fn genesis_builtins(cluster_type: ClusterType, bpf_jit: bool) -> Vec { + if cluster_type != ClusterType::MainnetBeta { vec![ - solana_bpf_loader_deprecated_program!(), - solana_bpf_loader_program!(), + to_builtin!(solana_bpf_loader_deprecated_program!()), + if bpf_jit { + to_builtin!(solana_bpf_loader_program_with_jit!()) + } else { + to_builtin!(solana_bpf_loader_program!()) + }, ] } else { // Remove this `else` block and the `cluster_type` argument to this function once // `feature_set::bpf_loader2_program::id()` is active on Mainnet Beta - vec![solana_bpf_loader_deprecated_program!()] - }; - - builtins - .into_iter() - .map(|b| Builtin::new(&b.0, b.1, b.2)) - .collect() + vec![to_builtin!(solana_bpf_loader_deprecated_program!())] + } } /// Builtin programs activated dynamically by feature fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> { - let builtins = vec![( - solana_bpf_loader_program!(), + vec![( + to_builtin!(solana_bpf_loader_program!()), feature_set::bpf_loader2_program::id(), ActivationType::NewProgram, - )]; - - builtins - .into_iter() - .map(|(b, p, t)| (Builtin::new(&b.0, b.1, b.2), p, t)) - .collect() + )] } -pub(crate) fn get(cluster_type: ClusterType) -> Builtins { +pub(crate) fn get(cluster_type: ClusterType, bpf_jit: bool) -> Builtins { Builtins { - genesis_builtins: genesis_builtins(cluster_type), + genesis_builtins: genesis_builtins(cluster_type, bpf_jit), feature_builtins: feature_builtins(), } } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 2104927782..1a4df76057 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -4,6 +4,7 @@ pub mod bpf_verifier; pub mod deprecated; pub mod serialization; pub mod syscalls; +pub mod with_jit; use crate::{ bpf_verifier::VerifierError, @@ -91,11 +92,10 @@ fn map_ebpf_error( InstructionError::InvalidAccountData } -const IS_JIT_ENABLED: bool = false; - pub fn create_and_cache_executor( program: &KeyedAccount, invoke_context: &mut dyn InvokeContext, + use_jit: bool, ) -> Result, InstructionError> { let bpf_compute_budget = invoke_context.get_bpf_compute_budget(); let mut executable = Executable::::from_elf( @@ -120,7 +120,7 @@ pub fn create_and_cache_executor( let syscall_registry = syscalls::register_syscalls(invoke_context) .map_err(|e| map_ebpf_error(invoke_context, e))?; executable.set_syscall_registry(syscall_registry); - if IS_JIT_ENABLED && executable.jit_compile().is_err() { + if use_jit && executable.jit_compile().is_err() { return Err(BPFLoaderError::JustInTimeCompilationFailed.into()); } let executor = Arc::new(BPFExecutor { executable }); @@ -153,11 +153,12 @@ pub fn create_vm<'a>( Ok(vm) } -pub fn process_instruction( +fn process_instruction_general( program_id: &Pubkey, keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, + use_jit: bool, ) -> Result<(), InstructionError> { debug_assert!(bpf_loader::check_id(program_id) || bpf_loader_deprecated::check_id(program_id)); @@ -172,9 +173,15 @@ pub fn process_instruction( if is_executable(keyed_accounts)? { let executor = match invoke_context.get_executor(program.unsigned_key()) { Some(executor) => executor, - None => create_and_cache_executor(program, invoke_context)?, + None => create_and_cache_executor(program, invoke_context, use_jit)?, }; - executor.execute(program_id, keyed_accounts, instruction_data, invoke_context)? + executor.execute( + program_id, + keyed_accounts, + instruction_data, + invoke_context, + use_jit, + )? } else if !keyed_accounts.is_empty() { match limited_deserialize(instruction_data)? { LoaderInstruction::Write { offset, bytes } => { @@ -201,7 +208,7 @@ pub fn process_instruction( return Err(InstructionError::MissingRequiredSignature); } - let _ = create_and_cache_executor(program, invoke_context)?; + let _ = create_and_cache_executor(program, invoke_context, use_jit)?; program.try_account_ref_mut()?.executable = true; log!( logger, @@ -214,6 +221,36 @@ pub fn process_instruction( Ok(()) } +pub fn process_instruction( + program_id: &Pubkey, + keyed_accounts: &[KeyedAccount], + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext, +) -> Result<(), InstructionError> { + process_instruction_general( + program_id, + keyed_accounts, + instruction_data, + invoke_context, + false, + ) +} + +pub fn process_instruction_jit( + program_id: &Pubkey, + keyed_accounts: &[KeyedAccount], + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext, +) -> Result<(), InstructionError> { + process_instruction_general( + program_id, + keyed_accounts, + instruction_data, + invoke_context, + true, + ) +} + /// Passed to the VM to enforce the compute budget pub struct ThisInstructionMeter { pub compute_meter: Rc>, @@ -253,6 +290,7 @@ impl Executor for BPFExecutor { keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, + use_jit: bool, ) -> Result<(), InstructionError> { let logger = invoke_context.get_logger(); let invoke_depth = invoke_context.invoke_depth(); @@ -286,7 +324,7 @@ impl Executor for BPFExecutor { stable_log::program_invoke(&logger, program.unsigned_key(), invoke_depth); let mut instruction_meter = ThisInstructionMeter::new(compute_meter.clone()); let before = compute_meter.borrow().get_remaining(); - let result = if IS_JIT_ENABLED { + let result = if use_jit { vm.execute_program_jit(&mut instruction_meter) } else { vm.execute_program_interpreted(&mut instruction_meter) diff --git a/programs/bpf_loader/src/with_jit.rs b/programs/bpf_loader/src/with_jit.rs new file mode 100644 index 0000000000..43aafc690c --- /dev/null +++ b/programs/bpf_loader/src/with_jit.rs @@ -0,0 +1,5 @@ +solana_sdk::declare_builtin!( + solana_sdk::bpf_loader::ID, + solana_bpf_loader_program_with_jit, + solana_bpf_loader_program::process_instruction_jit +); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index eab3db251c..c0d01ae9ae 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -10085,6 +10085,7 @@ pub(crate) mod tests { _keyed_accounts: &[KeyedAccount], _instruction_data: &[u8], _invoke_context: &mut dyn InvokeContext, + _use_jit: bool, ) -> std::result::Result<(), InstructionError> { Ok(()) } diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 6c65c285df..10bbcc6da3 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -241,6 +241,7 @@ pub trait Executor: Debug + Send + Sync { keyed_accounts: &[KeyedAccount], instruction_data: &[u8], invoke_context: &mut dyn InvokeContext, + use_jit: bool, ) -> Result<(), InstructionError>; } diff --git a/validator/src/main.rs b/validator/src/main.rs index da8b8608ee..43975284e7 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1398,6 +1398,12 @@ pub fn main() { "Mode to recovery the ledger db write ahead log." ), ) + .arg( + Arg::with_name("bpf_jit") + .long("bpf-jit") + .takes_value(false) + .help("Use the just-in-time compiler instead of the interpreter for BPF."), + ) .get_matches(); let identity_keypair = Arc::new(keypair_of(&matches, "identity").unwrap_or_else(Keypair::new)); @@ -1540,6 +1546,7 @@ pub fn main() { poh_verify: !matches.is_present("skip_poh_verify"), debug_keys, contact_debug_interval, + bpf_jit: matches.is_present("bpf_jit"), ..ValidatorConfig::default() };