Merge pull request #13376 from ethereum/make-hardcoded-parts-of-optimizer-sequence-configurable

This commit is contained in:
Nikola Matić 2022-09-13 18:15:58 +02:00 committed by GitHub
commit ea78c8fd31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 525 additions and 104 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features:
* Yul Optimizer: Allow replacing the previously hard-coded cleanup sequence by specifying custom steps after a colon delimiter (``:``) in the sequence string.
Bugfixes:

View File

@ -281,60 +281,85 @@ The following transformation steps are the main components:
- Redundant Assign Eliminator
- Full Inliner
.. _optimizer-steps:
Optimizer Steps
---------------
This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information
on the individual steps and their sequence below.
- :ref:`block-flattener`.
- :ref:`circular-reference-pruner`.
- :ref:`common-subexpression-eliminator`.
- :ref:`conditional-simplifier`.
- :ref:`conditional-unsimplifier`.
- :ref:`control-flow-simplifier`.
- :ref:`dead-code-eliminator`.
- :ref:`equal-store-eliminator`.
- :ref:`equivalent-function-combiner`.
- :ref:`expression-joiner`.
- :ref:`expression-simplifier`.
- :ref:`expression-splitter`.
- :ref:`for-loop-condition-into-body`.
- :ref:`for-loop-condition-out-of-body`.
- :ref:`for-loop-init-rewriter`.
- :ref:`expression-inliner`.
- :ref:`full-inliner`.
- :ref:`function-grouper`.
- :ref:`function-hoister`.
- :ref:`function-specializer`.
- :ref:`literal-rematerialiser`.
- :ref:`load-resolver`.
- :ref:`loop-invariant-code-motion`.
- :ref:`redundant-assign-eliminator`.
- :ref:`reasoning-based-simplifier`.
- :ref:`rematerialiser`.
- :ref:`SSA-reverser`.
- :ref:`SSA-transform`.
- :ref:`structural-simplifier`.
- :ref:`unused-function-parameter-pruner`.
- :ref:`unused-pruner`.
- :ref:`var-decl-initializer`.
============ ===============================
Abbreviation Full name
============ ===============================
``f`` :ref:`block-flattener`
``l`` :ref:`circular-reference-pruner`
``c`` :ref:`common-subexpression-eliminator`
``C`` :ref:`conditional-simplifier`
``U`` :ref:`conditional-unsimplifier`
``n`` :ref:`control-flow-simplifier`
``D`` :ref:`dead-code-eliminator`
``E`` :ref:`equal-store-eliminator`
``v`` :ref:`equivalent-function-combiner`
``e`` :ref:`expression-inliner`
``j`` :ref:`expression-joiner`
``s`` :ref:`expression-simplifier`
``x`` :ref:`expression-splitter`
``I`` :ref:`for-loop-condition-into-body`
``O`` :ref:`for-loop-condition-out-of-body`
``o`` :ref:`for-loop-init-rewriter`
``i`` :ref:`full-inliner`
``g`` :ref:`function-grouper`
``h`` :ref:`function-hoister`
``F`` :ref:`function-specializer`
``T`` :ref:`literal-rematerialiser`
``L`` :ref:`load-resolver`
``M`` :ref:`loop-invariant-code-motion`
``r`` :ref:`redundant-assign-eliminator`
``R`` :ref:`reasoning-based-simplifier` - highly experimental
``m`` :ref:`rematerialiser`
``V`` :ref:`SSA-reverser`
``a`` :ref:`SSA-transform`
``t`` :ref:`structural-simplifier`
``p`` :ref:`unused-function-parameter-pruner`
``u`` :ref:`unused-pruner`
``d`` :ref:`var-decl-initializer`
============ ===============================
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
The ReasoningBasedSimplifier is an optimizer step that is currently not enabled
in the default set of steps. It uses an SMT solver to simplify arithmetic expressions
and boolean conditions. It has not received thorough testing or validation yet and can produce
non-reproducible results, so please use with care!
Selecting Optimizations
-----------------------
By default the optimizer applies its predefined sequence of optimization steps to
the generated assembly. You can override this sequence and supply your own using
the ``--yul-optimizations`` option:
By default the optimizer applies its predefined sequence of optimization steps to the generated assembly.
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
.. code-block:: bash
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOc'
The order of steps is significant and affects the quality of the output.
Moreover, applying a step may uncover new optimization opportunities for others that were already applied,
so repeating steps is often beneficial.
The sequence inside ``[...]`` will be applied multiple times in a loop until the Yul code
remains unchanged or until the maximum number of rounds (currently 12) has been reached.
Brackets (``[]``) may be used multiple times in a sequence, but can not be nested.
Available abbreviations are listed in the :ref:`Yul optimizer docs <optimization-step-sequence>`.
An important thing to note, is that there are some hardcoded steps that are always run before and after the
user-supplied sequence, or the default sequence if one was not supplied by the user.
The cleanup sequence delimiter ``:`` is optional, and is used to supply a custom cleanup sequence
in order to replace the default one. If omitted, the optimizer will simply apply the default cleanup
sequence. In addition, the delimiter may be placed at the beginning of the user-supplied sequence,
which will result in the optimization sequence being empty, whereas conversely, if placed at the end of
the sequence, will be treated as an empty cleanup sequence.
Preprocessing
-------------

