Coverage Report

Created: 2022-08-24 06:40

/src/solidity/libyul/optimiser/LoadResolver.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * Optimisation stage that replaces expressions of type ``sload(x)`` by the value
20
 * currently stored in storage, if known.
21
 */
22
23
#include <libyul/optimiser/LoadResolver.h>
24
25
#include <libyul/backends/evm/EVMDialect.h>
26
#include <libyul/backends/evm/EVMMetrics.h>
27
#include <libyul/optimiser/Semantics.h>
28
#include <libyul/optimiser/CallGraphGenerator.h>
29
#include <libyul/SideEffects.h>
30
#include <libyul/AST.h>
31
#include <libyul/Utilities.h>
32
33
#include <libevmasm/GasMeter.h>
34
#include <libsolutil/Keccak256.h>
35
36
#include <limits>
37
38
using namespace std;
39
using namespace solidity;
40
using namespace solidity::util;
41
using namespace solidity::evmasm;
42
using namespace solidity::yul;
43
44
void LoadResolver::run(OptimiserStepContext& _context, Block& _ast)
45
97.3k
{
46
97.3k
  bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast);
47
97.3k
  LoadResolver{
48
97.3k
    _context.dialect,
49
97.3k
    SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)),
50
97.3k
    containsMSize,
51
97.3k
    _context.expectedExecutionsPerDeployment
52
97.3k
  }(_ast);
53
97.3k
}
54
55
void LoadResolver::visit(Expression& _e)
56
14.5M
{
57
14.5M
  DataFlowAnalyzer::visit(_e);
58
59
14.5M
  if (FunctionCall const* funCall = std::get_if<FunctionCall>(&_e))
60
2.39M
  {
61
2.39M
    for (auto location: { StoreLoadLocation::Memory, StoreLoadLocation::Storage })
62
4.70M
      if (funCall->functionName.name == m_loadFunctionName[static_cast<unsigned>(location)])
63
102k
      {
64
102k
        tryResolve(_e, location, funCall->arguments);
65
102k
        break;
66
102k
      }
67
68
2.39M
    if (!m_containsMSize && funCall->functionName.name == m_dialect.hashFunction({}))
69
1.36k
      tryEvaluateKeccak(_e, funCall->arguments);
70
2.39M
  }
71
14.5M
}
72
73
void LoadResolver::tryResolve(
74
  Expression& _e,
75
  StoreLoadLocation _location,
76
  vector<Expression> const& _arguments
77
)
78
102k
{
79
102k
  if (_arguments.empty() || !holds_alternative<Identifier>(_arguments.at(0)))
80
16.7k
    return;
81
82
85.7k
  YulString key = std::get<Identifier>(_arguments.at(0)).name;
83
85.7k
  if (_location == StoreLoadLocation::Storage)
84
15.2k
  {
85
15.2k
    if (auto value = storageValue(key))
86
94
      if (inScope(*value))
87
74
        _e = Identifier{debugDataOf(_e), *value};
88
15.2k
  }
89
70.4k
  else if (!m_containsMSize && _location == StoreLoadLocation::Memory)
90
70.3k
    if (auto value = memoryValue(key))
91
5.17k
      if (inScope(*value))
92
5.17k
        _e = Identifier{debugDataOf(_e), *value};
93
85.7k
}
94
95
void LoadResolver::tryEvaluateKeccak(
96
  Expression& _e,
97
  std::vector<Expression> const& _arguments
98
)
99
1.36k
{
100
1.36k
  yulAssert(_arguments.size() == 2, "");
101
1.36k
  Identifier const* memoryKey = std::get_if<Identifier>(&_arguments.at(0));
102
1.36k
  Identifier const* length = std::get_if<Identifier>(&_arguments.at(1));
103
104
1.36k
  if (!memoryKey || !length)
105
301
    return;
106
107
  // The costs are only correct for hashes of 32 bytes or 1 word (when rounded up).
108
1.06k
  GasMeter gasMeter{
109
1.06k
    dynamic_cast<EVMDialect const&>(m_dialect),
110
1.06k
    !m_expectedExecutionsPerDeployment,
111
1.06k
    m_expectedExecutionsPerDeployment ? *m_expectedExecutionsPerDeployment : 1
112
1.06k
  };
113
114
1.06k
  bigint costOfKeccak = gasMeter.costs(_e);
115
1.06k
  bigint costOfLiteral = gasMeter.costs(
116
1.06k
    Literal{
117
1.06k
      {},
118
1.06k
      LiteralKind::Number,
119
      // a dummy 256-bit number to represent the Keccak256 hash.
120
1.06k
      YulString{numeric_limits<u256>::max().str()},
121
1.06k
      {}
122
1.06k
    }
123
1.06k
  );
124
125
  // We skip if there are no net gas savings.
126
  // Note that for default `m_runs = 200`, the values are
127
  // `costOfLiteral = 7200` and `costOfKeccak = 9000` for runtime context.
128
  // For creation context: `costOfLiteral = 531` and `costOfKeccak = 90`.
129
1.06k
  if (costOfLiteral > costOfKeccak)
130
1.06k
    return;
131
132
0
  optional<YulString> value = memoryValue(memoryKey->name);
133
0
  if (value && inScope(*value))
134
0
  {
135
0
    optional<u256> memoryContent = valueOfIdentifier(*value);
136
0
    optional<u256> byteLength = valueOfIdentifier(length->name);
137
0
    if (memoryContent && byteLength && *byteLength <= 32)
138
0
    {
139
0
      bytes contentAsBytes = toBigEndian(*memoryContent);
140
0
      contentAsBytes.resize(static_cast<size_t>(*byteLength));
141
0
      _e = Literal{
142
0
        debugDataOf(_e),
143
0
        LiteralKind::Number,
144
0
        YulString{u256(keccak256(contentAsBytes)).str()},
145
0
        m_dialect.defaultType
146
0
      };
147
0
    }
148
0
  }
149
0
}