Skip to content

Commit b4c395b

Browse files
authored
Merge pull request #15162 from ethereum/do-not-optimize-without-optimized-outputs
Do not optimize IR without optimized outputs
2 parents 35231a1 + 1a60d18 commit b4c395b

File tree

18 files changed

+387
-34
lines changed

18 files changed

+387
-34
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ Language Features:
77

88

99
Compiler Features:
10+
* Command Line Interface: Do not perform IR optimization when only unoptimized IR is requested.
1011
* Error Reporting: Unimplemented features are now properly reported as errors instead of being handled as if they were bugs.
1112
* EVM: Support for the EVM version "Prague".
1213
* SMTChecker: Add CHC engine check for underflow and overflow in unary minus operation.
1314
* SMTChecker: Replace CVC4 as a possible BMC backend with cvc5.
15+
* Standard JSON Interface: Do not perform IR optimization when only unoptimized IR is requested.
1416
* Yul Optimizer: The optimizer now treats some previously unrecognized identical literals as identical.
1517

1618

libsolidity/interface/CompilerStack.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ void CompilerStack::reset(bool _keepSettings)
313313
m_viaIR = false;
314314
m_evmVersion = langutil::EVMVersion();
315315
m_modelCheckerSettings = ModelCheckerSettings{};
316-
m_generateIR = false;
316+
m_irOutputSelection = IROutputSelection::None;
317317
m_revertStrings = RevertStrings::Default;
318318
m_optimiserSettings = OptimiserSettings::minimal();
319319
m_metadataLiteralSources = false;
@@ -728,8 +728,17 @@ bool CompilerStack::compile(State _stopAfter)
728728
{
729729
try
730730
{
731-
if ((m_generateEvmBytecode && m_viaIR) || m_generateIR)
732-
generateIR(*contract);
731+
// NOTE: Bytecode generation via IR always uses Contract::yulIROptimized.
732+
// When optimization is not enabled, that member simply contains unoptimized code.
733+
bool needIROutput =
734+
(m_generateEvmBytecode && m_viaIR) ||
735+
m_irOutputSelection != IROutputSelection::None;
736+
bool needUnoptimizedIROutputOnly =
737+
!(m_generateEvmBytecode && m_viaIR) &&
738+
m_irOutputSelection != IROutputSelection::UnoptimizedAndOptimized;
739+
740+
if (needIROutput)
741+
generateIR(*contract, needUnoptimizedIROutputOnly);
733742
if (m_generateEvmBytecode)
734743
{
735744
if (m_viaIR)
@@ -1402,7 +1411,7 @@ void CompilerStack::compileContract(
14021411
assembleYul(_contract, compiler->assemblyPtr(), compiler->runtimeAssemblyPtr());
14031412
}
14041413

1405-
void CompilerStack::generateIR(ContractDefinition const& _contract)
1414+
void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unoptimizedOnly)
14061415
{
14071416
solAssert(m_stackState >= AnalysisSuccessful, "");
14081417

@@ -1420,7 +1429,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
14201429

14211430
std::string dependenciesSource;
14221431
for (auto const& [dependency, referencee]: _contract.annotation().contractDependencies)
1423-
generateIR(*dependency);
1432+
generateIR(*dependency, _unoptimizedOnly);
14241433

14251434
if (!_contract.canBeDeployed())
14261435
return;
@@ -1485,9 +1494,14 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
14851494
{
14861495
YulStack stack = parseYul(compiledContract.yulIR);
14871496
compiledContract.yulIRAst = stack.astJson();
1488-
stack.optimize();
1489-
compiledContract.yulIROptimized = stack.print(this);
1497+
if (!_unoptimizedOnly)
1498+
{
1499+
stack.optimize();
1500+
compiledContract.yulIROptimized = stack.print(this);
1501+
}
14901502
}
1503+
1504+
if (!_unoptimizedOnly)
14911505
{
14921506
// Optimizer does not maintain correct native source locations in the AST.
14931507
// We can work around it by regenerating the AST from scratch from optimized IR.

libsolidity/interface/CompilerStack.h

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
126126
SolidityAST,
127127
};
128128

129+
enum class IROutputSelection {
130+
None,
131+
UnoptimizedOnly,
132+
UnoptimizedAndOptimized,
133+
};
134+
129135
/// Creates a new compiler stack.
130136
/// @param _readFile callback used to read files for import statements. Must return
131137
/// and must not emit exceptions.
@@ -192,8 +198,14 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
192198
/// Enable EVM Bytecode generation. This is enabled by default.
193199
void enableEvmBytecodeGeneration(bool _enable = true) { m_generateEvmBytecode = _enable; }
194200

195-
/// Enable generation of Yul IR code.
196-
void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; }
201+
/// Enable generation of Yul IR code so that IR output can be safely requested for all contracts.
202+
/// Note that IR may also be implicitly generated when not requested. In particular
203+
/// @a setViaIR(true) requires access to the IR outputs for bytecode generation.
204+
void requestIROutputs(IROutputSelection _selection = IROutputSelection::UnoptimizedAndOptimized)
205+
{
206+
solAssert(m_stackState < ParsedAndImported);
207+
m_irOutputSelection = _selection;
208+
}
197209

198210
/// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata.
199211
/// Must be set before parsing.
@@ -387,8 +399,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
387399
std::shared_ptr<evmasm::Assembly> evmRuntimeAssembly;
388400
evmasm::LinkerObject object; ///< Deployment object (includes the runtime sub-object).
389401
evmasm::LinkerObject runtimeObject; ///< Runtime object.
390-
std::string yulIR; ///< Yul IR code.
391-
std::string yulIROptimized; ///< Optimized Yul IR code.
402+
std::string yulIR; ///< Yul IR code straight from the code generator.
403+
std::string yulIROptimized; ///< Reparsed and possibly optimized Yul IR code.
392404
Json yulIRAst; ///< JSON AST of Yul IR code.
393405
Json yulIROptimizedAst; ///< JSON AST of optimized Yul IR code.
394406
util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
@@ -449,8 +461,14 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
449461
);
450462

451463
/// Generate Yul IR for a single contract.
452-
/// The IR is stored but otherwise unused.
453-
void generateIR(ContractDefinition const& _contract);
464+
/// Unoptimized IR is stored but otherwise unused, while optimized IR may be used for code
465+
/// generation if compilation via IR is enabled. Note that whether "optimized IR" is actually
466+
/// optimized depends on the optimizer settings.
467+
/// @param _contract Contract to generate IR for.
468+
/// @param _unoptimizedOnly If true, only the IR coming directly from the codegen is stored.
469+
/// Optimizer is not invoked and optimized IR output is not available, which means that
470+
/// optimized IR, its AST or compilation via IR must not be requested.
471+
void generateIR(ContractDefinition const& _contract, bool _unoptimizedOnly);
454472

455473
/// Generate EVM representation for a single contract.
456474
/// Depends on output generated by generateIR.
@@ -515,7 +533,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
515533
ModelCheckerSettings m_modelCheckerSettings;
516534
std::map<std::string, std::set<std::string>> m_requestedContractNames;
517535
bool m_generateEvmBytecode = true;
518-
bool m_generateIR = false;
536+
IROutputSelection m_irOutputSelection = IROutputSelection::None;
519537
std::map<std::string, util::h160> m_libraries;
520538
ImportRemapper m_importRemapper;
521539
std::map<std::string const, Source> m_sources;

libsolidity/interface/StandardCompiler.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -295,25 +295,26 @@ bool isEvmBytecodeRequested(Json const& _outputSelection)
295295
return false;
296296
}
297297