View File

@ -1245,65 +1245,8 @@ In Solidity mode, the Yul optimizer is activated together with the regular optim
Optimization Step Sequence
--------------------------
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
.. code-block:: sh
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
The order of steps is significant and affects the quality of the output.
Moreover, applying a step may uncover new optimization opportunities for others that were already
applied so repeating steps is often beneficial.
By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly
apply that part until it no longer improves the size of the resulting assembly.
You can use brackets multiple times in a single sequence but they cannot be nested.
The following optimization steps are available:
============ ===============================
Abbreviation Full name
============ ===============================
``f`` ``BlockFlattener``
``l`` ``CircularReferencesPruner``
``c`` ``CommonSubexpressionEliminator``
``C`` ``ConditionalSimplifier``
``U`` ``ConditionalUnsimplifier``
``n`` ``ControlFlowSimplifier``
``D`` ``DeadCodeEliminator``
``v`` ``EquivalentFunctionCombiner``
``e`` ``ExpressionInliner``
``j`` ``ExpressionJoiner``
``s`` ``ExpressionSimplifier``
``x`` ``ExpressionSplitter``
``I`` ``ForLoopConditionIntoBody``
``O`` ``ForLoopConditionOutOfBody``
``o`` ``ForLoopInitRewriter``
``i`` ``FullInliner``
``g`` ``FunctionGrouper``
``h`` ``FunctionHoister``
``F`` ``FunctionSpecializer``
``T`` ``LiteralRematerialiser``
``L`` ``LoadResolver``
``M`` ``LoopInvariantCodeMotion``
``r`` ``RedundantAssignEliminator``
``R`` ``ReasoningBasedSimplifier`` - highly experimental
``m`` ``Rematerialiser``
``V`` ``SSAReverser``
``a`` ``SSATransform``
``t`` ``StructuralSimplifier``
``u`` ``UnusedPruner``
``p`` ``UnusedFunctionParameterPruner``
``d`` ``VarDeclInitializer``
============ ===============================
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
The ReasoningBasedSimplifier is an optimizer step that is currently not enabled
in the default set of steps. It uses an SMT solver to simplify arithmetic expressions
and boolean conditions. It has not received thorough testing or validation yet and can produce
non-reproducible results, so please use with care!
Detailed information regrading the optimization sequence as well a list of abbreviations is
available in the :ref:`optimizer docs <optimizer-steps>`.
.. _erc20yul:

View File

