mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Command line tests and minor touch ups
This commit is contained in:
parent
ddf0d784ac
commit
314a1cc92f
@ -323,22 +323,8 @@ on the individual steps and their sequence below.
|
|||||||
Selecting Optimizations
|
Selecting Optimizations
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
By default the optimizer applies its predefined sequence of optimization steps to
|
Detailed information regrading the optimization sequence as well a list of abbreviations is
|
||||||
the generated assembly. You can override this sequence and supply your own using
|
available in the :ref:`Yul optimizer docs <optimization-step-sequence>`.
|
||||||
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>`.
|
|
||||||
|
|
||||||
Preprocessing
|
Preprocessing
|
||||||
-------------
|
-------------
|
||||||
|
27
docs/yul.rst
27
docs/yul.rst
@ -1245,24 +1245,25 @@ In Solidity mode, the Yul optimizer is activated together with the regular optim
|
|||||||
Optimization Step Sequence
|
Optimization Step Sequence
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
|
By default the optimizer applies its predefined sequence of optimization steps to
|
||||||
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
|
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'
|
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOc'
|
||||||
|
|
||||||
The order of steps is significant and affects the quality of the output.
|
The sequence inside ``[...]`` will be applied multiple times in a loop until the Yul code
|
||||||
Moreover, applying a step may uncover new optimization opportunities for others that were already
|
remains unchanged or until the maximum number of rounds (currently 12) has been reached.
|
||||||
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 colon delimiter (``:``) in the example is used to specify a custom cleanup sequence, which is run after
|
An important thing to note, is that there are hardcoded sequences that are run before and after the
|
||||||
the main part of the sequence before the delimiter (and, when using the legacy EVM code transform,
|
user-supplied sequence, or the default sequence if one was not supplied by the user.
|
||||||
only *after* the stack compressor). If no such delimiter is present, the default cleanup sequence (``fDnTOc``)
|
|
||||||
is run after the supplied sequence.
|
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:
|
The following optimization steps are available:
|
||||||
|
@ -1540,6 +1540,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
|
|||||||
details["yulDetails"] = Json::objectValue;
|
details["yulDetails"] = Json::objectValue;
|
||||||
details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation;
|
details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation;
|
||||||
details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps;
|
details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps;
|
||||||
|
details["yulDetails"]["optimizerCleanupSteps"] = m_optimiserSettings.yulOptimiserCleanupSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta["settings"]["optimizer"]["details"] = std::move(details);
|
meta["settings"]["optimizer"]["details"] = std::move(details);
|
||||||
|
@ -148,10 +148,9 @@ struct OptimiserSettings
|
|||||||
/// them just by setting this to an empty string. Set @a runYulOptimiser to false if you want
|
/// them just by setting this to an empty string. Set @a runYulOptimiser to false if you want
|
||||||
/// no optimisations.
|
/// no optimisations.
|
||||||
std::string yulOptimiserSteps = DefaultYulOptimiserSteps;
|
std::string yulOptimiserSteps = DefaultYulOptimiserSteps;
|
||||||
/// Sequence of clean up optimisation steps after the above (default or user supplied) set of
|
/// Sequence of clean-up optimisation steps after yulOptimiserSteps is run. Note that if the string
|
||||||
/// optimisation steps is completed. Note that if the string is left empty, there will still
|
/// is left empty, there will still be hard-coded optimisation steps that will run regardless.
|
||||||
/// be hard-coded optimisation steps that will run regardless. Set @a runYulOptimiser to false
|
/// Set @a runYulOptimiser to false if you want no optimisations.
|
||||||
/// if you want no optimisations.
|
|
||||||
std::string yulOptimiserCleanupSteps = DefaultYulOptimiserCleanupSteps;
|
std::string yulOptimiserCleanupSteps = DefaultYulOptimiserCleanupSteps;
|
||||||
/// This specifies an estimate on how often each opcode in this assembly will be executed,
|
/// 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.
|
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
|
||||||
|
@ -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(":");
|
auto const delimiterPos = fullSequence.find(":");
|
||||||
_optimiserSetting = fullSequence.substr(0, delimiterPos);
|
_optimiserSetting = fullSequence.substr(0, delimiterPos);
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ void OptimiserSuite::run(
|
|||||||
stackCompressorMaxIterations
|
stackCompressorMaxIterations
|
||||||
);
|
);
|
||||||
|
|
||||||
// Run the user supplied (otherwise default) clean up sequence
|
// Run the user-supplied clean up sequence
|
||||||
suite.runSequence(_optimisationCleanupSequence, ast);
|
suite.runSequence(_optimisationCleanupSequence, ast);
|
||||||
suite.runSequence("g", ast);
|
suite.runSequence("g", ast);
|
||||||
|
|
||||||
@ -316,8 +316,9 @@ void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
|
|||||||
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
|
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
|
||||||
break;
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
assertThrow(nestingLevel == 0, OptimizerException, "Cleanup delimiter may only be placed at nesting level zero");
|
++colonDelimiters;
|
||||||
assertThrow(++colonDelimiters <=1, OptimizerException, "Too many colon delimiters");
|
assertThrow(nestingLevel == 0, OptimizerException, "Cleanup sequence delimiter cannot be placed inside the brackets");
|
||||||
|
assertThrow(colonDelimiters <=1, OptimizerException, "Too many cleanup sequence delimiters");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -267,7 +267,7 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
|
|||||||
|
|
||||||
if (optimizer.yulSteps.has_value())
|
if (optimizer.yulSteps.has_value())
|
||||||
{
|
{
|
||||||
std::string const fullSequence = optimizer.yulSteps.value();
|
string const fullSequence = optimizer.yulSteps.value();
|
||||||
auto const delimiterPos = fullSequence.find(":");
|
auto const delimiterPos = fullSequence.find(":");
|
||||||
settings.yulOptimiserSteps = fullSequence.substr(0, delimiterPos);
|
settings.yulOptimiserSteps = fullSequence.substr(0, delimiterPos);
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"component": "general",
|
"component": "general",
|
||||||
"formattedMessage": "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 delimiter may only be placed at nesting level zero",
|
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup sequence delimiter cannot be placed inside the brackets",
|
||||||
"severity": "error",
|
"severity": "error",
|
||||||
"type": "JSONError"
|
"type": "JSONError"
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"component": "general",
|
"component": "general",
|
||||||
"formattedMessage": "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 colon delimiters",
|
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many cleanup sequence delimiters",
|
||||||
"severity": "error",
|
"severity": "error",
|
||||||
"type": "JSONError"
|
"type": "JSONError"
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -6,8 +6,8 @@ contract C {
|
|||||||
constructor() payable {
|
constructor() payable {
|
||||||
assembly ("memory-safe") {
|
assembly ("memory-safe") {
|
||||||
let a := 0
|
let a := 0
|
||||||
revert(0, a)
|
|
||||||
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
|
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
|
||||||
|
revert(0, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ contract C {
|
|||||||
constructor() payable {
|
constructor() payable {
|
||||||
assembly ("memory-safe") {
|
assembly ("memory-safe") {
|
||||||
let a := 0
|
let a := 0
|
||||||
revert(0, a)
|
|
||||||
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
|
// Without the cleanup sequence this will not be simplified to ``revert(a, a)``.
|
||||||
|
revert(0, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1244,10 +1244,11 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
|
|||||||
BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
|
BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
|
||||||
BOOST_CHECK(
|
BOOST_CHECK(
|
||||||
util::convertContainer<set<string>>(optimizer["details"]["yulDetails"].getMemberNames()) ==
|
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"]["stackAllocation"].asBool() == true);
|
||||||
BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps);
|
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_EQUAL(optimizer["details"].getMemberNames().size(), 9);
|
||||||
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
||||||
}
|
}
|
||||||
|
@ -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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
} // namespace solidity::frontend::test
|
} // namespace solidity::frontend::test
|
||||||
|
Loading…
Reference in New Issue
Block a user