Suport passing arbitrary llvm arguments (#271)

- Support for passing LLVM command line options via the prcoess input or
providing one or more `--llvm-arg='..'` resolc CLI flag. This allows
more fine-grained control over the LLVM backend configuration.
- Make LLVM initialization idempotent.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus
2025-04-03 13:21:00 +02:00
committed by GitHub
parent 8a98346a9c
commit 87c1d7a8be
16 changed files with 142 additions and 26 deletions
+30 -4
View File
@@ -54,6 +54,7 @@ pub fn yul<T: Compiler>(
optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -74,7 +75,12 @@ pub fn yul<T: Compiler>(
let solc_validator = Some(&*solc);
let project = Project::try_from_yul_path(path, solc_validator)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
let build = project.compile(
optimizer_settings,
include_metadata_hash,
debug_config,
llvm_arguments,
)?;
Ok(build)
}
@@ -85,6 +91,7 @@ pub fn llvm_ir(
optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -97,7 +104,12 @@ pub fn llvm_ir(
let project = Project::try_from_llvm_ir_path(path)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
let build = project.compile(
optimizer_settings,
include_metadata_hash,
debug_config,
llvm_arguments,
)?;
Ok(build)
}
@@ -118,6 +130,7 @@ pub fn standard_output<T: Compiler>(
remappings: Option<BTreeSet<String>>,
suppressed_warnings: Option<Vec<ResolcWarning>>,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<Build> {
let solc_version = solc.version()?;
@@ -171,7 +184,12 @@ pub fn standard_output<T: Compiler>(
&debug_config,
)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
let build = project.compile(
optimizer_settings,
include_metadata_hash,
debug_config,
llvm_arguments,
)?;
Ok(build)
}
@@ -184,6 +202,7 @@ pub fn standard_json<T: Compiler>(
include_paths: Vec<String>,
allow_paths: Option<String>,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<()> {
let solc_version = solc.version()?;
@@ -226,7 +245,12 @@ pub fn standard_json<T: Compiler>(
let missing_libraries = project.get_missing_libraries();
missing_libraries.write_to_standard_json(&mut solc_output, &solc_version)?;
} else {
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
let build = project.compile(
optimizer_settings,
include_metadata_hash,
debug_config,
llvm_arguments,
)?;
build.write_to_standard_json(&mut solc_output, &solc_version)?;
}
serde_json::to_writer(std::io::stdout(), &solc_output)?;
@@ -252,6 +276,7 @@ pub fn combined_json<T: Compiler>(
debug_config: revive_llvm_context::DebugConfig,
output_directory: Option<PathBuf>,
overwrite: bool,
llvm_arguments: &[String],
) -> anyhow::Result<()> {
let build = standard_output(
input_files,
@@ -267,6 +292,7 @@ pub fn combined_json<T: Compiler>(
remappings,
suppressed_warnings,
debug_config,
llvm_arguments,
)?;
let mut combined_json = solc.combined_json(input_files, format.as_str())?;
+4
View File
@@ -20,6 +20,8 @@ pub struct Input {
pub optimizer_settings: revive_llvm_context::OptimizerSettings,
/// The debug output config.
pub debug_config: revive_llvm_context::DebugConfig,
/// The extra LLVM arguments give used for manual control.
pub llvm_arguments: Vec<String>,
}
impl Input {
@@ -30,6 +32,7 @@ impl Input {
include_metadata_hash: bool,
optimizer_settings: revive_llvm_context::OptimizerSettings,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: Vec<String>,
) -> Self {
Self {
contract,
@@ -37,6 +40,7 @@ impl Input {
include_metadata_hash,
optimizer_settings,
debug_config,
llvm_arguments,
}
}
}
+8
View File
@@ -37,11 +37,19 @@ pub trait Process {
}
let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?;
revive_llvm_context::initialize_llvm(
revive_llvm_context::Target::PVM,
crate::DEFAULT_EXECUTABLE_NAME,
&input.llvm_arguments,
);
let result = input.contract.compile(
input.project,
input.optimizer_settings,
input.include_metadata_hash,
input.debug_config,
&input.llvm_arguments,
);
match result {
@@ -18,6 +18,8 @@ pub struct Metadata {
pub revive_version: String,
/// The PolkaVM compiler optimizer settings.
pub optimizer_settings: revive_llvm_context::OptimizerSettings,
/// The extra LLVM arguments give used for manual control.
pub llvm_arguments: Vec<String>,
}
impl Metadata {
@@ -27,6 +29,7 @@ impl Metadata {
solc_version: String,
revive_pallet_version: Option<semver::Version>,
optimizer_settings: revive_llvm_context::OptimizerSettings,
llvm_arguments: Vec<String>,
) -> Self {
Self {
solc_metadata,
@@ -34,6 +37,7 @@ impl Metadata {
revive_pallet_version,
revive_version: ResolcVersion::default().long,
optimizer_settings,
llvm_arguments,
}
}
}
@@ -77,6 +77,7 @@ impl Contract {
optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<ContractBuild> {
let llvm = inkwell::context::Context::create();
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
@@ -89,6 +90,7 @@ impl Contract {
version.long.clone(),
version.l2_revision.clone(),
optimizer.settings().to_owned(),
llvm_arguments.to_vec(),
);
let metadata_json = serde_json::to_value(&metadata).expect("Always valid");
let metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]> = if include_metadata_hash
@@ -120,6 +122,7 @@ impl Contract {
Some(project),
include_metadata_hash,
debug_config,
llvm_arguments,
);
context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default());
match self.ir {
+4
View File
@@ -66,6 +66,7 @@ impl Project {
optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<Build> {
let project = self.clone();
#[cfg(feature = "parallel")]
@@ -81,6 +82,7 @@ impl Project {
include_metadata_hash,
optimizer_settings.clone(),
debug_config.clone(),
llvm_arguments.to_vec(),
);
let process_output = {
#[cfg(target_os = "emscripten")]
@@ -316,6 +318,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
optimizer_settings: revive_llvm_context::OptimizerSettings,
include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String],
) -> anyhow::Result<String> {
let contract_path = project.resolve_path(identifier)?;
let contract = project
@@ -335,6 +338,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
optimizer_settings,
include_metadata_hash,
debug_config,
llvm_arguments,
)
.map_err(|error| {
anyhow::anyhow!(
+4
View File
@@ -166,6 +166,10 @@ pub struct Arguments {
#[cfg(debug_assertions)]
#[arg(long = "recursive-process-input")]
pub recursive_process_input: Option<String>,
#[arg(long = "llvm-arg")]
/// These are passed to LLVM as the command line to allow manual control.
pub llvm_arguments: Vec<String>,
}
impl Arguments {
+5 -2
View File
@@ -64,8 +64,6 @@ fn main_inner() -> anyhow::Result<()> {
.stack_size(RAYON_WORKER_STACK_SIZE)
.build_global()
.expect("Thread pool configuration failure");
inkwell::support::enable_llvm_pretty_stack_trace();
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM); // TODO: pass from CLI
if arguments.recursive_process {
#[cfg(debug_assertions)]
@@ -157,6 +155,7 @@ fn main_inner() -> anyhow::Result<()> {
optimizer_settings,
include_metadata_hash,
debug_config,
&arguments.llvm_arguments,
)
} else if arguments.llvm_ir {
revive_solidity::llvm_ir(
@@ -164,6 +163,7 @@ fn main_inner() -> anyhow::Result<()> {
optimizer_settings,
include_metadata_hash,
debug_config,
&arguments.llvm_arguments,
)
} else if arguments.standard_json {
revive_solidity::standard_json(
@@ -173,6 +173,7 @@ fn main_inner() -> anyhow::Result<()> {
arguments.include_paths,
arguments.allow_paths,
debug_config,
&arguments.llvm_arguments,
)?;
return Ok(());
} else if let Some(format) = arguments.combined_json {
@@ -193,6 +194,7 @@ fn main_inner() -> anyhow::Result<()> {
debug_config,
arguments.output_directory,
arguments.overwrite,
&arguments.llvm_arguments,
)?;
return Ok(());
} else {
@@ -210,6 +212,7 @@ fn main_inner() -> anyhow::Result<()> {
remappings,
suppressed_warnings,
debug_config,
&arguments.llvm_arguments,
)
}?;
+23 -6
View File
@@ -73,7 +73,11 @@ pub fn build_solidity_with_options(
check_dependencies();
inkwell::support::enable_llvm_pretty_stack_trace();
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
revive_llvm_context::initialize_llvm(
revive_llvm_context::Target::PVM,
crate::DEFAULT_EXECUTABLE_NAME,
&[],
);
let _ = crate::process::native_process::EXECUTABLE
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
@@ -106,7 +110,8 @@ pub fn build_solidity_with_options(
&DEBUG_CONFIG,
)?;
let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
let build: crate::Build =
project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?;
build.write_to_standard_json(&mut output, &solc_version)?;
Ok(output)
@@ -122,7 +127,11 @@ pub fn build_solidity_with_options_evm(
check_dependencies();
inkwell::support::enable_llvm_pretty_stack_trace();
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
revive_llvm_context::initialize_llvm(
revive_llvm_context::Target::PVM,
crate::DEFAULT_EXECUTABLE_NAME,
&[],
);
let _ = crate::process::native_process::EXECUTABLE
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
@@ -174,7 +183,11 @@ pub fn build_solidity_and_detect_missing_libraries(
check_dependencies();
inkwell::support::enable_llvm_pretty_stack_trace();
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
revive_llvm_context::initialize_llvm(
revive_llvm_context::Target::PVM,
crate::DEFAULT_EXECUTABLE_NAME,
&[],
);
let _ = crate::process::native_process::EXECUTABLE
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
@@ -213,7 +226,11 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
check_dependencies();
inkwell::support::enable_llvm_pretty_stack_trace();
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
revive_llvm_context::initialize_llvm(
revive_llvm_context::Target::PVM,
crate::DEFAULT_EXECUTABLE_NAME,
&[],
);
let optimizer_settings = revive_llvm_context::OptimizerSettings::none();
let project = Project::try_from_yul_string::<SolcCompiler>(
@@ -221,7 +238,7 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
source_code,
None,
)?;
let _build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
let _build = project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?;
Ok(())
}