@ -547,6 +547,7 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
_object,
_optimiserSettings.optimizeStackAllocation,
_optimiserSettings.yulOptimiserSteps,
_optimiserSettings.yulOptimiserCleanupSteps,
isCreation? nullopt : make_optional(_optimiserSettings.expectedExecutionsPerDeployment),
_externalIdentifiers
);

View File

@ -1539,7 +1539,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
{
details["yulDetails"] = Json::objectValue;
details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation;
details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps;
details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps;
}
meta["settings"]["optimizer"]["details"] = std::move(details);

View File

@ -59,6 +59,8 @@ struct OptimiserSettings
"]"
"jmul[jul] VcTOcul jmul"; // Make source short and pretty
static char constexpr DefaultYulOptimiserCleanupSteps[] = "fDnTOc";
/// No optimisations at all - not recommended.
static OptimiserSettings none()
{
@ -146,6 +148,10 @@ struct OptimiserSettings
/// them just by setting this to an empty string. Set @a runYulOptimiser to false if you want
/// no optimisations.
std::string yulOptimiserSteps = DefaultYulOptimiserSteps;
/// Sequence of clean-up optimisation steps after yulOptimiserSteps is run. Note that if the string
/// is left empty, there will still be hard-coded optimisation steps that will run regardless.
/// Set @a runYulOptimiser to false if you want no optimisations.
std::string yulOptimiserCleanupSteps = DefaultYulOptimiserCleanupSteps;
/// This specifies an estimate on how often each opcode in this assembly will be executed,
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
size_t expectedExecutionsPerDeployment = 200;

View File

@ -472,7 +472,7 @@ std::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std
return {};
}
std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _setting)
std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _optimiserSetting, string& _cleanupSetting)
{
if (_details.isMember(_name))
{
@ -490,7 +490,14 @@ std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details
);
}
_setting = _details[_name].asString();
string const fullSequence = _details[_name].asString();
auto const delimiterPos = fullSequence.find(":");
_optimiserSetting = fullSequence.substr(0, delimiterPos);
if (delimiterPos != string::npos)
_cleanupSetting = fullSequence.substr(delimiterPos + 1);
else
solAssert(_cleanupSetting == OptimiserSettings::DefaultYulOptimiserCleanupSteps);
}
else
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be a string");
@ -616,7 +623,7 @@ std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value
return *result;
if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation))
return *error;
if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps))
if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps))
return *error;
}
}

View File

@ -209,6 +209,7 @@ void YulStack::optimize(Object& _object, bool _isCreation)
_object,
m_optimiserSettings.optimizeStackAllocation,
m_optimiserSettings.yulOptimiserSteps,
m_optimiserSettings.yulOptimiserCleanupSteps,
_isCreation ? nullopt : make_optional(m_optimiserSettings.expectedExecutionsPerDeployment),
{}
);

View File

