Coverage Report

Created: 2022-08-24 06:52

/src/solidity/libyul/optimiser/FullInliner.h
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
 * Optimiser component that performs function inlining for arbitrary functions.
20
 */
21
#pragma once
22
23
#include <libyul/ASTForward.h>
24
25
#include <libyul/optimiser/ASTCopier.h>
26
#include <libyul/optimiser/ASTWalker.h>
27
#include <libyul/optimiser/NameDispenser.h>
28
#include <libyul/optimiser/OptimiserStep.h>
29
#include <libyul/Exceptions.h>
30
31
#include <liblangutil/SourceLocation.h>
32
33
#include <optional>
34
#include <set>
35
#include <utility>
36
37
namespace solidity::yul
38
{
39
40
class NameCollector;
41
42
43
/**
44
 * Optimiser component that modifies an AST in place, inlining functions.
45
 * Expressions are expected to be split, i.e. the component will only inline
46
 * function calls that are at the root of the expression and that only contains
47
 * variables as arguments. More specifically, it will inline
48
 *  - let x1, ..., xn := f(a1, ..., am)
49
 *  - x1, ..., xn := f(a1, ..., am)
50
 * f(a1, ..., am)
51
 *
52
 * The transform changes code of the form
53
 *
54
 * function f(a, b) -> c { ... }
55
 * let z := f(x, y)
56
 *
57
 * into
58
 *
59
 * function f(a, b) -> c { ... }
60
 *
61
 * let f_a := x
62
 * let f_b := y
63
 * let f_c
64
 * code of f, with replacements: a -> f_a, b -> f_b, c -> f_c
65
 * let z := f_c
66
 *
67
 * Prerequisites: Disambiguator
68
 * More efficient if run after: Function Hoister, Expression Splitter
69
 */
70
class FullInliner: public ASTModifier
71
{
72
public:
73
  static constexpr char const* name{"FullInliner"};
74
  static void run(OptimiserStepContext& _context, Block& _ast);
75
76
  /// Inlining heuristic.
77
  /// @param _callSite the name of the function in which the function call is located.
78
  bool shallInline(FunctionCall const& _funCall, YulString _callSite);
79
80
  FunctionDefinition* function(YulString _name)
81
0
  {
82
0
    auto it = m_functions.find(_name);
83
0
    if (it != m_functions.end())
84
0
      return it->second;
85
0
    return nullptr;
86
0
  }
87
88
  /// Adds the size of _funCall to the size of _callSite. This is just
89
  /// a rough estimate that is done during inlining. The proper size
90
  /// should be determined after inlining is completed.
91
  void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
92
93
private:
94
  enum Pass { InlineTiny, InlineRest };
95
96
  FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect);
97
  void run(Pass _pass);
98
99
  /// @returns a map containing the maximum depths of a call chain starting at each
100
  /// function. For recursive functions, the value is one larger than for all others.
101
  std::map<YulString, size_t> callDepths() const;
102
103
  void updateCodeSize(FunctionDefinition const& _fun);
104
  void handleBlock(YulString _currentFunctionName, Block& _block);
105
  bool recursive(FunctionDefinition const& _fun) const;
106
107
  Pass m_pass;
108
  /// The AST to be modified. The root block itself will not be modified, because
109
  /// we store pointers to functions.
110
  Block& m_ast;
111
  std::map<YulString, FunctionDefinition*> m_functions;
112
  /// Functions not to be inlined (because they contain the ``leave`` statement).
113
  std::set<YulString> m_noInlineFunctions;
114
  /// True, if the code contains a ``memoryguard`` and we can expect to be able to move variables to memory later.
115
  bool m_hasMemoryGuard = false;
116
  /// Set of recursive functions.
117
  std::set<YulString> m_recursiveFunctions;
118
  /// Names of functions to always inline.
119
  std::set<YulString> m_singleUse;
120
  /// Variables that are constants (used for inlining heuristic)
121
  std::set<YulString> m_constants;
122
  std::map<YulString, size_t> m_functionSizes;
123
  NameDispenser& m_nameDispenser;
124
  Dialect const& m_dialect;
125
};
126
127
/**
128
 * Class that walks the AST of a block that does not contain function definitions and perform
129
 * the actual code modifications.
130
 */
131
class InlineModifier: public ASTModifier
132
{
133
public:
134
  InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName, Dialect const& _dialect):
135
    m_currentFunction(std::move(_functionName)),
136
    m_driver(_driver),
137
    m_nameDispenser(_nameDispenser),
138
    m_dialect(_dialect)
139
0
  { }
140
141
  void operator()(Block& _block) override;
142
143
private:
144
  std::optional<std::vector<Statement>> tryInlineStatement(Statement& _statement);
145
  std::vector<Statement> performInline(Statement& _statement, FunctionCall& _funCall);
146
147
  YulString m_currentFunction;
148
  FullInliner& m_driver;
149
  NameDispenser& m_nameDispenser;
150
  Dialect const& m_dialect;
151
};
152
153
/**
154
 * Creates a copy of a block that is supposed to be the body of a function.
155
 * Applies replacements to referenced variables and creates new names for
156
 * variable declarations.
157
 */
158
class BodyCopier: public ASTCopier
159
{
160
public:
161
  BodyCopier(
162
    NameDispenser& _nameDispenser,
163
    std::map<YulString, YulString> _variableReplacements
164
  ):
165
    m_nameDispenser(_nameDispenser),
166
    m_variableReplacements(std::move(_variableReplacements))
167
0
  {}
168
169
  using ASTCopier::operator ();
170
171
  Statement operator()(VariableDeclaration const& _varDecl) override;
172
  Statement operator()(FunctionDefinition const& _funDef) override;
173
174
  YulString translateIdentifier(YulString _name) override;
175
176
  NameDispenser& m_nameDispenser;
177
  std::map<YulString, YulString> m_variableReplacements;
178
};
179
180
181
}