298-
/// @returns true if any Yul IR was requested. Note that as an exception, '*' does not
299-
/// yet match "ir", "irAst", "irOptimized" or "irOptimizedAst"
300-
bool isIRRequested(Json const& _outputSelection)
298+
/// @returns The IR output selection for CompilerStack, based on outputs requested in the JSON.
299+
/// Note that as an exception, '*' does not yet match "ir", "irAst", "irOptimized" or "irOptimizedAst".
300+
CompilerStack::IROutputSelection irOutputSelection(Json const& _outputSelection)
301301
{
302302
if (!_outputSelection.is_object())
303-
return false;
303+
return CompilerStack::IROutputSelection::None;
304304

305+
CompilerStack::IROutputSelection selection = CompilerStack::IROutputSelection::None;
305306
for (auto const& fileRequests: _outputSelection)
306307
for (auto const& requests: fileRequests)
307308
for (auto const& request: requests)
308-
if (
309-
request == "ir" ||
310-
request == "irAst" ||
311-
request == "irOptimized" ||
312-
request == "irOptimizedAst"
313-
)
314-
return true;
309+
{
310+
if (request == "irOptimized" || request == "irOptimizedAst")
311+
return CompilerStack::IROutputSelection::UnoptimizedAndOptimized;
315312

316-
return false;
313+
if (request == "ir" || request == "irAst")
314+
selection = CompilerStack::IROutputSelection::UnoptimizedOnly;
315+
}
316+
317+
return selection;
317318
}
318319

