Command line tests and minor touch ups

This commit is contained in:
Nikola Matic 2022-08-23 11:58:12 +02:00
parent ddf0d784ac
commit 314a1cc92f
15 changed files with 88 additions and 47 deletions

View File

@ -323,22 +323,8 @@ on the individual steps and their sequence below.
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:
.. code-block:: bash
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOc'
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.
The colon delimiter ``:`` is used to supply a custom clean up sequence in order to replace the
default (``fDnTOc``) one, which is run after the stack compressor when using the legacy EVM
code transform.
Available abbreviations are listed in the :ref:`Yul optimizer docs <optimization-step-sequence>`.
Detailed information regrading the optimization sequence as well a list of abbreviations is
available in the :ref:`Yul optimizer docs <optimization-step-sequence>`.
Preprocessing
-------------

View File

@ -1245,24 +1245,25 @@ 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:
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:: sh
.. code-block:: bash
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.
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 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.
The colon delimiter (``:``) in the example is used to specify a custom cleanup sequence, which is run after
the main part of the sequence before the delimiter (and, when using the legacy EVM code transform,
only *after* the stack compressor). If no such delimiter is present, the default cleanup sequence (``fDnTOc``)
is run after the supplied sequence.
An important thing to note, is that there are hardcoded sequences that are 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 be treated as an empty optimization sequence, whereas conversely, if placed at the end of
the sequence, will be treated as an empty cleanup sequence.
The following optimization steps are available:

View File

@ -1540,6 +1540,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"]["optimizerCleanupSteps"] = m_optimiserSettings.yulOptimiserCleanupSteps;
}
meta["settings"]["optimizer"]["details"] = std::move(details);

View File

@ -148,10 +148,9 @@ 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 the above (default or user supplied) set of
/// optimisation steps is completed. 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.
/// 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.

View File

@ -490,7 +490,7 @@ std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details
);
}
std::string const fullSequence = _details[_name].asString();
string const fullSequence = _details[_name].asString();
auto const delimiterPos = fullSequence.find(":");
_optimiserSetting = fullSequence.substr(0, delimiterPos);

View File

@ -141,7 +141,7 @@ void OptimiserSuite::run(
stackCompressorMaxIterations
);
// Run the user supplied (otherwise default) clean up sequence
// Run the user-supplied clean up sequence
suite.runSequence(_optimisationCleanupSequence, ast);
suite.runSequence("g", ast);
@ -316,8 +316,9 @@ void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
break;
case ':':
assertThrow(nestingLevel == 0, OptimizerException, "Cleanup delimiter may only be placed at nesting level zero");
assertThrow(++colonDelimiters <=1, OptimizerException, "Too many colon delimiters");
++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:
{

View File

@ -267,7 +267,7 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
if (optimizer.yulSteps.has_value())
{
std::string const fullSequence = optimizer.yulSteps.value();
string const fullSequence = optimizer.yulSteps.value();
auto const delimiterPos = fullSequence.find(":");
settings.yulOptimiserSteps = fullSequence.substr(0, delimiterPos);

View File

@ -3,8 +3,8 @@
[
{
"component": "general",
"formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup delimiter may only be placed at nesting level zero",
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup delimiter may only be placed at nesting level zero",
"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

@ -3,8 +3,8 @@
[
{
"component": "general",
"formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many colon delimiters",
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many colon delimiters",
"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

@ -1 +1 @@
Invalid optimizer step sequence in --yul-optimizations: Cleanup delimiter may only be placed at nesting level zero
Invalid optimizer step sequence in --yul-optimizations: Cleanup sequence delimiter cannot be placed inside the brackets

View File

@ -1 +1 @@
Invalid optimizer step sequence in --yul-optimizations: Too many colon delimiters
Invalid optimizer step sequence in --yul-optimizations: Too many cleanup sequence delimiters

View File

@ -6,8 +6,8 @@ contract C {
constructor() payable {
assembly ("memory-safe") {
let a := 0
revert(0, a)
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
revert(0, a)
}
}
}

View File

@ -6,8 +6,8 @@ contract C {
constructor() payable {
assembly ("memory-safe") {
let a := 0
revert(0, a)
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
revert(0, a)
}
}
}

View File

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

View File

@ -426,6 +426,58 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations)
}
}
BOOST_AUTO_TEST_CASE(default_optimiser_sequence)
{
auto 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 optimizaiton sequence and empty cleanup sequence
":fDn", // Empty optimization sequence and specified cleanup sequence
"dhfoDgvulfnTUtnIf:", // Specified optimizaiton 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)
{
auto 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