mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6118 from ethereum/useStackOpt
Use stack optimizations.
This commit is contained in:
commit
85a0d6a334
@ -4,6 +4,7 @@ Language Features:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails).
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -216,8 +216,12 @@ Input Description
|
|||||||
// It can only be activated through the details here.
|
// It can only be activated through the details here.
|
||||||
// This feature is still considered experimental.
|
// This feature is still considered experimental.
|
||||||
"yul": false,
|
"yul": false,
|
||||||
// Future tuning options, currently unused.
|
// Tuning options for the Yul optimizer.
|
||||||
"yulDetails": {}
|
"yulDetails": {
|
||||||
|
// Improve allocation of stack slots for variables, can free up stack slots early.
|
||||||
|
// Activated by default if the Yul optimizer is activated.
|
||||||
|
"stackAllocation": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg
|
"evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg
|
||||||
|
@ -331,7 +331,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
vector<string> const& _localVariables,
|
vector<string> const& _localVariables,
|
||||||
set<string> const& _externallyUsedFunctions,
|
set<string> const& _externallyUsedFunctions,
|
||||||
bool _system,
|
bool _system,
|
||||||
bool _optimise
|
OptimiserSettings const& _optimiserSettings
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int startStackHeight = stackHeight();
|
int startStackHeight = stackHeight();
|
||||||
@ -422,7 +422,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
|
|
||||||
// Several optimizer steps cannot handle externally supplied stack variables,
|
// Several optimizer steps cannot handle externally supplied stack variables,
|
||||||
// so we essentially only optimize the ABI functions.
|
// so we essentially only optimize the ABI functions.
|
||||||
if (_optimise && _localVariables.empty())
|
if (_optimiserSettings.runYulOptimiser && _localVariables.empty())
|
||||||
{
|
{
|
||||||
yul::OptimiserSuite::run(
|
yul::OptimiserSuite::run(
|
||||||
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
||||||
@ -445,7 +445,15 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
reportError("Failed to analyze inline assembly block.");
|
reportError("Failed to analyze inline assembly block.");
|
||||||
|
|
||||||
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
|
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
|
||||||
yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, m_evmVersion, identifierAccess, _system, _optimise);
|
yul::CodeGenerator::assemble(
|
||||||
|
*parserResult,
|
||||||
|
analysisInfo,
|
||||||
|
*m_asm,
|
||||||
|
m_evmVersion,
|
||||||
|
identifierAccess,
|
||||||
|
_system,
|
||||||
|
_optimiserSettings.optimizeStackAllocation
|
||||||
|
);
|
||||||
|
|
||||||
// Reset the source location to the one of the node (instead of the CODEGEN source location)
|
// Reset the source location to the one of the node (instead of the CODEGEN source location)
|
||||||
updateSourceLocation();
|
updateSourceLocation();
|
||||||
|
@ -217,7 +217,7 @@ public:
|
|||||||
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
|
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
|
||||||
std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
|
std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
|
||||||
bool _system = false,
|
bool _system = false,
|
||||||
bool _optimise = false
|
OptimiserSettings const& _optimiserSettings = OptimiserSettings::none()
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Appends arbitrary data to the end of the bytecode.
|
/// Appends arbitrary data to the end of the bytecode.
|
||||||
|
@ -720,7 +720,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
*_inlineAssembly.annotation().analysisInfo,
|
*_inlineAssembly.annotation().analysisInfo,
|
||||||
*m_context.assemblyPtr(),
|
*m_context.assemblyPtr(),
|
||||||
m_context.evmVersion(),
|
m_context.evmVersion(),
|
||||||
identifierAccess
|
identifierAccess,
|
||||||
|
false,
|
||||||
|
m_optimiserSettings.optimizeStackAllocation
|
||||||
);
|
);
|
||||||
m_context.setStackOffset(startStackHeight);
|
m_context.setStackOffset(startStackHeight);
|
||||||
return false;
|
return false;
|
||||||
@ -983,7 +985,7 @@ void ContractCompiler::appendMissingFunctions()
|
|||||||
{},
|
{},
|
||||||
abiFunctions.second,
|
abiFunctions.second,
|
||||||
true,
|
true,
|
||||||
m_optimiserSettings.runYulOptimiser
|
m_optimiserSettings
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -993,7 +993,11 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
|||||||
details["cse"] = m_optimiserSettings.runCSE;
|
details["cse"] = m_optimiserSettings.runCSE;
|
||||||
details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser;
|
details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser;
|
||||||
details["yul"] = m_optimiserSettings.runYulOptimiser;
|
details["yul"] = m_optimiserSettings.runYulOptimiser;
|
||||||
details["yulDetails"] = Json::objectValue;
|
if (m_optimiserSettings.runYulOptimiser)
|
||||||
|
{
|
||||||
|
details["yulDetails"] = Json::objectValue;
|
||||||
|
details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation;
|
||||||
|
}
|
||||||
|
|
||||||
meta["settings"]["optimizer"]["details"] = std::move(details);
|
meta["settings"]["optimizer"]["details"] = std::move(details);
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,17 @@ struct OptimiserSettings
|
|||||||
s.runDeduplicate = true;
|
s.runDeduplicate = true;
|
||||||
s.runCSE = true;
|
s.runCSE = true;
|
||||||
s.runConstantOptimiser = true;
|
s.runConstantOptimiser = true;
|
||||||
// The only disabled one
|
// The only disabled ones
|
||||||
|
s.optimizeStackAllocation = false;
|
||||||
s.runYulOptimiser = false;
|
s.runYulOptimiser = false;
|
||||||
s.expectedExecutionsPerDeployment = 200;
|
s.expectedExecutionsPerDeployment = 200;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
/// Standard optimisations plus yul optimiser.
|
/// Standard optimisations plus yul and stack optimiser.
|
||||||
static OptimiserSettings full()
|
static OptimiserSettings full()
|
||||||
{
|
{
|
||||||
OptimiserSettings s = enabled();
|
OptimiserSettings s = enabled();
|
||||||
|
s.optimizeStackAllocation = true;
|
||||||
s.runYulOptimiser = true;
|
s.runYulOptimiser = true;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -76,6 +78,7 @@ struct OptimiserSettings
|
|||||||
runDeduplicate == _other.runDeduplicate &&
|
runDeduplicate == _other.runDeduplicate &&
|
||||||
runCSE == _other.runCSE &&
|
runCSE == _other.runCSE &&
|
||||||
runConstantOptimiser == _other.runConstantOptimiser &&
|
runConstantOptimiser == _other.runConstantOptimiser &&
|
||||||
|
optimizeStackAllocation == _other.optimizeStackAllocation &&
|
||||||
runYulOptimiser == _other.runYulOptimiser &&
|
runYulOptimiser == _other.runYulOptimiser &&
|
||||||
expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment;
|
expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment;
|
||||||
}
|
}
|
||||||
@ -94,6 +97,8 @@ struct OptimiserSettings
|
|||||||
/// Constant optimizer, which tries to find better representations that satisfy the given
|
/// Constant optimizer, which tries to find better representations that satisfy the given
|
||||||
/// size/cost-trade-off.
|
/// size/cost-trade-off.
|
||||||
bool runConstantOptimiser = false;
|
bool runConstantOptimiser = false;
|
||||||
|
/// Perform more efficient stack allocation for variables during code generation from Yul to bytecode.
|
||||||
|
bool optimizeStackAllocation = false;
|
||||||
/// Yul optimiser with default settings. Will only run on certain parts of the code for now.
|
/// Yul optimiser with default settings. Will only run on certain parts of the code for now.
|
||||||
bool runYulOptimiser = false;
|
bool runYulOptimiser = false;
|
||||||
/// 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,
|
||||||
|
@ -399,12 +399,17 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
|
|||||||
return *error;
|
return *error;
|
||||||
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
|
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
|
||||||
return *error;
|
return *error;
|
||||||
|
if (settings.runYulOptimiser)
|
||||||
|
settings.optimizeStackAllocation = true;
|
||||||
if (details.isMember("yulDetails"))
|
if (details.isMember("yulDetails"))
|
||||||
{
|
{
|
||||||
if (!_jsonInput["yulDetails"].isObject())
|
if (!settings.runYulOptimiser)
|
||||||
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting has to be a JSON object.");
|
return formatFatalError("JSONError", "\"Providing yulDetails requires Yul optimizer to be enabled.");
|
||||||
if (!_jsonInput["yulDetails"].getMemberNames().empty())
|
|
||||||
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting cannot have any settings yet.");
|
if (auto result = checkKeys(details["yulDetails"], {"stackAllocation"}, "settings.optimizer.details.yulDetails"))
|
||||||
|
return *result;
|
||||||
|
if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation))
|
||||||
|
return *error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::move(settings);
|
return std::move(settings);
|
||||||
|
@ -74,7 +74,7 @@ public:
|
|||||||
|
|
||||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||||
/// @param _optimize does not run the optimizer but performs optimized code generation.
|
/// @param _optimize does not run the optimizer but performs optimized code generation.
|
||||||
MachineAssemblyObject assemble(Machine _machine, bool _optimize = false) const;
|
MachineAssemblyObject assemble(Machine _machine, bool _optimize) const;
|
||||||
|
|
||||||
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
||||||
langutil::ErrorList const& errors() const { return m_errors; }
|
langutil::ErrorList const& errors() const { return m_errors; }
|
||||||
|
@ -180,7 +180,7 @@ void CodeGenerator::assemble(
|
|||||||
langutil::EVMVersion _evmVersion,
|
langutil::EVMVersion _evmVersion,
|
||||||
ExternalIdentifierAccess const& _identifierAccess,
|
ExternalIdentifierAccess const& _identifierAccess,
|
||||||
bool _useNamedLabelsForFunctions,
|
bool _useNamedLabelsForFunctions,
|
||||||
bool _optimize
|
bool _optimizeStackAllocation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EthAssemblyAdapter assemblyAdapter(_assembly);
|
EthAssemblyAdapter assemblyAdapter(_assembly);
|
||||||
@ -190,7 +190,7 @@ void CodeGenerator::assemble(
|
|||||||
_analysisInfo,
|
_analysisInfo,
|
||||||
_parsedData,
|
_parsedData,
|
||||||
*dialect,
|
*dialect,
|
||||||
_optimize,
|
_optimizeStackAllocation,
|
||||||
false,
|
false,
|
||||||
_identifierAccess,
|
_identifierAccess,
|
||||||
_useNamedLabelsForFunctions
|
_useNamedLabelsForFunctions
|
||||||
|
@ -82,7 +82,7 @@ public:
|
|||||||
langutil::EVMVersion _evmVersion,
|
langutil::EVMVersion _evmVersion,
|
||||||
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
|
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
|
||||||
bool _useNamedLabelsForFunctions = false,
|
bool _useNamedLabelsForFunctions = false,
|
||||||
bool _optimize = false
|
bool _optimizeStackAllocation = false
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -907,6 +907,7 @@ bool CommandLineInterface::processInput()
|
|||||||
OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
|
OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
|
||||||
settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as<unsigned>();
|
settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as<unsigned>();
|
||||||
settings.runYulOptimiser = m_args.count(g_strOptimizeYul);
|
settings.runYulOptimiser = m_args.count(g_strOptimizeYul);
|
||||||
|
settings.optimizeStackAllocation = settings.runYulOptimiser;
|
||||||
m_compiler->setOptimiserSettings(settings);
|
m_compiler->setOptimiserSettings(settings);
|
||||||
|
|
||||||
bool successful = m_compiler->compile();
|
bool successful = m_compiler->compile();
|
||||||
@ -1294,7 +1295,7 @@ bool CommandLineInterface::assemble(
|
|||||||
yul::MachineAssemblyObject object;
|
yul::MachineAssemblyObject object;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
object = stack.assemble(_targetMachine);
|
object = stack.assemble(_targetMachine, _optimize);
|
||||||
}
|
}
|
||||||
catch (Exception const& _exception)
|
catch (Exception const& _exception)
|
||||||
{
|
{
|
||||||
|
@ -125,6 +125,8 @@ function test_solc_behaviour()
|
|||||||
sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
|
sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
|
||||||
sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path"
|
sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path"
|
||||||
fi
|
fi
|
||||||
|
# Remove path to cpp file
|
||||||
|
sed -i -e 's/^\(Exception while assembling:\).*/\1/' "$stderr_path"
|
||||||
|
|
||||||
if [[ $exitCode -ne "$exit_code_expected" ]]
|
if [[ $exitCode -ne "$exit_code_expected" ]]
|
||||||
then
|
then
|
||||||
|
@ -42,7 +42,6 @@ Text representation:
|
|||||||
0x00
|
0x00
|
||||||
/* "object_compiler/input.sol":265:295 */
|
/* "object_compiler/input.sol":265:295 */
|
||||||
return
|
return
|
||||||
/* "object_compiler/input.sol":29:299 */
|
|
||||||
pop
|
pop
|
||||||
stop
|
stop
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"settings":
|
"settings":
|
||||||
{
|
{
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"details": { "yulDetails": 7 }
|
"details": { "yul": true, "yulDetails": 7 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
{"errors":[{"component":"general","formattedMessage":"The \"yulDetails\" optimizer setting has to be a JSON object.","message":"The \"yulDetails\" optimizer setting has to be a JSON object.","severity":"error","type":"JSONError"}]}
|
{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer.details.yulDetails\" must be an object","message":"\"settings.optimizer.details.yulDetails\" must be an object","severity":"error","type":"JSONError"}]}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"optimizer": {
|
||||||
|
"details": { "yulDetails": 7 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{"errors":[{"component":"general","formattedMessage":"\"Providing yulDetails requires Yul optimizer to be enabled.","message":"\"Providing yulDetails requires Yul optimizer to be enabled.","severity":"error","type":"JSONError"}]}
|
1
test/cmdlineTests/yul_stack_opt/args
Normal file
1
test/cmdlineTests/yul_stack_opt/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--strict-assembly --optimize
|
24
test/cmdlineTests/yul_stack_opt/input.sol
Normal file
24
test/cmdlineTests/yul_stack_opt/input.sol
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
|
||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
let b := 1
|
||||||
|
let z3 := 1
|
||||||
|
sstore(a, b)
|
||||||
|
sstore(add(a, 1), b)
|
||||||
|
sstore(add(a, 2), b)
|
||||||
|
sstore(add(a, 3), b)
|
||||||
|
sstore(add(a, 4), b)
|
||||||
|
sstore(add(a, 5), b)
|
||||||
|
sstore(add(a, 6), b)
|
||||||
|
sstore(add(a, 7), b)
|
||||||
|
sstore(add(a, 8), b)
|
||||||
|
sstore(add(a, 9), b)
|
||||||
|
sstore(add(a, 10), b)
|
||||||
|
sstore(add(a, 11), b)
|
||||||
|
sstore(add(a, 12), b)
|
||||||
|
}
|
||||||
|
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
|
||||||
|
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
|
||||||
|
sstore(a1, a2)
|
||||||
|
}
|
202
test/cmdlineTests/yul_stack_opt/output
Normal file
202
test/cmdlineTests/yul_stack_opt/output
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
======= yul_stack_opt/input.sol (EVM) =======
|
||||||
|
|
||||||
|
Pretty printed source:
|
||||||
|
object "object" {
|
||||||
|
code {
|
||||||
|
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
|
||||||
|
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
|
||||||
|
sstore(a1, a2)
|
||||||
|
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
|
||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
sstore(a, a)
|
||||||
|
sstore(2, a)
|
||||||
|
sstore(3, a)
|
||||||
|
sstore(4, a)
|
||||||
|
sstore(5, a)
|
||||||
|
sstore(6, a)
|
||||||
|
sstore(7, a)
|
||||||
|
sstore(8, a)
|
||||||
|
sstore(9, a)
|
||||||
|
sstore(10, a)
|
||||||
|
sstore(11, a)
|
||||||
|
sstore(12, a)
|
||||||
|
sstore(13, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Binary representation:
|
||||||
|
60056032565b505050505050505050505050505050601a6032565b5050505050505050505050505050508082555050609a565b60006000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5550909192939495969798999a9b9c9d9e9f565b
|
||||||
|
|
||||||
|
Text representation:
|
||||||
|
/* "yul_stack_opt/input.sol":495:500 */
|
||||||
|
tag_1
|
||||||
|
jump(tag_2)
|
||||||
|
tag_1:
|
||||||
|
/* "yul_stack_opt/input.sol":425:500 */
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
/* "yul_stack_opt/input.sol":572:577 */
|
||||||
|
tag_3
|
||||||
|
jump(tag_2)
|
||||||
|
tag_3:
|
||||||
|
/* "yul_stack_opt/input.sol":502:577 */
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
/* "yul_stack_opt/input.sol":590:592 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":586:588 */
|
||||||
|
dup3
|
||||||
|
/* "yul_stack_opt/input.sol":579:593 */
|
||||||
|
sstore
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
/* "yul_stack_opt/input.sol":3:423 */
|
||||||
|
jump(tag_4)
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
0x00
|
||||||
|
/* "yul_stack_opt/input.sol":98:99 */
|
||||||
|
0x01
|
||||||
|
/* "yul_stack_opt/input.sol":139:140 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":136:137 */
|
||||||
|
dup2
|
||||||
|
/* "yul_stack_opt/input.sol":129:141 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":162:163 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":151:160 */
|
||||||
|
0x02
|
||||||
|
/* "yul_stack_opt/input.sol":144:164 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":185:186 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":174:183 */
|
||||||
|
0x03
|
||||||
|
/* "yul_stack_opt/input.sol":167:187 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":208:209 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":197:206 */
|
||||||
|
0x04
|
||||||
|
/* "yul_stack_opt/input.sol":190:210 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":231:232 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":220:229 */
|
||||||
|
0x05
|
||||||
|
/* "yul_stack_opt/input.sol":213:233 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":254:255 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":243:252 */
|
||||||
|
0x06
|
||||||
|
/* "yul_stack_opt/input.sol":236:256 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":277:278 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":266:275 */
|
||||||
|
0x07
|
||||||
|
/* "yul_stack_opt/input.sol":259:279 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":300:301 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":289:298 */
|
||||||
|
0x08
|
||||||
|
/* "yul_stack_opt/input.sol":282:302 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":323:324 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":312:321 */
|
||||||
|
0x09
|
||||||
|
/* "yul_stack_opt/input.sol":305:325 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":346:347 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":335:344 */
|
||||||
|
0x0a
|
||||||
|
/* "yul_stack_opt/input.sol":328:348 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":370:371 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":358:368 */
|
||||||
|
0x0b
|
||||||
|
/* "yul_stack_opt/input.sol":351:372 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":394:395 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":382:392 */
|
||||||
|
0x0c
|
||||||
|
/* "yul_stack_opt/input.sol":375:396 */
|
||||||
|
sstore
|
||||||
|
/* "yul_stack_opt/input.sol":418:419 */
|
||||||
|
dup1
|
||||||
|
/* "yul_stack_opt/input.sol":406:416 */
|
||||||
|
0x0d
|
||||||
|
/* "yul_stack_opt/input.sol":399:420 */
|
||||||
|
sstore
|
||||||
|
pop
|
||||||
|
/* "yul_stack_opt/input.sol":85:423 */
|
||||||
|
swap1
|
||||||
|
swap2
|
||||||
|
swap3
|
||||||
|
swap4
|
||||||
|
swap5
|
||||||
|
swap6
|
||||||
|
swap7
|
||||||
|
swap8
|
||||||
|
swap9
|
||||||
|
swap10
|
||||||
|
swap11
|
||||||
|
swap12
|
||||||
|
swap13
|
||||||
|
swap14
|
||||||
|
swap15
|
||||||
|
swap16
|
||||||
|
jump
|
||||||
|
tag_4:
|
||||||
|
|
1
test/cmdlineTests/yul_stack_opt_disabled/args
Normal file
1
test/cmdlineTests/yul_stack_opt_disabled/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--strict-assembly
|
5
test/cmdlineTests/yul_stack_opt_disabled/err
Normal file
5
test/cmdlineTests/yul_stack_opt_disabled/err
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Exception while assembling:
|
||||||
|
Dynamic exception type: boost::exception_detail::clone_impl<yul::StackTooDeepError>
|
||||||
|
std::exception::what: Variable a1 is 17 slot(s) too deep inside the stack.
|
||||||
|
[dev::tag_comment*] = Variable a1 is 17 slot(s) too deep inside the stack.
|
||||||
|
|
1
test/cmdlineTests/yul_stack_opt_disabled/exit
Normal file
1
test/cmdlineTests/yul_stack_opt_disabled/exit
Normal file
@ -0,0 +1 @@
|
|||||||
|
1
|
24
test/cmdlineTests/yul_stack_opt_disabled/input.sol
Normal file
24
test/cmdlineTests/yul_stack_opt_disabled/input.sol
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
|
||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
let b := 1
|
||||||
|
let z3 := 1
|
||||||
|
sstore(a, b)
|
||||||
|
sstore(add(a, 1), b)
|
||||||
|
sstore(add(a, 2), b)
|
||||||
|
sstore(add(a, 3), b)
|
||||||
|
sstore(add(a, 4), b)
|
||||||
|
sstore(add(a, 5), b)
|
||||||
|
sstore(add(a, 6), b)
|
||||||
|
sstore(add(a, 7), b)
|
||||||
|
sstore(add(a, 8), b)
|
||||||
|
sstore(add(a, 9), b)
|
||||||
|
sstore(add(a, 10), b)
|
||||||
|
sstore(add(a, 11), b)
|
||||||
|
sstore(add(a, 12), b)
|
||||||
|
}
|
||||||
|
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
|
||||||
|
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
|
||||||
|
sstore(a1, a2)
|
||||||
|
}
|
31
test/cmdlineTests/yul_stack_opt_disabled/output
Normal file
31
test/cmdlineTests/yul_stack_opt_disabled/output
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
======= yul_stack_opt_disabled/input.sol (EVM) =======
|
||||||
|
|
||||||
|
Pretty printed source:
|
||||||
|
object "object" {
|
||||||
|
code {
|
||||||
|
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
|
||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
let b := 1
|
||||||
|
let z3 := 1
|
||||||
|
sstore(a, b)
|
||||||
|
sstore(add(a, 1), b)
|
||||||
|
sstore(add(a, 2), b)
|
||||||
|
sstore(add(a, 3), b)
|
||||||
|
sstore(add(a, 4), b)
|
||||||
|
sstore(add(a, 5), b)
|
||||||
|
sstore(add(a, 6), b)
|
||||||
|
sstore(add(a, 7), b)
|
||||||
|
sstore(add(a, 8), b)
|
||||||
|
sstore(add(a, 9), b)
|
||||||
|
sstore(add(a, 10), b)
|
||||||
|
sstore(add(a, 11), b)
|
||||||
|
sstore(add(a, 12), b)
|
||||||
|
}
|
||||||
|
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
|
||||||
|
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
|
||||||
|
sstore(a1, a2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,8 +66,9 @@ boost::optional<Error> parseAndReturnFirstError(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
success = stack.parseAndAnalyze("", _source);
|
success = stack.parseAndAnalyze("", _source);
|
||||||
|
bool const optimize = false;
|
||||||
if (success && _assemble)
|
if (success && _assemble)
|
||||||
stack.assemble(_machine);
|
stack.assemble(_machine, optimize);
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
{
|
{
|
||||||
|
@ -1010,7 +1010,10 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
|
|||||||
BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true);
|
BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true);
|
||||||
BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false);
|
BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false);
|
||||||
BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true);
|
BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true);
|
||||||
|
BOOST_CHECK(optimizer["details"]["yul"].asBool() == true);
|
||||||
BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
|
BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
|
||||||
|
BOOST_CHECK(optimizer["details"]["yulDetails"].getMemberNames() == vector<string>{"stackAllocation"});
|
||||||
|
BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
|
||||||
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
|
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
|
||||||
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
||||||
}
|
}
|
||||||
@ -1077,6 +1080,75 @@ BOOST_AUTO_TEST_CASE(common_pattern)
|
|||||||
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
|
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(use_stack_optimization)
|
||||||
|
{
|
||||||
|
// NOTE: the contract code here should fail to compile due to "out of stack"
|
||||||
|
// If we enable stack optimization, though, it will compile.
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"settings": {
|
||||||
|
"optimizer": { "enabled": true, "details": { "yul": true } },
|
||||||
|
"outputSelection": {
|
||||||
|
"fileA": { "A": [ "evm.bytecode.object" ] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"fileA": {
|
||||||
|
"content": "contract A {
|
||||||
|
function y() public {
|
||||||
|
assembly {
|
||||||
|
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
|
||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
let b := 1
|
||||||
|
let z3 := 1
|
||||||
|
sstore(a, b)
|
||||||
|
sstore(add(a, 1), b)
|
||||||
|
sstore(add(a, 2), b)
|
||||||
|
sstore(add(a, 3), b)
|
||||||
|
sstore(add(a, 4), b)
|
||||||
|
sstore(add(a, 5), b)
|
||||||
|
sstore(add(a, 6), b)
|
||||||
|
sstore(add(a, 7), b)
|
||||||
|
sstore(add(a, 8), b)
|
||||||
|
sstore(add(a, 9), b)
|
||||||
|
sstore(add(a, 10), b)
|
||||||
|
sstore(add(a, 11), b)
|
||||||
|
sstore(add(a, 12), b)
|
||||||
|
}
|
||||||
|
let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
|
||||||
|
let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
|
||||||
|
sstore(a1, a2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
Json::Value parsedInput;
|
||||||
|
BOOST_REQUIRE(jsonParseStrict(input, parsedInput));
|
||||||
|
|
||||||
|
dev::solidity::StandardCompiler compiler;
|
||||||
|
Json::Value result = compiler.compile(parsedInput);
|
||||||
|
|
||||||
|
BOOST_CHECK(containsAtMostWarnings(result));
|
||||||
|
Json::Value contract = getContractResult(result, "fileA", "A");
|
||||||
|
BOOST_REQUIRE(contract.isObject());
|
||||||
|
BOOST_REQUIRE(contract["evm"]["bytecode"]["object"].isString());
|
||||||
|
BOOST_CHECK(contract["evm"]["bytecode"]["object"].asString().length() > 20);
|
||||||
|
|
||||||
|
// Now disable stack optimizations
|
||||||
|
// results in "stack too deep"
|
||||||
|
parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["stackAllocation"] = false;
|
||||||
|
result = compiler.compile(parsedInput);
|
||||||
|
BOOST_REQUIRE(result["errors"].isArray());
|
||||||
|
BOOST_CHECK(result["errors"][0]["severity"] == "error");
|
||||||
|
BOOST_CHECK(result["errors"][0]["type"] == "InternalCompilerError");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ bool ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool c
|
|||||||
if (m_optimize)
|
if (m_optimize)
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
|
|
||||||
MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM);
|
MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM, m_optimize);
|
||||||
solAssert(obj.bytecode, "");
|
solAssert(obj.bytecode, "");
|
||||||
|
|
||||||
m_obtainedResult = "Assembly:\n" + obj.assembly;
|
m_obtainedResult = "Assembly:\n" + obj.assembly;
|
||||||
|
Loading…
Reference in New Issue
Block a user