mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Take termination into account for data flow analyzer.
This commit is contained in:
parent
04c8a7f0c6
commit
ca29136348
@ -43,10 +43,12 @@ using namespace solidity::yul;
|
||||
|
||||
DataFlowAnalyzer::DataFlowAnalyzer(
|
||||
Dialect const& _dialect,
|
||||
map<YulString, SideEffects> _functionSideEffects
|
||||
map<YulString, SideEffects> _functionSideEffects,
|
||||
map<YulString, ControlFlowSideEffects> _controlFlowSideEffects
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_functionSideEffects(std::move(_functionSideEffects)),
|
||||
m_controlFlowSideEffects(std::move(_controlFlowSideEffects)),
|
||||
m_knowledgeBase(_dialect, m_value)
|
||||
{
|
||||
if (auto const* builtin = _dialect.memoryStoreFunction(YulString{}))
|
||||
@ -120,9 +122,10 @@ void DataFlowAnalyzer::operator()(If& _if)
|
||||
unordered_map<YulString, YulString> memory = m_memory;
|
||||
|
||||
ASTModifier::operator()(_if);
|
||||
joinKnowledge(storage, memory, _if.body);
|
||||
|
||||
joinKnowledge(storage, memory);
|
||||
|
||||
// TODO we can do the same optimization here, but maybe this is already done in the ssa
|
||||
// transform or some other step?
|
||||
clearValues(assignedVariableNames(_if.body));
|
||||
}
|
||||
|
||||
@ -136,8 +139,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
|
||||
unordered_map<YulString, YulString> storage = m_storage;
|
||||
unordered_map<YulString, YulString> memory = m_memory;
|
||||
(*this)(_case.body);
|
||||
//TODO throw away knowledge if it is terminating
|
||||
joinKnowledge(storage, memory);
|
||||
|
||||
joinKnowledge(storage, memory, _case.body);
|
||||
|
||||
set<YulString> variables = assignedVariableNames(_case.body);
|
||||
assignedVariables += variables;
|
||||
@ -343,7 +346,8 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Block const& _block)
|
||||
|
||||
void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
|
||||
{
|
||||
// TODO with the extended side-effects, this could also be more precise now.
|
||||
// TODO with the extended side-effects, this could also be more precise now,
|
||||
// I.e. we should check collisions like we do for sstore and mstore
|
||||
SideEffectsCollector sideEffects(m_dialect, _expr, &m_functionSideEffects);
|
||||
if (sideEffects.invalidatesStorage())
|
||||
m_storage.clear();
|
||||
@ -353,11 +357,25 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
|
||||
|
||||
void DataFlowAnalyzer::joinKnowledge(
|
||||
unordered_map<YulString, YulString> const& _olderStorage,
|
||||
unordered_map<YulString, YulString> const& _olderMemory
|
||||
unordered_map<YulString, YulString> const& _olderMemory,
|
||||
Block const& _branch
|
||||
)
|
||||
{
|
||||
joinKnowledgeHelper(m_storage, _olderStorage);
|
||||
joinKnowledgeHelper(m_memory, _olderMemory);
|
||||
if (
|
||||
_branch.statements.empty() ||
|
||||
TerminationFinder(m_dialect, &m_controlFlowSideEffects).controlFlowKind(_branch.statements.back()) ==
|
||||
TerminationFinder::ControlFlow::FlowOut
|
||||
)
|
||||
{
|
||||
joinKnowledgeHelper(m_storage, _olderStorage);
|
||||
joinKnowledgeHelper(m_memory, _olderMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TOOD test that this works with 'break' and 'continue'
|
||||
m_storage = _olderStorage;
|
||||
m_memory = _olderMemory;
|
||||
}
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::joinKnowledgeHelper(
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/AST.h> // Needed for m_zero below.
|
||||
#include <libyul/SideEffects.h>
|
||||
#include <libyul/ControlFlowSideEffects.h>
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
@ -85,7 +86,8 @@ public:
|
||||
/// The parameter is mostly used to determine movability of expressions.
|
||||
explicit DataFlowAnalyzer(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> _functionSideEffects = {}
|
||||
std::map<YulString, SideEffects> _functionSideEffects = {},
|
||||
std::map<YulString, ControlFlowSideEffects> _controlFlowSideEffects = {}
|
||||
);
|
||||
|
||||
using ASTModifier::operator();
|
||||
@ -123,9 +125,12 @@ protected:
|
||||
/// Joins knowledge about storage and memory with an older point in the control-flow.
|
||||
/// This only works if the current state is a direct successor of the older point,
|
||||
/// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes.
|
||||
/// If the last statement in the vector is not continuing, replace the current knowledge
|
||||
/// with the old instead.
|
||||
void joinKnowledge(
|
||||
std::unordered_map<YulString, YulString> const& _olderStorage,
|
||||
std::unordered_map<YulString, YulString> const& _olderMemory
|
||||
std::unordered_map<YulString, YulString> const& _olderMemory,
|
||||
Block const& _branch
|
||||
);
|
||||
|
||||
static void joinKnowledgeHelper(
|
||||
@ -163,6 +168,7 @@ protected:
|
||||
/// Side-effects of user-defined functions. Worst-case side-effects are assumed
|
||||
/// if this is not provided or the function is not found.
|
||||
std::map<YulString, SideEffects> m_functionSideEffects;
|
||||
std::map<YulString, ControlFlowSideEffects> m_controlFlowSideEffects;
|
||||
|
||||
/// Current values of variables, always movable.
|
||||
std::map<YulString, AssignedValue> m_value;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Utilities.h>
|
||||
@ -47,6 +48,7 @@ void LoadResolver::run(OptimiserStepContext& _context, Block& _ast)
|
||||
LoadResolver{
|
||||
_context.dialect,
|
||||
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)),
|
||||
ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed(),
|
||||
containsMSize,
|
||||
_context.expectedExecutionsPerDeployment
|
||||
}(_ast);
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <libyul/optimiser/DataFlowAnalyzer.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
#include <libyul/ControlFlowSideEffects.h>
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
@ -50,10 +51,11 @@ private:
|
||||
LoadResolver(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> _functionSideEffects,
|
||||
std::map<YulString, ControlFlowSideEffects> _controlFlowSideEffects,
|
||||
bool _containsMSize,
|
||||
std::optional<size_t> _expectedExecutionsPerDeployment
|
||||
):
|
||||
DataFlowAnalyzer(_dialect, std::move(_functionSideEffects)),
|
||||
DataFlowAnalyzer(_dialect, std::move(_functionSideEffects), std::move(_controlFlowSideEffects)),
|
||||
m_containsMSize(_containsMSize),
|
||||
m_expectedExecutionsPerDeployment(std::move(_expectedExecutionsPerDeployment))
|
||||
{}
|
||||
|
@ -14,6 +14,6 @@ contract c {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 9, 4
|
||||
// gas irOptimized: 123162
|
||||
// gas irOptimized: 123195
|
||||
// gas legacy: 123579
|
||||
// gas legacyOptimized: 123208
|
||||
|
@ -18,6 +18,6 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 7
|
||||
// gas irOptimized: 124041
|
||||
// gas irOptimized: 124024
|
||||
// gas legacy: 205196
|
||||
// gas legacyOptimized: 204987
|
||||
|
@ -11,7 +11,7 @@ contract Creator {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): 1, 2, 3, 4 ->
|
||||
// gas irOptimized: 129908
|
||||
// gas irOptimized: 127748
|
||||
// gas legacy: 176789
|
||||
// gas legacyOptimized: 129585
|
||||
// r() -> 4
|
||||
|
@ -45,6 +45,6 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 5, 6, 7
|
||||
// gas irOptimized: 298983
|
||||
// gas irOptimized: 298177
|
||||
// gas legacy: 452172
|
||||
// gas legacyOptimized: 285017
|
||||
|
@ -26,6 +26,6 @@ contract Creator {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
|
||||
// gas irOptimized: 443960
|
||||
// gas irOptimized: 446570
|
||||
// gas legacy: 590683
|
||||
// gas legacyOptimized: 448326
|
||||
|
@ -26,6 +26,6 @@ contract Creator {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h"
|
||||
// gas irOptimized: 300804
|
||||
// gas irOptimized: 299178
|
||||
// gas legacy: 428917
|
||||
// gas legacyOptimized: 298128
|
||||
|
@ -19,7 +19,7 @@ contract Main {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): "abc", true
|
||||
// gas irOptimized: 107175
|
||||
// gas irOptimized: 105225
|
||||
// gas legacy: 145838
|
||||
// gas legacyOptimized: 104017
|
||||
// getFlag() -> true
|
||||
|
@ -12,7 +12,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): 1, 2, 3, 4 ->
|
||||
// gas irOptimized: 174905
|
||||
// gas irOptimized: 175559
|
||||
// gas legacy: 221377
|
||||
// gas legacyOptimized: 177671
|
||||
// a() -> 1
|
||||
|
@ -15,5 +15,5 @@ contract B is A {
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// constructor() ->
|
||||
// gas irOptimized: 122017
|
||||
// gas irOptimized: 120289
|
||||
// y() -> 42
|
||||
|
@ -12,7 +12,7 @@ contract B is A {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor() ->
|
||||
// gas irOptimized: 122017
|
||||
// gas irOptimized: 120289
|
||||
// gas legacy: 135046
|
||||
// gas legacyOptimized: 116176
|
||||
// y() -> 42
|
||||
|
@ -11,7 +11,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): 2, 0 ->
|
||||
// gas irOptimized: 104227
|
||||
// gas irOptimized: 102283
|
||||
// gas legacy: 117158
|
||||
// i() -> 2
|
||||
// k() -> 0
|
||||
|
@ -23,7 +23,7 @@ contract D is B, C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): 2, 0 ->
|
||||
// gas irOptimized: 160166
|
||||
// gas irOptimized: 159086
|
||||
// gas legacy: 170665
|
||||
// gas legacyOptimized: 145396
|
||||
// i() -> 2
|
||||
|
@ -14,7 +14,7 @@ contract D is C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(): 2, 0 ->
|
||||
// gas irOptimized: 124844
|
||||
// gas irOptimized: 122900
|
||||
// gas legacy: 139250
|
||||
// gas legacyOptimized: 119367
|
||||
// i() -> 2
|
||||
|
@ -51,7 +51,7 @@ contract test {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor()
|
||||
// gas irOptimized: 707330
|
||||
// gas irOptimized: 708410
|
||||
// gas legacy: 1130761
|
||||
// gas legacyOptimized: 750416
|
||||
// toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0
|
||||
@ -71,6 +71,6 @@ contract test {
|
||||
// gas legacy: 31621
|
||||
// gas legacyOptimized: 27914
|
||||
// benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020
|
||||
// gas irOptimized: 2040045
|
||||
// gas irOptimized: 2040061
|
||||
// gas legacy: 4381235
|
||||
// gas legacyOptimized: 2317529
|
||||
|
@ -18,7 +18,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(), 20 wei
|
||||
// gas irOptimized: 219285
|
||||
// gas irOptimized: 218415
|
||||
// gas legacy: 294569
|
||||
// gas legacyOptimized: 174699
|
||||
// f(uint256): 20 -> 1370859564726510389319704988634906228201275401179
|
||||
|
@ -41,7 +41,7 @@ contract test {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(), 20 wei ->
|
||||
// gas irOptimized: 283040
|
||||
// gas irOptimized: 281960
|
||||
// gas legacy: 402654
|
||||
// gas legacyOptimized: 274470
|
||||
// sendAmount(uint256): 5 -> 5
|
||||
|
@ -40,7 +40,7 @@ contract test {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(), 20 wei ->
|
||||
// gas irOptimized: 283040
|
||||
// gas irOptimized: 281960
|
||||
// gas legacy: 402654
|
||||
// gas legacyOptimized: 274470
|
||||
// sendAmount(uint256): 5 -> 5
|
||||
|
@ -29,7 +29,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> 3, 7, 5
|
||||
// gas irOptimized: 127347
|
||||
// gas irOptimized: 127321
|
||||
// gas legacy: 151334
|
||||
// gas legacyOptimized: 125166
|
||||
// x() -> 7
|
||||
|
@ -25,6 +25,6 @@ contract B {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g() -> 42
|
||||
// gas irOptimized: 111794
|
||||
// gas irOptimized: 112591
|
||||
// gas legacy: 185053
|
||||
// gas legacyOptimized: 114598
|
||||
|
@ -42,7 +42,7 @@ contract Main {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor(), 22 wei ->
|
||||
// gas irOptimized: 284287
|
||||
// gas irOptimized: 281653
|
||||
// gas legacy: 402045
|
||||
// gas legacyOptimized: 266772
|
||||
// getFlag() -> true
|
||||
|
@ -22,6 +22,6 @@ contract A {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(), 10 ether -> 3007, 3008, 3009
|
||||
// gas irOptimized: 272413
|
||||
// gas irOptimized: 273010
|
||||
// gas legacy: 422501
|
||||
// gas legacyOptimized: 287472
|
||||
|
@ -14,7 +14,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor()
|
||||
// gas irOptimized: 115297
|
||||
// gas irOptimized: 114001
|
||||
// gas legacy: 155081
|
||||
// gas legacyOptimized: 107997
|
||||
// genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737
|
||||
|
@ -17,7 +17,7 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// constructor() ->
|
||||
// gas irOptimized: 199723
|
||||
// gas irOptimized: 199084
|
||||
// gas legacy: 241124
|
||||
// gas legacyOptimized: 155549
|
||||
// initCode() -> 0x20, 0
|
||||
|
@ -36,6 +36,6 @@ contract C {
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(bytes): 0x20, 0x5, "abcde" -> 0
|
||||
// gas irOptimized: 240691
|
||||
// gas irOptimized: 240688
|
||||
// gas legacy: 240358
|
||||
// gas legacyOptimized: 239682
|
||||
|
@ -23,6 +23,6 @@
|
||||
// mstore(_1, sload(0))
|
||||
// return(_1, 32)
|
||||
// }
|
||||
// case 0xc2985578 { return(mload(64), 0) }
|
||||
// case 0xc2985578 { return(_1, 0) }
|
||||
// }
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user