@ -93,6 +93,7 @@ void OptimiserSuite::run(
Object& _object,
bool _optimizeStackAllocation,
string_view _optimisationSequence,
string_view _optimisationCleanupSequence,
optional<size_t> _expectedExecutionsPerDeployment,
set<YulString> const& _externallyUsedIdentifiers
)
@ -139,7 +140,13 @@ void OptimiserSuite::run(
_optimizeStackAllocation,
stackCompressorMaxIterations
);
suite.runSequence("fDnTOc g", ast);
// Run the user-supplied clean up sequence
suite.runSequence(_optimisationCleanupSequence, ast);
// Hard-coded FunctionGrouper step is used to bring the AST into a canonical form required by the StackCompressor
// and StackLimitEvader. This is hard-coded as the last step, as some previously executed steps may break the
// aforementioned form, thus causing the StackCompressor/StackLimitEvader to throw.
suite.runSequence("g", ast);
if (evmDialect)
{
@ -296,6 +303,7 @@ map<char, string> const& OptimiserSuite::stepAbbreviationToNameMap()
void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
{
int8_t nestingLevel = 0;
int8_t colonDelimiters = 0;
for (char abbreviation: _stepAbbreviations)
switch (abbreviation)
{
@ -310,6 +318,11 @@ void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
nestingLevel--;
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
break;
case ':':
++colonDelimiters;
assertThrow(nestingLevel == 0, OptimizerException, "Cleanup sequence delimiter cannot be placed inside the brackets");
assertThrow(colonDelimiters <= 1, OptimizerException, "Too many cleanup sequence delimiters");
break;
default:
{
yulAssert(

View File

@ -51,7 +51,7 @@ public:
/// Special characters that do not represent optimiser steps but are allowed in abbreviation sequences.
/// Some of them (like whitespace) are ignored, others (like brackets) are a part of the syntax.
static constexpr char NonStepAbbreviations[] = " \n[]";
static constexpr char NonStepAbbreviations[] = " \n[]:";
enum class Debug
{
@ -68,6 +68,7 @@ public:
Object& _object,
bool _optimizeStackAllocation,
std::string_view _optimisationSequence,
std::string_view _optimisationCleanupSequence,
std::optional<size_t> _expectedExecutionsPerDeployment,
std::set<YulString> const& _externallyUsedIdentifiers = {}
);

View File

@ -266,7 +266,16 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
settings.expectedExecutionsPerDeployment = optimizer.expectedExecutionsPerDeployment.value();
if (optimizer.yulSteps.has_value())
settings.yulOptimiserSteps = optimizer.yulSteps.value();
{
string const fullSequence = optimizer.yulSteps.value();
auto const delimiterPos = fullSequence.find(":");
settings.yulOptimiserSteps = fullSequence.substr(0, delimiterPos);
if (delimiterPos != string::npos)
settings.yulOptimiserCleanupSteps = fullSequence.substr(delimiterPos + 1);
else
solAssert(settings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps);
}
return settings;
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_invalid_nested_delimiter/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": "dhfoDgvulfnTUtnIf\n[ xar:rscLM\n]\njmuljuljul VcTOcul jmul"
}
}
}
}
}

View File

@ -0,0 +1,12 @@
{
"errors":
[
{
"component": "general",
"formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup sequence delimiter cannot be placed inside the brackets",
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup sequence delimiter cannot be placed inside the brackets",
"severity": "error",
"type": "JSONError"
}
]
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_mutliple_delimiters/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": "dhfoDgvu:lfnTUtnIf\n[ xarrscLM\n]\njmuljulj:ul VcTOcul jmul"
}
}
}
}
}

View File

@ -0,0 +1,12 @@
{
"errors":
[
{
"component": "general",
"formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many cleanup sequence delimiters",
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many cleanup sequence delimiters",
"severity": "error",
"type": "JSONError"
}
]
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": "dhfoDgvulfnTUtnIf\n[ xarrscLM\n]\njmuljuljul VcTOcul jmul:fDnTOc"
}
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": "dhfoDgvulfnTUtnIf\n[ xarrscLM\n]\njmuljuljul VcTOcul jmul:"
}
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": ":dhfoDgvulfnTUtnIf\n[ xarrscLM\n]\njmuljuljul VcTOcul jmul"
}
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {
function f() public pure {}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]}
},
"settings": {
"optimizer": {
"details": {
"yul": true,
"yulDetails": {
"optimizerSteps": ":"
}
}
}
}
}

View File

@ -0,0 +1,9 @@
{
"sources":
{
"A":
{
"id": 0
}
}
}

View File

@ -0,0 +1 @@
--ir-optimized --optimize --yul-optimizations iDu

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma abicoder v2;
contract C {
constructor() payable {
assembly ("memory-safe") {
let a := 0
revert(0, a)
}
}
}

View File

