Make hardcoded parts of the optimizer sequence configurable

This commit is contained in:
Nikola Matic 2022-08-10 15:57:01 +02:00
parent f808855329
commit f6f0d6a360
55 changed files with 461 additions and 6 deletions

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

@ -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,11 @@ 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.
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,12 @@ std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details
);
}
_setting = _details[_name].asString();
std::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
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be a string");
@ -616,7 +621,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,10 @@ void OptimiserSuite::run(
_optimizeStackAllocation,
stackCompressorMaxIterations
);
suite.runSequence("fDnTOc g", ast);
// Run the user supplied (otherwise default) clean up sequence
suite.runSequence(_optimisationCleanupSequence, ast);
suite.runSequence("g", ast);
if (evmDialect)
{
@ -296,6 +300,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 +315,10 @@ void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
nestingLevel--;
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");
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,14 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
settings.expectedExecutionsPerDeployment = optimizer.expectedExecutionsPerDeployment.value();
if (optimizer.yulSteps.has_value())
settings.yulOptimiserSteps = optimizer.yulSteps.value();
{
std::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);
}
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 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",
"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 colon delimiters",
"message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many colon 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 dhfo[Dg:vu]lfnTUtnIf

View File

@ -0,0 +1 @@
Invalid optimizer step sequence in --yul-optimizations: Cleanup delimiter may only be placed at nesting level zero

View File

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

View File

@ -0,0 +1 @@
--ir-optimized --optimize --yul-optimizations dhfoDg:vulfn:TUtnIf

View File

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

View File

@ -0,0 +1 @@
1

View File

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

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 dhfoDgvulfnTUtnIf:fDnTOc

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma abicoder v2;
contract C
{
constructor() {}
}

View File

@ -0,0 +1,34 @@
Optimized IR:
/// @use-src 0:"yul_optimizer_steps_with_cleanup_sequence/input.sol"
object "C_7" {
code {
{
/// @src 0:80:115 "contract C..."
mstore(64, memoryguard(0x80))
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
let _1 := allocate_unbounded()
codecopy(_1, dataoffset("C_7_deployed"), datasize("C_7_deployed"))
return(_1, datasize("C_7_deployed"))
}
function allocate_unbounded() -> memPtr
{ memPtr := mload(64) }
function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
{ revert(0, 0) }
}
/// @use-src 0:"yul_optimizer_steps_with_cleanup_sequence/input.sol"
object "C_7_deployed" {
code {
{
/// @src 0:80:115 "contract C..."
mstore(64, memoryguard(0x80))
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
}
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
{ revert(0, 0) }
}
data ".metadata" hex"<BYTECODE REMOVED>"
}
}

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_with_empty_cleanup_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(0, usr$a)
}
}
/// @use-src 0:"yul_optimizer_steps_with_empty_cleanup_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,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_with_empty_optimization_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(0, usr$a)
}
}
/// @use-src 0:"yul_optimizer_steps_with_empty_optimization_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 :

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma abicoder v2;
contract C
{
constructor() {}
}

View File

@ -0,0 +1,42 @@
Optimized IR:
/// @use-src 0:"yul_optimizer_steps_with_empty_sequences/input.sol"
object "C_7" {
code {
{
/// @src 0:80:115 "contract C..."
mstore(64, memoryguard(0x80))
if callvalue()
{
revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
}
constructor_C()
let _1 := allocate_unbounded()
codecopy(_1, dataoffset("C_7_deployed"), datasize("C_7_deployed"))
return(_1, datasize("C_7_deployed"))
}
function allocate_unbounded() -> memPtr
{ memPtr := mload(64) }
function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb()
{ revert(0, 0) }
/// @ast-id 6 @src 0:97:113 "constructor() {}"
function constructor_C()
{ }
}
/// @use-src 0:"yul_optimizer_steps_with_empty_sequences/input.sol"
object "C_7_deployed" {
code {
{
/// @src 0:80:115 "contract C..."
mstore(64, memoryguard(0x80))
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
}
function shift_right_unsigned(value) -> newValue
{ newValue := shr(224, value) }
function allocate_unbounded() -> memPtr
{ memPtr := mload(64) }
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
{ revert(0, 0) }
}
data ".metadata" hex"<BYTECODE REMOVED>"
}
}

View File

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