Coverage Report

Created: 2022-08-24 06:55

/src/solidity/libevmasm/Assembly.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
#pragma once
20
21
#include <libevmasm/Instruction.h>
22
#include <liblangutil/SourceLocation.h>
23
#include <libevmasm/AssemblyItem.h>
24
#include <libevmasm/LinkerObject.h>
25
#include <libevmasm/Exceptions.h>
26
27
#include <liblangutil/DebugInfoSelection.h>
28
#include <liblangutil/EVMVersion.h>
29
30
#include <libsolutil/Common.h>
31
#include <libsolutil/Assertions.h>
32
#include <libsolutil/Keccak256.h>
33
34
#include <libsolidity/interface/OptimiserSettings.h>
35
36
#include <json/json.h>
37
38
#include <iostream>
39
#include <sstream>
40
#include <memory>
41
#include <map>
42
#include <utility>
43
44
namespace solidity::evmasm
45
{
46
47
using AssemblyPointer = std::shared_ptr<Assembly>;
48
49
class Assembly
50
{
51
public:
52
9.10k
  Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { }
53
54
3.48M
  AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
55
8.12M
  AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
56
  /// Returns a tag identified by the given name. Creates it if it does not yet exist.
57
  AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
58
137k
  AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
59
0
  bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
60
4.55k
  AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
61
0
  Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
62
0
  Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
63
0
  size_t numSubs() const { return m_subs.size(); }
64
4.55k
  AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
65
  AssemblyItem newPushLibraryAddress(std::string const& _identifier);
66
  AssemblyItem newPushImmutable(std::string const& _identifier);
67
  AssemblyItem newImmutableAssignment(std::string const& _identifier);
68
69
  AssemblyItem const& append(AssemblyItem _i);
70
137k
  AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
71
72
4.73M
  template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
solidity::evmasm::Assembly& solidity::evmasm::Assembly::operator<< <solidity::evmasm::AssemblyItem>(solidity::evmasm::AssemblyItem const&)
Line
Count
Source
72
4.73M
  template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
Unexecuted instantiation: solidity::evmasm::Assembly& solidity::evmasm::Assembly::operator<< <boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0> >(boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0> const&)
73
74
  /// Pushes the final size of the current assembly itself. Use this when the code is modified
75
  /// after compilation and CODESIZE is not an option.
76
0
  void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
77
0
  void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); }
78
0
  void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); }
79
0
  void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); }
80
81
  void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables)
82
0
  {
83
0
    append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
84
0
  }
85
86
8.03k
  AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
87
4.02M
  AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
88
0
  AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
89
120k
  AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
90
91
  /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
92
  /// on the stack. @returns the pushsub assembly item.
93
0
  AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; }
94
4.55k
  void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); }
95
  /// Pushes the offset of the subroutine.
96
4.55k
  void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
97
98
  /// Appends @a _data literally to the very end of the bytecode.
99
4.55k
  void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
100
101
  /// Returns the assembly items.
102
0
  AssemblyItems const& items() const { return m_items; }
103
104
  /// Returns the mutable assembly items. Use with care!
105
0
  AssemblyItems& items() { return m_items; }
106
107
36.2M
  int deposit() const { return m_deposit; }
108
6.35M
  void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
109
1.19M
  void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
110
0
  std::string const& name() const { return m_name; }
111
112
  /// Changes the source location used for each appended item.
113
47.6M
  void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; }
114
152k
  langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; }
115
116
  /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.
117
  LinkerObject const& assemble() const;
118
119
  struct OptimiserSettings
120
  {
121
    bool runInliner = false;
122
    bool runJumpdestRemover = false;
123
    bool runPeephole = false;
124
    bool runDeduplicate = false;
125
    bool runCSE = false;
126
    bool runConstantOptimiser = false;
127
    langutil::EVMVersion evmVersion;
128
    /// This specifies an estimate on how often each opcode in this assembly will be executed,
129
    /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
130
    size_t expectedExecutionsPerDeployment = frontend::OptimiserSettings{}.expectedExecutionsPerDeployment;
131
  };
132
133
  /// Modify and return the current assembly such that creation and execution gas usage
134
  /// is optimised according to the settings in @a _settings.
135
  Assembly& optimise(OptimiserSettings const& _settings);
136
137
  /// Create a text representation of the assembly.
138
  std::string assemblyString(
139
    langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
140
    StringMap const& _sourceCodes = StringMap()
141
  ) const;
142
  void assemblyStream(
143
    std::ostream& _out,
144
    langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
145
    std::string const& _prefix = "",
146
    StringMap const& _sourceCodes = StringMap()
147
  ) const;
148
149
  /// Create a JSON representation of the assembly.
150
  Json::Value assemblyJSON(
151
    std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
152
    bool _includeSourceList = true
153
  ) const;
154
155
  /// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
156
0
  void markAsInvalid() { m_invalid = true; }
157
158
  std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
159
  size_t encodeSubPath(std::vector<size_t> const& _subPath);
160
161
0
  bool isCreation() const { return m_creation; }
162
163
protected:
164
  /// Does the same operations as @a optimise, but should only be applied to a sub and
165
  /// returns the replaced tags. Also takes an argument containing the tags of this assembly
166
  /// that are referenced in a super-assembly.
167
  std::map<u256, u256> const& optimiseInternal(OptimiserSettings const& _settings, std::set<size_t> _tagsReferencedFromOutside);
168
169
  unsigned codeSize(unsigned subTagSize) const;
170
171
private:
172
  bool m_invalid = false;
173
174
  Assembly const* subAssemblyById(size_t _subId) const;
175
176
protected:
177
  /// 0 is reserved for exception
178
  unsigned m_usedTags = 1;
179
180
  struct NamedTagInfo
181
  {
182
    size_t id;
183
    std::optional<size_t> sourceID;
184
    size_t params;
185
    size_t returns;
186
  };
187
188
  std::map<std::string, NamedTagInfo> m_namedTags;
189
  AssemblyItems m_items;
190
  std::map<util::h256, bytes> m_data;
191
  /// Data that is appended to the very end of the contract.
192
  bytes m_auxiliaryData;
193
  std::vector<std::shared_ptr<Assembly>> m_subs;
194
  std::map<util::h256, std::string> m_strings;
195
  std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
196
  std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
197
198
  /// Map from a vector representing a path to a particular sub assembly to sub assembly id.
199
  /// This map is used only for sub-assemblies which are not direct sub-assemblies (where path is having more than one value).
200
  std::map<std::vector<size_t>, size_t> m_subPaths;
201
202
  /// Contains the tag replacements relevant for super-assemblies.
203
  /// If set, it means the optimizer has run and we will not run it again.
204
  std::optional<std::map<u256, u256>> m_tagReplacements;
205
206
  mutable LinkerObject m_assembledObject;
207
  mutable std::vector<size_t> m_tagPositionsInBytecode;
208
209
  int m_deposit = 0;
210
  /// True, if the assembly contains contract creation code.
211
  bool const m_creation = false;
212
  /// Internal name of the assembly object, only used with the Yul backend
213
  /// currently
214
  std::string m_name;
215
216
  langutil::SourceLocation m_currentSourceLocation;
217
218
public:
219
  size_t m_currentModifierDepth = 0;
220
};
221
222
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
223
0
{
224
0
  _a.assemblyStream(_out);
225
0
  return _out;
226
0
}
227
228
}