use std::sync::{Arc, OnceLock};
use agave_feature_set::FeatureSet;
use solana_address::Address;
use solana_compute_budget::compute_budget::ComputeBudget;
use solana_program_runtime::{
invoke_context::InvokeContext,
loaded_programs::{ProgramRuntimeEnvironment, ProgramRuntimeEnvironments},
program_cache_entry::ProgramCacheEntry,
program_metrics::LoadProgramMetrics,
solana_sbpf::program::BuiltinProgram,
};
use solana_syscalls::create_program_runtime_environment;
use crate::{FeatureGate, HPSVMError, Svm, SvmTypesHost, TxFeatureFlags};
pub(crate) static DEFAULT_RUNTIME_ENV: OnceLock<Arc<ProgramRuntimeEnvironments>> = OnceLock::new();
pub(crate) fn shared_default_env_ptr(envs: &Arc<ProgramRuntimeEnvironments>) -> Option<usize> {
let shared = DEFAULT_RUNTIME_ENV.get()?;
Arc::ptr_eq(envs, shared).then_some(Arc::as_ptr(shared) as usize)
}
impl<T: SvmTypesHost> Svm<'_, T> {
pub(crate) fn try_refresh_runtime_environments(&mut self) -> Result<(), HPSVMError> {
#[cfg(feature = "register-tracing")]
let enable_register_tracing = self.enable_register_tracing;
#[cfg(not(feature = "register-tracing"))]
let enable_register_tracing = false;
let is_default_config = !enable_register_tracing &&
self.version.compute_budget.is_none() &&
self.runtime_registry.custom_syscalls().is_empty() &&
self.version.feature_set.as_ref() == &FeatureSet::all_enabled();
if is_default_config {
let shared = DEFAULT_RUNTIME_ENV.get();
if let Some(env) = shared {
self.accounts.set_runtime_environments_arc(Arc::clone(env));
return Ok(());
}
}
let flags = TxFeatureFlags::load(&self.version.feature_set);
let compute_budget = self.version.compute_budget.unwrap_or_else(|| {
ComputeBudget::new_with_defaults(flags.is_enabled(FeatureGate::RaiseCpiNestingLimitTo8))
});
let program_runtime = create_program_runtime_environment(
&self.version.feature_set.runtime_features(),
&compute_budget.to_budget(),
false,
enable_register_tracing,
)
.map_err(|error| HPSVMError::RuntimeEnvironment {
version: "v1",
reason: error.to_string(),
})?;
let mut current_runtime = program_runtime;
for syscall in self.runtime_registry.custom_syscalls() {
let config = current_runtime.get_config().clone();
let mut loader: BuiltinProgram<InvokeContext<'static, 'static>> =
BuiltinProgram::new_loader(config);
for (_key, (name, value)) in current_runtime.get_function_registry().iter() {
let name = std::str::from_utf8(name).expect("syscall names are valid UTF-8");
loader.register_function(name, value).map_err(|error| {
HPSVMError::CustomSyscallRegistration {
name: name.to_owned(),
runtime: "runtime",
reason: error.to_string(),
}
})?;
}
(syscall.function)(&mut loader, &syscall.name).map_err(|error| {
HPSVMError::CustomSyscallRegistration {
name: syscall.name.clone(),
runtime: "runtime",
reason: error.to_string(),
}
})?;
current_runtime = ProgramRuntimeEnvironment::from(loader);
}
let program_runtime = current_runtime;
let env =
Arc::new(ProgramRuntimeEnvironments::new(program_runtime.clone(), program_runtime));
if is_default_config {
let _ = DEFAULT_RUNTIME_ENV.set(Arc::clone(&env));
}
self.accounts.set_runtime_environments_arc(env);
Ok(())
}
pub(crate) fn refresh_runtime_environments(&mut self) {
self.try_refresh_runtime_environments()
.expect("runtime environment refresh should never fail for internal configuration");
}
pub(crate) fn resolve_program_entry<const CACHED: bool>(
&self,
loader_id: &Address,
env: &ProgramRuntimeEnvironment,
current_slot: u64,
program_bytes: &[u8],
) -> Result<Arc<ProgramCacheEntry>, HPSVMError> {
use crate::{
accounts_db::IMMEDIATE_DEPLOYMENT_SLOT,
programs::{content_key, default_program_cache, program_entry_store},
};
let envs_arc = self.accounts.runtime_environments_arc();
let env_ptr = Arc::as_ptr(envs_arc) as usize;
let fast_key = if CACHED && current_slot == 0 {
shared_default_env_ptr(envs_arc)
.map(|ptr| (ptr, program_bytes.as_ptr() as usize, program_bytes.len(), *loader_id))
} else {
None
};
if let Some(key) = fast_key &&
let Some(entry) = default_program_cache().read().get(&key).cloned()
{
return Ok(entry);
}
let store_key = content_key(env_ptr, program_bytes, loader_id, IMMEDIATE_DEPLOYMENT_SLOT);
if let Some(entry) = program_entry_store().get(&store_key) {
if let Some(key) = fast_key {
let winner = default_program_cache()
.write()
.entry(key)
.or_insert_with(|| Arc::clone(&entry))
.clone();
return Ok(winner);
}
return Ok(entry);
}
let loaded =
if self.version.jit_policy.requires_uncompiled_load() && cfg!(target_arch = "x86_64") {
crate::programs::load_program_uncompiled(loader_id, env, program_bytes)
.map_err(HPSVMError::from)?
} else {
ProgramCacheEntry::load(
loader_id,
env.clone(),
crate::accounts_db::IMMEDIATE_DEPLOYMENT_SLOT,
program_bytes,
&mut LoadProgramMetrics::default(),
)
.map_err(HPSVMError::from)?
};
let arc = Arc::new(loaded);
let resident = program_entry_store().insert(store_key, Arc::clone(&arc));
if let Some(key) = fast_key {
let winner = Arc::clone(&resident);
let cached = default_program_cache().write().entry(key).or_insert(winner).clone();
return Ok(cached);
}
Ok(resident)
}
}