@ -0,0 +1,24 @@
Optimized IR:
/// @use-src 0:"yul_optimizer_steps_short_sequence/input.sol"
object "C_8" {
code {
{
/// @src 0:80:221 "contract C {..."
mstore(64, memoryguard(0x80))
/// @src 0:129:213 "assembly (\"memory-safe\") {..."
let usr$a := 0
revert(usr$a, usr$a)
}
}
/// @use-src 0:"yul_optimizer_steps_short_sequence/input.sol"
object "C_8_deployed" {
code {
{
/// @src 0:80:221 "contract C {..."
mstore(64, memoryguard(0x80))
revert(0, 0)
}
}
data ".metadata" hex"<BYTECODE REMOVED>"
}
}

View File

@ -0,0 +1 @@
--ir-optimized --optimize --yul-optimizations iDu:

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma abicoder v2;
contract C {
constructor() payable {
assembly ("memory-safe") {
let a := 0
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
revert(0, a)
}
}
}

View File

@ -0,0 +1,24 @@
Optimized IR:
/// @use-src 0:"yul_optimizer_steps_with_empty_cleanup_sequence/input.sol"
object "C_8" {
code {
{
/// @src 0:80:314 "contract C {..."
mstore(64, memoryguard(0x80))
/// @src 0:129:306 "assembly (\"memory-safe\") {..."
let usr$a := 0
revert(0, usr$a)
}
}
/// @use-src 0:"yul_optimizer_steps_with_empty_cleanup_sequence/input.sol"
object "C_8_deployed" {
code {
{
/// @src 0:80:314 "contract C {..."
mstore(64, memoryguard(0x80))
revert(0, 0)
}
}
data ".metadata" hex"<BYTECODE REMOVED>"
}
}

View File

@ -0,0 +1 @@
--ir-optimized --optimize --yul-optimizations :iDu

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma abicoder v2;
contract C {
constructor() payable {
assembly ("memory-safe") {
let a := 0
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
revert(0, a)
}
}
}

View File

@ -0,0 +1,24 @@
Optimized IR:
/// @use-src 0:"yul_optimizer_steps_with_empty_optimization_sequence/input.sol"
object "C_8" {
code {
{
/// @src 0:80:314 "contract C {..."
mstore(64, memoryguard(0x80))
/// @src 0:129:306 "assembly (\"memory-safe\") {..."
let usr$a := 0
revert(0, usr$a)
}
}
/// @use-src 0:"yul_optimizer_steps_with_empty_optimization_sequence/input.sol"
object "C_8_deployed" {
code {
{
/// @src 0:80:314 "contract C {..."
mstore(64, memoryguard(0x80))
revert(0, 0)
}
}
data ".metadata" hex"<BYTECODE REMOVED>"
}
}

View File

@ -400,6 +400,53 @@ BOOST_AUTO_TEST_CASE(metadata_revert_strings)
BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip");
}
BOOST_AUTO_TEST_CASE(metadata_optimiser_sequence)
{
char const* sourceCode = R"(
pragma solidity >=0.0;
contract C {
}
)";
vector<tuple<string, string>> sequences =
{
// {"<optimizer sequence>", "<optimizer cleanup sequence>"}
{"", ""},
{"", "fDn"},
{"dhfoDgvulfnTUtnIf", "" },
{"dhfoDgvulfnTUtnIf", "fDn"}
};
auto check = [sourceCode](string const& _optimizerSequence, string const& _optimizerCleanupSequence)
{
OptimiserSettings optimizerSettings = OptimiserSettings::minimal();
optimizerSettings.runYulOptimiser = true;
optimizerSettings.yulOptimiserSteps = _optimizerSequence;
optimizerSettings.yulOptimiserCleanupSteps = _optimizerCleanupSequence;
CompilerStack compilerStack;
compilerStack.setSources({{"", std::string(sourceCode)}});
compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
compilerStack.setOptimiserSettings(optimizerSettings);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
std::string const& serialisedMetadata = compilerStack.metadata("C");
Json::Value metadata;
BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata));
BOOST_CHECK(solidity::test::isValidMetadata(metadata));
BOOST_CHECK(metadata["settings"]["optimizer"].isMember("details"));
BOOST_CHECK(metadata["settings"]["optimizer"]["details"].isMember("yulDetails"));
BOOST_CHECK(metadata["settings"]["optimizer"]["details"]["yulDetails"].isMember("optimizerSteps"));
string const metadataOptimizerSteps = metadata["settings"]["optimizer"]["details"]["yulDetails"]["optimizerSteps"].asString();
string const expectedMetadataOptimiserSteps = _optimizerSequence + ":" + _optimizerCleanupSequence;
BOOST_CHECK_EQUAL(metadataOptimizerSteps, expectedMetadataOptimiserSteps);
};
for (auto const& [sequence, cleanupSequence] : sequences)
check(sequence, cleanupSequence);
}
BOOST_AUTO_TEST_CASE(metadata_license_missing)
{
char const* sourceCode = R"(

View File

@ -1247,7 +1247,10 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
(set<string>{"stackAllocation", "optimizerSteps"})
);
BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps);
BOOST_CHECK(
optimizer["details"]["yulDetails"]["optimizerSteps"].asString() ==
OptimiserSettings::DefaultYulOptimiserSteps + ":"s + OptimiserSettings::DefaultYulOptimiserCleanupSteps
);
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 9);
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
}

