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 ### Added
- Line debug information per YUL builtin and for `if` statements. - Line debug information per YUL builtin and for `if` statements.
- Support for the YUL optimizer details in the standard json input definition.
### Fixed ### Fixed
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line. - 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>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
) -> anyhow::Result<SolcStandardJsonOutput> { ) -> anyhow::Result<SolcStandardJsonOutput> {
let version = self.version()?.validate(&include_paths)?.default;
let mut command = std::process::Command::new(self.executable.as_str()); let mut command = std::process::Command::new(self.executable.as_str());
command.stdin(std::process::Stdio::piped()); command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped()); command.stdout(std::process::Stdio::piped());
@@ -65,7 +63,7 @@ impl Compiler for SolcCompiler {
command.arg(allow_paths); command.arg(allow_paths);
} }
input.normalize(&version); input.normalize();
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); 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") anyhow::bail!("configuring allow paths is not supported with solJson")
} }
let version = self.version()?.validate(&include_paths)?.default; input.normalize();
input.normalize(&version);
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); 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::language::Language as SolcStandardJsonInputLanguage;
pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; 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::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::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::MemoryConfig as SolcStandardJsonInputSettingsPolkaVMMemory;
pub use self::standard_json::input::settings::polkavm::memory::DEFAULT_HEAP_SIZE as PolkaVMDefaultHeapMemorySize; pub use self::standard_json::input::settings::polkavm::memory::DEFAULT_HEAP_SIZE as PolkaVMDefaultHeapMemorySize;
@@ -140,7 +140,7 @@ impl Input {
} }
/// Sets the necessary defaults. /// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) { pub fn normalize(&mut self) {
self.settings.normalize(version); self.settings.normalize();
} }
} }
@@ -74,9 +74,9 @@ impl Settings {
} }
/// Sets the necessary defaults. /// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) { pub fn normalize(&mut self) {
self.polkavm = None; 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. /// Parses the library list and returns their double hashmap with path and name as keys.
@@ -3,37 +3,54 @@
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::standard_json::input::settings::optimizer::yul_details::YulDetails;
/// The `solc --standard-json` input settings optimizer details. /// The `solc --standard-json` input settings optimizer details.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Details { pub struct Details {
/// Whether the pass is enabled. /// Whether the pass is enabled.
pub peephole: bool, #[serde(skip_serializing_if = "Option::is_none")]
pub peephole: Option<bool>,
/// Whether the pass is enabled. /// Whether the pass is enabled.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub inliner: Option<bool>, pub inliner: Option<bool>,
/// Whether the pass is enabled. /// 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. /// 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. /// Whether the pass is enabled.
pub deduplicate: bool, #[serde(skip_serializing_if = "Option::is_none")]
pub deduplicate: Option<bool>,
/// Whether the pass is enabled. /// Whether the pass is enabled.
pub cse: bool, #[serde(skip_serializing_if = "Option::is_none")]
pub cse: Option<bool>,
/// Whether the pass is enabled. /// 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 { impl Details {
/// A shortcut constructor. /// A shortcut constructor.
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
peephole: bool, peephole: Option<bool>,
inliner: Option<bool>, inliner: Option<bool>,
jumpdest_remover: bool, jumpdest_remover: Option<bool>,
order_literals: bool, order_literals: Option<bool>,
deduplicate: bool, deduplicate: Option<bool>,
cse: bool, cse: Option<bool>,
constant_optimizer: bool, constant_optimizer: Option<bool>,
yul: Option<bool>,
yul_details: Option<YulDetails>,
) -> Self { ) -> Self {
Self { Self {
peephole, peephole,
@@ -43,10 +60,11 @@ impl Details {
deduplicate, deduplicate,
cse, cse,
constant_optimizer, constant_optimizer,
yul,
yul_details,
} }
} }
/// Creates a set of disabled optimizations.
pub fn disabled(version: &semver::Version) -> Self { pub fn disabled(version: &semver::Version) -> Self {
let inliner = if version >= &semver::Version::new(0, 8, 5) { let inliner = if version >= &semver::Version::new(0, 8, 5) {
Some(false) Some(false)
@@ -54,6 +72,16 @@ impl Details {
None 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. //! The `solc --standard-json` input settings optimizer.
pub mod details; pub mod details;
pub mod yul_details;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -41,13 +42,8 @@ impl Optimizer {
} }
/// Sets the necessary defaults. /// Sets the necessary defaults.
pub fn normalize(&mut self, version: &semver::Version) { pub fn normalize(&mut self) {
self.mode = None; self.mode = None;
self.fallback_to_optimizing_for_size = 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,
}
}
}