Coverage Report

Created: 2022-08-24 06:32

/src/solidity/libyul/optimiser/FunctionSpecializer.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
#include <libyul/optimiser/FunctionSpecializer.h>
20
21
#include <libyul/optimiser/ASTCopier.h>
22
#include <libyul/optimiser/CallGraphGenerator.h>
23
#include <libyul/optimiser/NameCollector.h>
24
#include <libyul/optimiser/NameDispenser.h>
25
26
#include <libyul/AST.h>
27
#include <libyul/YulString.h>
28
#include <libsolutil/CommonData.h>
29
30
#include <range/v3/algorithm/any_of.hpp>
31
#include <range/v3/view/enumerate.hpp>
32
33
#include <variant>
34
35
using namespace std;
36
using namespace solidity::util;
37
using namespace solidity::yul;
38
39
FunctionSpecializer::LiteralArguments FunctionSpecializer::specializableArguments(
40
  FunctionCall const& _f
41
)
42
0
{
43
0
  auto heuristic = [&](Expression const& _e) -> optional<Expression>
44
0
  {
45
0
    if (holds_alternative<Literal>(_e))
46
0
      return ASTCopier{}.translate(_e);
47
0
    return nullopt;
48
0
  };
49
50
0
  return applyMap(_f.arguments, heuristic);
51
0
}
52
53
void FunctionSpecializer::operator()(FunctionCall& _f)
54
0
{
55
0
  ASTModifier::operator()(_f);
56
57
  // TODO When backtracking is implemented, the restriction of recursive functions can be lifted.
58
0
  if (
59
0
    m_dialect.builtin(_f.functionName.name) ||
60
0
    m_recursiveFunctions.count(_f.functionName.name)
61
0
  )
62
0
    return;
63
64
0
  LiteralArguments arguments = specializableArguments(_f);
65
66
0
  if (ranges::any_of(arguments, [](auto& _a) { return _a.has_value(); }))
67
0
  {
68
0
    YulString oldName = move(_f.functionName.name);
69
0
    auto newName = m_nameDispenser.newName(oldName);
70
71
0
    m_oldToNewMap[oldName].emplace_back(make_pair(newName, arguments));
72
73
0
    _f.functionName.name = newName;
74
0
    _f.arguments = util::filter(
75
0
      _f.arguments,
76
0
      applyMap(arguments, [](auto& _a) { return !_a; })
77
0
    );
78
0
  }
79
0
}
80
81
FunctionDefinition FunctionSpecializer::specialize(
82
  FunctionDefinition const& _f,
83
  YulString _newName,
84
  FunctionSpecializer::LiteralArguments _arguments
85
)
86
0
{
87
0
  yulAssert(_arguments.size() == _f.parameters.size(), "");
88
89
0
  map<YulString, YulString> translatedNames = applyMap(
90
0
    NameCollector{_f, NameCollector::OnlyVariables}.names(),
91
0
    [&](auto& _name) -> pair<YulString, YulString>
92
0
    {
93
0
      return make_pair(_name, m_nameDispenser.newName(_name));
94
0
    },
95
0
    map<YulString, YulString>{}
96
0
  );
97
98
0
  FunctionDefinition newFunction = get<FunctionDefinition>(FunctionCopier{translatedNames}(_f));
99
100
  // Function parameters that will be specialized inside the body are converted into variable
101
  // declarations.
102
0
  vector<Statement> missingVariableDeclarations;
103
0
  for (auto&& [index, argument]: _arguments | ranges::views::enumerate)
104
0
    if (argument)
105
0
      missingVariableDeclarations.emplace_back(
106
0
        VariableDeclaration{
107
0
          _f.debugData,
108
0
          vector<TypedName>{newFunction.parameters[index]},
109
0
          make_unique<Expression>(move(*argument))
110
0
        }
111
0
      );
112
113
0
  newFunction.body.statements =
114
0
    move(missingVariableDeclarations) + move(newFunction.body.statements);
115
116
  // Only take those indices that cannot be specialized, i.e., whose value is `nullopt`.
117
0
  newFunction.parameters =
118
0
    util::filter(
119
0
      newFunction.parameters,
120
0
      applyMap(_arguments, [&](auto const& _v) { return !_v; })
121
0
    );
122
123
0
  newFunction.name = move(_newName);
124
125
0
  return newFunction;
126
0
}
127
128
void FunctionSpecializer::run(OptimiserStepContext& _context, Block& _ast)
129
0
{
130
0
  FunctionSpecializer f{
131
0
    CallGraphGenerator::callGraph(_ast).recursiveFunctions(),
132
0
    _context.dispenser,
133
0
    _context.dialect
134
0
  };
135
0
  f(_ast);
136
137
0
  iterateReplacing(_ast.statements, [&](Statement& _s) -> optional<vector<Statement>>
138
0
  {
139
0
    if (holds_alternative<FunctionDefinition>(_s))
140
0
    {
141
0
      auto& functionDefinition = get<FunctionDefinition>(_s);
142
143
0
      if (f.m_oldToNewMap.count(functionDefinition.name))
144
0
      {
145
0
        vector<Statement> out = applyMap(
146
0
          f.m_oldToNewMap.at(functionDefinition.name),
147
0
          [&](auto& _p) -> Statement
148
0
          {
149
0
            return f.specialize(functionDefinition, move(_p.first), move(_p.second));
150
0
          }
151
0
        );
152
0
        return move(out) + make_vector<Statement>(move(functionDefinition));
153
0
      }
154
0
    }
155
156
0
    return nullopt;
157
0
  });
158
0
}