solc-json-interface: support for YUL optimizer details (#361)

- Add support for the YUL optimizer details in the standard json input
definition.
- Make optimizer settings optional. They can be omitted and solc will
pick default values ([see here for
reference](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description)).

For example allows to use the
[`yul-phaser`](https://github.com/ethereum/solidity/blob/0917604a5ec7cff8bd40a1137f4fcb303fb90527/tools/yulPhaser/README.md?plain=1)
utility. I did a single search with slightly adjusted costs (just made
some educated guess) and after an hour or so this already found an
optimizer sequence
(`OESsShMxeoufcSTvlFxtelTfnfEvicdFxnsvopgCaIeLcnvsTtUrUgdVTUttaeUomccTTTuujsVVvVDvvueUrTjUOmjrrhuuTtj`)
which shrinks the size of the `EndpointV2.sol` from LayerZero by 10%.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-07-14 11:25:45 +02:00
committed by GitHub
parent 141a8b752c
commit a0396dd6d0
9 changed files with 78 additions and 29 deletions
+1
View File
@@ -14,6 +14,7 @@ Supported `polkadot-sdk` rev: `2503.0.1`
### Added
- Line debug information per YUL builtin and for `if` statements.
- Support for the YUL optimizer details in the standard json input definition.
### Fixed
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
+1 -3
View File
@@ -45,8 +45,6 @@ impl Compiler for SolcCompiler {
include_paths: Vec<String>,
allow_paths: Option<String>,
) -> anyhow::Result<SolcStandardJsonOutput> {
let version = self.version()?.validate(&include_paths)?.default;
let mut command = std::process::Command::new(self.executable.as_str());
command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped());
@@ -65,7 +63,7 @@ impl Compiler for SolcCompiler {
command.arg(allow_paths);
}
input.normalize(&version);
input.normalize();
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
+1 -2
View File
@@ -40,8 +40,7 @@ impl Compiler for SoljsonCompiler {
anyhow::bail!("configuring allow paths is not supported with solJson")
}
let version = self.version()?.validate(&include_paths)?.default;
input.normalize(&version);
input.normalize();
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
+1
View File
@@ -7,6 +7,7 @@ pub use self::combined_json::contract::Contract as CombinedJsonContract;
pub use self::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash;
pub use self::standard_json::input::settings::optimizer::yul_details::YulDetails as SolcStandardJsonInputSettingsYulOptimizerDetails;
pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
pub use self::standard_json::input::settings::polkavm::memory::MemoryConfig as SolcStandardJsonInputSettingsPolkaVMMemory;
pub use self::standard_json::input::settings::polkavm::memory::DEFAULT_HEAP_SIZE as PolkaVMDefaultHeapMemorySize;
@@ -140,7 +140,7 @@ impl Input {
}
/// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) {
self.settings.normalize(version);
pub fn normalize(&mut self) {
self.settings.normalize();
}
}
@@ -74,9 +74,9 @@ impl Settings {
}
/// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) {
pub fn normalize(&mut self) {
self.polkavm = None;
self.optimizer.normalize(version);
self.optimizer.normalize();
}
/// Parses the library list and returns their double hashmap with path and name as keys.
@@ -3,37 +3,54 @@
use serde::Deserialize;
use serde::Serialize;
use crate::standard_json::input::settings::optimizer::yul_details::YulDetails;
/// The `solc --standard-json` input settings optimizer details.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Details {
/// Whether the pass is enabled.
pub peephole: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub peephole: Option<bool>,
/// Whether the pass is enabled.
#[serde(skip_serializing_if = "Option::is_none")]
pub inliner: Option<bool>,
/// Whether the pass is enabled.
pub jumpdest_remover: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub jumpdest_remover: Option<bool>,
/// Whether the pass is enabled.
pub order_literals: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub order_literals: Option<bool>,
/// Whether the pass is enabled.
pub deduplicate: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub deduplicate: Option<bool>,
/// Whether the pass is enabled.
pub cse: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub cse: Option<bool>,
/// Whether the pass is enabled.
pub constant_optimizer: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub constant_optimizer: Option<bool>,
/// Whether the YUL optimizer is enabled.
#[serde(skip_serializing_if = "Option::is_none")]
pub yul: Option<bool>,
/// The YUL optimizer configuration.
#[serde(skip_serializing_if = "Option::is_none")]
pub yul_details: Option<YulDetails>,
}
impl Details {
/// A shortcut constructor.
#[allow(clippy::too_many_arguments)]
pub fn new(
peephole: bool,
peephole: Option<bool>,
inliner: Option<bool>,
jumpdest_remover: bool,
order_literals: bool,
deduplicate: bool,
cse: bool,
constant_optimizer: bool,
jumpdest_remover: Option<bool>,
order_literals: Option<bool>,
deduplicate: Option<bool>,
cse: Option<bool>,
constant_optimizer: Option<bool>,
yul: Option<bool>,
yul_details: Option<YulDetails>,
) -> Self {
Self {
peephole,
@@ -43,10 +60,11 @@ impl Details {
deduplicate,
cse,
constant_optimizer,
yul,
yul_details,
}
}
/// Creates a set of disabled optimizations.
pub fn disabled(version: &semver::Version) -> Self {
let inliner = if version >= &semver::Version::new(0, 8, 5) {
Some(false)
@@ -54,6 +72,16 @@ impl Details {
None
};
Self::new(false, inliner, false, false, false, false, false)
Self::new(
Some(false),
inliner,
Some(false),
Some(false),
Some(false),
Some(false),
Some(false),
None,
None,
)
}
}
@@ -1,6 +1,7 @@
//! The `solc --standard-json` input settings optimizer.
pub mod details;
pub mod yul_details;
use serde::Deserialize;
use serde::Serialize;
@@ -41,13 +42,8 @@ impl Optimizer {
}
/// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) {
pub fn normalize(&mut self) {
self.mode = None;
self.fallback_to_optimizing_for_size = None;
self.details = if version >= &semver::Version::new(0, 5, 5) {
Some(Details::disabled(version))
} else {
None
};
}
}
@@ -0,0 +1,26 @@
//! The `solc --standard-json` input settings YUL optimizer details.
use serde::Deserialize;
use serde::Serialize;
/// The `solc --standard-json` input settings optimizer YUL details.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct YulDetails {
/// Whether the stack allocation pass is enabled.
#[serde(skip_serializing_if = "Option::is_none")]
pub stack_allocation: Option<bool>,
/// The optimization step sequence string.
#[serde(skip_serializing_if = "Option::is_none")]
pub optimizer_steps: Option<String>,
}
impl YulDetails {
/// A shortcut constructor.
pub fn new(stack_allocation: Option<bool>, optimizer_steps: Option<String>) -> Self {
Self {
stack_allocation,
optimizer_steps,
}
}
}