319320
Json formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
@@ -1320,7 +1321,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
13201321
compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings);
13211322

13221323
compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
1323-
compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection));
1324+
compilerStack.requestIROutputs(irOutputSelection(_inputsAndSettings.outputSelection));
13241325

13251326
Json errors = std::move(_inputsAndSettings.errors);
13261327

solc/CommandLineInterface.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -849,13 +849,14 @@ void CommandLineInterface::compile()
849849
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
850850
if (m_options.output.debugInfoSelection.has_value())
851851
m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value());
852-
// TODO: Perhaps we should not compile unless requested
853-
m_compiler->enableIRGeneration(
854-
m_options.compiler.outputs.ir ||
855-
m_options.compiler.outputs.irOptimized ||
856-
m_options.compiler.outputs.irAstJson ||
857-
m_options.compiler.outputs.irOptimizedAstJson
858-
);
852+
853+
CompilerStack::IROutputSelection irOutputSelection = CompilerStack::IROutputSelection::None;
854+
if (m_options.compiler.outputs.irOptimized || m_options.compiler.outputs.irOptimizedAstJson)
855+
irOutputSelection = CompilerStack::IROutputSelection::UnoptimizedAndOptimized;
856+
else if (m_options.compiler.outputs.ir || m_options.compiler.outputs.irAstJson)
857+
irOutputSelection = CompilerStack::IROutputSelection::UnoptimizedOnly;
858+
859+
m_compiler->requestIROutputs(irOutputSelection);
859860
m_compiler->enableEvmBytecodeGeneration(
860861
m_options.compiler.estimateGas ||
861862
m_options.compiler.outputs.asm_ ||
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--ir-optimized --optimize --debug-info none
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity *;
3+
4+
contract C {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Optimized IR:
2+
/// @use-src 0:"ir_optimized_with_optimize/input.sol"
3+
object "C_2" {
4+
code {
5+
{
6+
let _1 := memoryguard(0x80)
7+
mstore(64, _1)
8+
if callvalue() { revert(0, 0) }
9+
let _2 := datasize("C_2_deployed")
10+
codecopy(_1, dataoffset("C_2_deployed"), _2)
11+
return(_1, _2)
12+
}
13+
}
14+
/// @use-src 0:"ir_optimized_with_optimize/input.sol"
15+
object "C_2_deployed" {
16+
code { { revert(0, 0) } }
17+
data ".metadata" hex"<BYTECODE REMOVED>"
18+
}
19+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--ir --optimize --debug-info none
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity *;
3+
4+
contract C {}

0 commit comments

Comments
 (0)