mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-12 06:31:14 +00:00
Add ability for compiler to opt out if it can't work with some Mode/version
This commit is contained in:
@@ -20,7 +20,7 @@ use revive_dt_common::types::VersionOrRequirement;
|
||||
use revive_dt_config::Arguments;
|
||||
|
||||
// Re-export this as it's a part of the compiler interface.
|
||||
pub use revive_dt_format::mode::ModeOptimizerSetting;
|
||||
pub use revive_dt_format::mode::{Mode, ModeOptimizerSetting, ModePipeline};
|
||||
|
||||
pub mod revive_js;
|
||||
pub mod revive_resolc;
|
||||
@@ -46,13 +46,20 @@ pub trait SolidityCompiler {
|
||||
) -> impl Future<Output = anyhow::Result<PathBuf>>;
|
||||
|
||||
fn version(&self) -> anyhow::Result<Version>;
|
||||
|
||||
/// Does the compiler support the provided mode and version settings?
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// The generic compilation input configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CompilerInput {
|
||||
pub pipeline: Option<ModePipeline>,
|
||||
pub optimization: Option<ModeOptimizerSetting>,
|
||||
pub via_ir: Option<bool>,
|
||||
pub evm_version: Option<EVMVersion>,
|
||||
pub allow_paths: Vec<PathBuf>,
|
||||
pub base_path: Option<PathBuf>,
|
||||
@@ -87,8 +94,8 @@ where
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
input: CompilerInput {
|
||||
pipeline: Default::default(),
|
||||
optimization: Default::default(),
|
||||
via_ir: Default::default(),
|
||||
evm_version: Default::default(),
|
||||
allow_paths: Default::default(),
|
||||
base_path: Default::default(),
|
||||
@@ -104,8 +111,8 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_via_ir(mut self, value: impl Into<Option<bool>>) -> Self {
|
||||
self.input.via_ir = value.into();
|
||||
pub fn with_pipeline(mut self, value: impl Into<Option<ModePipeline>>) -> Self {
|
||||
self.input.pipeline = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use revive_solc_json_interface::{
|
||||
SolcStandardJsonOutput,
|
||||
};
|
||||
|
||||
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, SolidityCompiler};
|
||||
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
|
||||
|
||||
use alloy::json_abi::JsonAbi;
|
||||
use anyhow::Context;
|
||||
@@ -39,9 +39,9 @@ impl SolidityCompiler for Resolc {
|
||||
async fn build(
|
||||
&self,
|
||||
CompilerInput {
|
||||
// Ignored as we only support one pipeline (Y)
|
||||
pipeline,
|
||||
optimization,
|
||||
// Ignored and not honored since this is required for the resolc compilation.
|
||||
via_ir: _via_ir,
|
||||
evm_version,
|
||||
allow_paths,
|
||||
base_path,
|
||||
@@ -231,6 +231,17 @@ impl SolidityCompiler for Resolc {
|
||||
|
||||
Version::parse(version_string).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
_optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool {
|
||||
// We only support the Y (IE compile via Yul IR) mode here, which also means that we can
|
||||
// only use solc version 0.8.13 and above. We must always compile via Yul IR as resolc
|
||||
// needs this to translate to LLVM IR and then RISCV.
|
||||
pipeline == ModePipeline::Y && compiler_version >= &Version::new(0, 8, 13)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -10,7 +10,7 @@ use revive_dt_common::types::VersionOrRequirement;
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_solc_binaries::download_solc;
|
||||
|
||||
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
||||
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
|
||||
|
||||
use anyhow::Context;
|
||||
use foundry_compilers_artifacts::{
|
||||
@@ -35,8 +35,8 @@ impl SolidityCompiler for Solc {
|
||||
async fn build(
|
||||
&self,
|
||||
CompilerInput {
|
||||
pipeline,
|
||||
optimization,
|
||||
via_ir,
|
||||
evm_version,
|
||||
allow_paths,
|
||||
base_path,
|
||||
@@ -70,7 +70,7 @@ impl SolidityCompiler for Solc {
|
||||
.map(|item| item.to_string()),
|
||||
),
|
||||
evm_version: evm_version.map(|version| version.to_string().parse().unwrap()),
|
||||
via_ir,
|
||||
via_ir: pipeline.map(|p| p.via_yul_ir()),
|
||||
libraries: Libraries {
|
||||
libs: libraries
|
||||
.into_iter()
|
||||
@@ -212,6 +212,17 @@ impl SolidityCompiler for Solc {
|
||||
|
||||
Version::parse(version_string).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
_optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool {
|
||||
// solc 0.8.13 and above supports --via-ir, and less than that does not. Thus, we support mode E
|
||||
// (ie no Yul IR) in either case, but only support Y (via Yul IR) if the compiler is new enough.
|
||||
pipeline == ModePipeline::E
|
||||
|| (pipeline == ModePipeline::Y && compiler_version >= &Version::new(0, 8, 13))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+70
-38
@@ -324,7 +324,10 @@ where
|
||||
.await;
|
||||
let mut metadata_case_status = metadata_case_status.write().await;
|
||||
match result {
|
||||
Ok(inputs_executed) => {
|
||||
Ok(None) => {
|
||||
tracing::info!("Execution skipped");
|
||||
}
|
||||
Ok(Some(inputs_executed)) => {
|
||||
tracing::info!(inputs_executed, "Execution succeeded");
|
||||
metadata_case_status
|
||||
.entry((metadata_file_path.clone(), solc_mode))
|
||||
@@ -362,14 +365,14 @@ async fn handle_case_driver<'a, L, F>(
|
||||
leader_node: &L::Blockchain,
|
||||
follower_node: &F::Blockchain,
|
||||
_: Span,
|
||||
) -> anyhow::Result<usize>
|
||||
) -> anyhow::Result<Option<usize>>
|
||||
where
|
||||
L: Platform,
|
||||
F: Platform,
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
let leader_pre_link_contracts = get_or_build_contracts::<L>(
|
||||
let Some(leader_pre_link_contracts) = get_or_build_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
@@ -377,8 +380,11 @@ where
|
||||
compilation_cache.clone(),
|
||||
&HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
let follower_pre_link_contracts = get_or_build_contracts::<F>(
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(follower_pre_link_contracts) = get_or_build_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
@@ -386,7 +392,10 @@ where
|
||||
compilation_cache.clone(),
|
||||
&HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut leader_deployed_libraries = HashMap::new();
|
||||
let mut follower_deployed_libraries = HashMap::new();
|
||||
@@ -537,7 +546,7 @@ where
|
||||
cache.remove(&follower_key);
|
||||
}
|
||||
|
||||
let leader_post_link_contracts = get_or_build_contracts::<L>(
|
||||
let Some(leader_post_link_contracts) = get_or_build_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
@@ -545,8 +554,11 @@ where
|
||||
compilation_cache.clone(),
|
||||
&leader_deployed_libraries,
|
||||
)
|
||||
.await?;
|
||||
let follower_post_link_contracts = get_or_build_contracts::<F>(
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(follower_post_link_contracts) = get_or_build_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
@@ -554,7 +566,10 @@ where
|
||||
compilation_cache,
|
||||
&follower_deployed_libraries,
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
(leader_post_link_contracts, follower_post_link_contracts)
|
||||
} else {
|
||||
@@ -581,7 +596,7 @@ where
|
||||
leader_state,
|
||||
follower_state,
|
||||
);
|
||||
driver.execute().await
|
||||
driver.execute().await.map(Some)
|
||||
}
|
||||
|
||||
async fn get_or_build_contracts<'a, P: Platform>(
|
||||
@@ -591,29 +606,32 @@ async fn get_or_build_contracts<'a, P: Platform>(
|
||||
config: &Arguments,
|
||||
compilation_cache: CompilationCache<'a>,
|
||||
deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
) -> anyhow::Result<Arc<(Version, CompilerOutput)>> {
|
||||
) -> anyhow::Result<Option<Arc<(Version, CompilerOutput)>>> {
|
||||
let key = (metadata_file_path, mode.clone(), P::config_id());
|
||||
if let Some(compilation_artifact) = compilation_cache.read().await.get(&key).cloned() {
|
||||
let mut compilation_artifact = compilation_artifact.lock().await;
|
||||
match *compilation_artifact {
|
||||
Some(ref compiled_contracts) => {
|
||||
tracing::debug!(?key, "Compiled contracts cache hit");
|
||||
return Ok(compiled_contracts.clone());
|
||||
return Ok(Some(compiled_contracts.clone()));
|
||||
}
|
||||
None => {
|
||||
tracing::debug!(?key, "Compiled contracts cache miss");
|
||||
let compiled_contracts = Arc::new(
|
||||
compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
let Some(compiled_contracts) = compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let compiled_contracts = Arc::new(compiled_contracts);
|
||||
|
||||
*compilation_artifact = Some(compiled_contracts.clone());
|
||||
return Ok(compiled_contracts.clone());
|
||||
return Ok(Some(compiled_contracts.clone()));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -626,18 +644,22 @@ async fn get_or_build_contracts<'a, P: Platform>(
|
||||
mutex
|
||||
};
|
||||
let mut compilation_artifact = mutex.lock().await;
|
||||
let compiled_contracts = Arc::new(
|
||||
compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
let Some(compiled_contracts) = compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let compiled_contracts = Arc::new(compiled_contracts);
|
||||
|
||||
*compilation_artifact = Some(compiled_contracts.clone());
|
||||
Ok(compiled_contracts.clone())
|
||||
Ok(Some(compiled_contracts.clone()))
|
||||
}
|
||||
|
||||
async fn compile_contracts<P: Platform>(
|
||||
@@ -646,12 +668,22 @@ async fn compile_contracts<P: Platform>(
|
||||
mode: &Mode,
|
||||
config: &Arguments,
|
||||
deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
) -> anyhow::Result<(Version, CompilerOutput)> {
|
||||
) -> anyhow::Result<Option<(Version, CompilerOutput)>> {
|
||||
let compiler_version_or_requirement = mode.compiler_version_to_use(config.solc.clone());
|
||||
let compiler_path =
|
||||
P::Compiler::get_compiler_executable(config, compiler_version_or_requirement).await?;
|
||||
let compiler_version = P::Compiler::new(compiler_path.clone()).version()?;
|
||||
|
||||
if !P::Compiler::supports_mode(&compiler_version, mode.optimize_setting, mode.pipeline) {
|
||||
tracing::info!(
|
||||
%compiler_version,
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
mode = ?mode,
|
||||
"Skipping compilation: compiler does not support this mode"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
%compiler_version,
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
@@ -662,7 +694,7 @@ async fn compile_contracts<P: Platform>(
|
||||
let compiler = Compiler::<P::Compiler>::new()
|
||||
.with_allow_path(metadata.directory()?)
|
||||
.with_optimization(mode.optimize_setting)
|
||||
.with_via_ir(mode.via_yul_ir());
|
||||
.with_pipeline(mode.pipeline);
|
||||
let mut compiler = metadata
|
||||
.files_to_compile()?
|
||||
.try_fold(compiler, |compiler, path| compiler.with_source(&path))?;
|
||||
@@ -688,7 +720,7 @@ async fn compile_contracts<P: Platform>(
|
||||
|
||||
let compiler_output = compiler.try_build(compiler_path).await?;
|
||||
|
||||
Ok((compiler_version, compiler_output))
|
||||
Ok(Some((compiler_version, compiler_output)))
|
||||
}
|
||||
|
||||
async fn execute_corpus(
|
||||
|
||||
@@ -61,11 +61,6 @@ impl Mode {
|
||||
None => default.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Should we go via Yul IR?
|
||||
pub fn via_yul_ir(&self) -> bool {
|
||||
self.pipeline == ModePipeline::Y
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a mode that has been parsed from test metadata.
|
||||
@@ -281,6 +276,11 @@ impl Display for ModePipeline {
|
||||
}
|
||||
|
||||
impl ModePipeline {
|
||||
/// Should we go via Yul IR?
|
||||
pub fn via_yul_ir(&self) -> bool {
|
||||
matches!(self, ModePipeline::Y)
|
||||
}
|
||||
|
||||
/// An iterator over the available pipelines that we'd like to test,
|
||||
/// when an explicit pipeline was not specified.
|
||||
pub fn test_cases() -> impl Iterator<Item = ModePipeline> + Clone {
|
||||
|
||||
Reference in New Issue
Block a user