Take termination into account for data flow analyzer.

This commit is contained in:
chriseth 2022-03-09 09:55:44 +01:00
parent 04c8a7f0c6
commit ca29136348
29 changed files with 66 additions and 38 deletions

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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))
{}

View File

@ -14,6 +14,6 @@ contract c {
// compileViaYul: also
// ----
// test() -> 9, 4
// gas irOptimized: 123162
// gas irOptimized: 123195
// gas legacy: 123579
// gas legacyOptimized: 123208

View File

@ -18,6 +18,6 @@ contract C {
// compileViaYul: also
// ----
// test() -> 7
// gas irOptimized: 124041
// gas irOptimized: 124024
// gas legacy: 205196
// gas legacyOptimized: 204987

View File

@ -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

View File

@ -45,6 +45,6 @@ contract C {
// compileViaYul: also
// ----
// test() -> 5, 6, 7
// gas irOptimized: 298983
// gas irOptimized: 298177
// gas legacy: 452172
// gas legacyOptimized: 285017

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -15,5 +15,5 @@ contract B is A {
// compileViaYul: true
// ----
// constructor() ->
// gas irOptimized: 122017
// gas irOptimized: 120289
// y() -> 42

View File

@ -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

View File

@ -11,7 +11,7 @@ contract C {
// compileViaYul: also
// ----
// constructor(): 2, 0 ->
// gas irOptimized: 104227
// gas irOptimized: 102283
// gas legacy: 117158
// i() -> 2
// k() -> 0

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -25,6 +25,6 @@ contract B {
// compileViaYul: also
// ----
// g() -> 42
// gas irOptimized: 111794
// gas irOptimized: 112591
// gas legacy: 185053
// gas legacyOptimized: 114598

View File

@ -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

View File

@ -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

View File

@ -14,7 +14,7 @@ contract C {
// compileViaYul: also
// ----
// constructor()
// gas irOptimized: 115297
// gas irOptimized: 114001
// gas legacy: 155081
// gas legacyOptimized: 107997
// genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737

View File

@ -17,7 +17,7 @@ contract C {
// compileViaYul: also
// ----
// constructor() ->
// gas irOptimized: 199723
// gas irOptimized: 199084
// gas legacy: 241124
// gas legacyOptimized: 155549
// initCode() -> 0x20, 0

View File

@ -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

View File

@ -23,6 +23,6 @@
// mstore(_1, sload(0))
// return(_1, 32)
// }
// case 0xc2985578 { return(mload(64), 0) }
// case 0xc2985578 { return(_1, 0) }
// }
// }