View File

@ -339,6 +339,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
*m_object,
true,
frontend::OptimiserSettings::DefaultYulOptimiserSteps,
frontend::OptimiserSettings::DefaultYulOptimiserCleanupSteps,
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
);
}},

View File

@ -426,6 +426,58 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations)
}
}
BOOST_AUTO_TEST_CASE(default_optimiser_sequence)
{
CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--optimize"});
BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, OptimiserSettings::DefaultYulOptimiserSteps);
BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, OptimiserSettings::DefaultYulOptimiserCleanupSteps);
}
BOOST_AUTO_TEST_CASE(valid_optimiser_sequences)
{
vector<string> validSequenceInputs {
":", // Empty optimization sequence and empty cleanup sequence
":fDn", // Empty optimization sequence and specified cleanup sequence
"dhfoDgvulfnTUtnIf:", // Specified optimization sequence and empty cleanup sequence
"dhfoDgvulfnTUtnIf:fDn", // Specified optimization sequence and cleanup sequence
"dhfo[Dgvulfn]TUtnIf:f[D]n" // Specified and nested optimization and cleanup sequence
};
vector<tuple<string, string>> expectedParsedSequences {
{"", ""},
{"", "fDn"},
{"dhfoDgvulfnTUtnIf", ""},
{"dhfoDgvulfnTUtnIf", "fDn"},
{"dhfo[Dgvulfn]TUtnIf", "f[D]n"}
};
BOOST_CHECK_EQUAL(validSequenceInputs.size(), expectedParsedSequences.size());
for (size_t i = 0; i < validSequenceInputs.size(); ++i)
{
CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--optimize", "--yul-optimizations=" + validSequenceInputs[i]});
auto const& [expectedYulOptimiserSteps, expectedYulCleanupSteps] = expectedParsedSequences[i];
BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, expectedYulOptimiserSteps);
BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, expectedYulCleanupSteps);
}
}
BOOST_AUTO_TEST_CASE(invalid_nested_cleanup_sequence_delimiter)
{
vector<string> commandLine {"solc", "contract.sol", "--optimize", "--yul-optimizations=dhfoDgvulfnTUt[nIf:fd]N"};
string expectedMessage = "Invalid optimizer step sequence in --yul-optimizations: Cleanup sequence delimiter cannot be placed inside the brackets";
auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; };
BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage);
}
BOOST_AUTO_TEST_CASE(too_many_cleanup_sequence_delimiters)
{
vector<string> commandLine {"solc", "contract.sol", "--optimize", "--yul-optimizations=dhfoDgvulfnTU:tnIf:fdN"};
string expectedMessage = "Invalid optimizer step sequence in --yul-optimizations: Too many cleanup sequence delimiters";
auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; };
BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace solidity::frontend::test