Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/backends/evm/EVMBuiltins.cpp
Line
Count
Source
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/backends/evm/EVMBuiltins.h>
20
21
#include <libyul/AST.h>
22
#include <libyul/Object.h>
23
#include <libyul/Utilities.h>
24
25
#include <libevmasm/AssemblyItem.h>
26
27
#include <libsolutil/StringUtils.h>
28
29
#include <range/v3/algorithm/all_of.hpp>
30
31
using namespace solidity;
32
using namespace solidity::yul;
33
34
namespace
35
{
36
37
BuiltinFunctionForEVM createFunction(
38
  std::string const& _name,
39
  size_t _params,
40
  size_t _returns,
41
  SideEffects _sideEffects,
42
  ControlFlowSideEffects _controlFlowSideEffects,
43
  std::vector<std::optional<LiteralKind>> _literalArguments,
44
  std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&)> _generateCode
45
)
46
1.69k
{
47
1.69k
  yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
48
49
1.69k
  BuiltinFunctionForEVM f;
50
1.69k
  f.name = _name;
51
1.69k
  f.numParameters = _params;
52
1.69k
  f.numReturns = _returns;
53
1.69k
  f.sideEffects = _sideEffects;
54
1.69k
  f.controlFlowSideEffects = _controlFlowSideEffects;
55
1.69k
  f.literalArguments = std::move(_literalArguments);
56
1.69k
  f.isMSize = false;
57
1.69k
  f.instruction = {};
58
1.69k
  f.generateCode = std::move(_generateCode);
59
1.69k
  return f;
60
1.69k
}
61
62
BuiltinFunctionForEVM instructionBuiltin(evmasm::Instruction const& _instruction, langutil::EVMVersion const& _evmVersion)
63
1.42k
{
64
1.42k
  evmasm::InstructionInfo const info = evmasm::instructionInfo(_instruction, _evmVersion);
65
1.42k
  BuiltinFunctionForEVM f;
66
1.42k
  f.name = util::toLower(info.name);
67
1.42k
  f.numParameters = static_cast<size_t>(info.args);
68
1.42k
  f.numReturns = static_cast<size_t>(info.ret);
69
1.42k
  f.sideEffects = EVMBuiltins::sideEffectsOfInstruction(_instruction);
70
1.42k
  f.controlFlowSideEffects = ControlFlowSideEffects::fromInstruction(_instruction);
71
1.42k
  f.isMSize = _instruction == evmasm::Instruction::MSIZE;
72
1.42k
  f.literalArguments.clear();
73
1.42k
  f.instruction = _instruction;
74
1.42k
  f.generateCode = [_instruction](
75
1.42k
    FunctionCall const&,
76
1.42k
    AbstractAssembly& _assembly,
77
1.42k
    BuiltinContext&
78
1.42k
  )
79
1.67M
  {
80
1.67M
    _assembly.appendInstruction(_instruction);
81
1.67M
  };
82
1.42k
  return f;
83
1.42k
}
84
85
BuiltinFunctionForEVM linkersymbolBuiltin()
86
12
{
87
12
  return createFunction(
88
12
    "linkersymbol",
89
12
    1,
90
12
    1,
91
12
    SideEffects{},
92
12
    ControlFlowSideEffects{},
93
12
    {LiteralKind::String},
94
1.54k
    [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) {
95
1.54k
      yulAssert(_call.arguments.size() == 1, "");
96
1.54k
      Expression const& arg = _call.arguments.front();
97
1.54k
      _assembly.appendLinkerSymbol(formatLiteral(std::get<Literal>(arg)));
98
1.54k
    }
99
12
  );
100
12
}
101
102
BuiltinFunctionForEVM memoryguardBuiltin()
103
12
{
104
12
  return createFunction(
105
12
    "memoryguard",
106
12
    1,
107
12
    1,
108
12
    SideEffects{},
109
12
    ControlFlowSideEffects{},
110
12
    {LiteralKind::Number},
111
23.0k
    [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) {
112
23.0k
      yulAssert(_call.arguments.size() == 1, "");
113
23.0k
      Literal const* literal = std::get_if<Literal>(&_call.arguments.front());
114
23.0k
      yulAssert(literal, "");
115
23.0k
      _assembly.appendConstant(literal->value.value());
116
23.0k
    }
117
12
  );
118
12
}
119
120
BuiltinFunctionForEVM datasizeBuiltin()
121
12
{
122
12
  return createFunction(
123
12
    "datasize",
124
12
    1,
125
12
    1,
126
12
    SideEffects{},
127
12
    ControlFlowSideEffects{},
128
12
    {LiteralKind::String},
129
6.48k
    [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context) {
130
6.48k
      yulAssert(_context.currentObject, "No object available.");
131
6.48k
      yulAssert(_call.arguments.size() == 1, "");
132
6.48k
      Expression const& arg = _call.arguments.front();
133
6.48k
      YulName const dataName (formatLiteral(std::get<Literal>(arg)));
134
6.48k
      if (_context.currentObject->name == dataName.str())
135
309
        _assembly.appendAssemblySize();
136
6.18k
      else
137
6.18k
      {
138
6.18k
        std::vector<AbstractAssembly::SubID> subIdPath =
139
6.18k
          _context.subIDs.count(dataName.str()) == 0 ?
140
0
            _context.currentObject->pathToSubObject(dataName.str()) :
141
6.18k
            std::vector{_context.subIDs.at(dataName.str())};
142
6.18k
        yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
143
6.18k
        _assembly.appendDataSize(subIdPath);
144
6.18k
      }
145
6.48k
    }
146
12
  );
147
12
}
148
149
BuiltinFunctionForEVM dataoffsetBuiltin()
150
12
{
151
12
  return createFunction("dataoffset", 1, 1, SideEffects{}, ControlFlowSideEffects{}, {LiteralKind::String}, [](
152
12
    FunctionCall const& _call,
153
12
    AbstractAssembly& _assembly,
154
12
    BuiltinContext& _context
155
5.41k
  ) {
156
5.41k
    yulAssert(_context.currentObject, "No object available.");
157
5.41k
    yulAssert(_call.arguments.size() == 1, "");
158
5.41k
    Expression const& arg = _call.arguments.front();
159
5.41k
    YulName const dataName (formatLiteral(std::get<Literal>(arg)));
160
5.41k
    if (_context.currentObject->name == dataName.str())
161
8
      _assembly.appendConstant(0);
162
5.40k
    else
163
5.40k
    {
164
5.40k
      std::vector<AbstractAssembly::SubID> subIdPath =
165
5.40k
        _context.subIDs.count(dataName.str()) == 0 ?
166
73
          _context.currentObject->pathToSubObject(dataName.str()) :
167
5.40k
          std::vector{_context.subIDs.at(dataName.str())};
168
5.40k
      yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">.");
169
5.40k
      _assembly.appendDataOffset(subIdPath);
170
5.40k
    }
171
5.41k
  });
172
12
}
173
174
BuiltinFunctionForEVM datacopyBuiltin()
175
12
{
176
12
  return createFunction(
177
12
    "datacopy",
178
12
    3,
179
12
    0,
180
12
    EVMBuiltins::sideEffectsOfInstruction(evmasm::Instruction::CODECOPY),
181
12
    ControlFlowSideEffects::fromInstruction(evmasm::Instruction::CODECOPY),
182
12
    {},
183
12
    [](
184
12
      FunctionCall const&,
185
12
      AbstractAssembly& _assembly,
186
12
      BuiltinContext&
187
157
    ) {
188
157
      _assembly.appendInstruction(evmasm::Instruction::CODECOPY);
189
157
    }
190
12
  );
191
12
}
192
193
BuiltinFunctionForEVM setimmutableBuiltin()
194
12
{
195
12
  return createFunction(
196
12
    "setimmutable",
197
12
    3,
198
12
    0,
199
12
    SideEffects{
200
12
      false,               // movable
201
12
      false,               // movableApartFromEffects
202
12
      false,               // canBeRemoved
203
12
      false,               // canBeRemovedIfNotMSize
204
12
      true,                // cannotLoop
205
12
      SideEffects::None,   // otherState
206
12
      SideEffects::None,   // storage
207
12
      SideEffects::Write,  // memory
208
12
      SideEffects::None    // transientStorage
209
12
    },
210
12
    ControlFlowSideEffects{},
211
12
    {std::nullopt, LiteralKind::String, std::nullopt},
212
12
    [](
213
12
      FunctionCall const& _call,
214
12
      AbstractAssembly& _assembly,
215
12
      BuiltinContext&
216
132
    ) {
217
132
      yulAssert(_call.arguments.size() == 3, "");
218
132
      auto const identifier = (formatLiteral(std::get<Literal>(_call.arguments[1])));
219
132
      _assembly.appendImmutableAssignment(identifier);
220
132
    }
221
12
  );
222
12
}
223
224
BuiltinFunctionForEVM loadimmutableBuiltin()
225
12
{
226
12
  return createFunction(
227
12
    "loadimmutable",
228
12
    1,
229
12
    1,
230
12
    SideEffects{},
231
12
    ControlFlowSideEffects{},
232
12
    {LiteralKind::String},
233
12
    [](
234
12
      FunctionCall const& _call,
235
12
      AbstractAssembly& _assembly,
236
12
      BuiltinContext&
237
105
    ) {
238
105
      yulAssert(_call.arguments.size() == 1, "");
239
105
      _assembly.appendImmutable(formatLiteral(std::get<Literal>(_call.arguments.front())));
240
105
    }
241
12
  );
242
12
}
243
}
244
245
EVMBuiltins::EVMBuiltins()
246
12
{
247
12
  for (auto const& [name, opcode]: evmasm::c_instructions)
248
1.81k
  {
249
1.81k
    if (
250
1.81k
      evmasm::SemanticInformation::isSwapInstruction(opcode) ||
251
1.62k
      evmasm::SemanticInformation::isDupInstruction(opcode)
252
1.81k
    )
253
384
      continue;
254
255
    // difficulty was replaced by prevrandao after london
256
1.42k
    if (opcode == evmasm::Instruction::PREVRANDAO && name == "DIFFICULTY")
257
12
      m_scopesAndFunctions.emplace_back(instruction, instructionBuiltin(opcode, langutil::EVMVersion::london()));
258
1.41k
    else
259
1.41k
      m_scopesAndFunctions.emplace_back(instruction, instructionBuiltin(opcode, langutil::EVMVersion::current()));
260
1.42k
  }
261
262
12
  m_scopesAndFunctions.emplace_back(objectAccess, linkersymbolBuiltin());
263
12
  m_scopesAndFunctions.emplace_back(objectAccess, memoryguardBuiltin());
264
265
12
  m_scopesAndFunctions.emplace_back(objectAccess, datasizeBuiltin());
266
12
  m_scopesAndFunctions.emplace_back(objectAccess, dataoffsetBuiltin());
267
12
  m_scopesAndFunctions.emplace_back(objectAccess, datacopyBuiltin());
268
12
  m_scopesAndFunctions.emplace_back(objectAccess, setimmutableBuiltin());
269
12
  m_scopesAndFunctions.emplace_back(objectAccess, loadimmutableBuiltin());
270
271
12
  static size_t constexpr verbatimPrefixLength = std::char_traits<char>::length("verbatim_");
272
12
  for (auto const& [scope, builtin]: m_scopesAndFunctions)
273
1.51k
  {
274
1.51k
    yulAssert(
275
1.51k
      builtin.name.substr(0, verbatimPrefixLength) != "verbatim_",
276
1.51k
      "Builtin functions besides verbatim should not start with the verbatim_ prefix."
277
1.51k
    );
278
1.51k
  }
279
12
}
280
281
BuiltinFunctionForEVM EVMBuiltins::createVerbatimFunction(size_t const _arguments, size_t const _returnVariables)
282
1.61k
{
283
1.61k
  BuiltinFunctionForEVM builtinFunction = createFunction(
284
1.61k
    "verbatim_" + std::to_string(_arguments) + "i_" + std::to_string(_returnVariables) + "o",
285
1.61k
    1 + _arguments,
286
1.61k
    _returnVariables,
287
1.61k
    SideEffects::worst(),
288
1.61k
    ControlFlowSideEffects::worst(), // Worst control flow side effects because verbatim can do anything.
289
1.61k
    std::vector<std::optional<LiteralKind>>{LiteralKind::String} + std::vector<std::optional<LiteralKind>>(_arguments),
290
1.61k
    [=](
291
1.61k
      FunctionCall const& _call,
292
1.61k
      AbstractAssembly& _assembly,
293
1.61k
      BuiltinContext&
294
1.61k
    ) {
295
1.23k
      yulAssert(_call.arguments.size() == (1 + _arguments), "");
296
1.23k
      Expression const& bytecode = _call.arguments.front();
297
298
1.23k
      _assembly.appendVerbatim(
299
1.23k
        util::asBytes(formatLiteral(std::get<Literal>(bytecode))),
300
1.23k
        _arguments,
301
1.23k
        _returnVariables
302
1.23k
      );
303
1.23k
    }
304
1.61k
  );
305
1.61k
  builtinFunction.isMSize = true;
306
1.61k
  return builtinFunction;
307
1.61k
}
308
309
SideEffects EVMBuiltins::sideEffectsOfInstruction(evmasm::Instruction _instruction)
310
1.44k
{
311
1.44k
  auto translate = [](evmasm::SemanticInformation::Effect _e) -> SideEffects::Effect
312
5.76k
  {
313
5.76k
    return static_cast<SideEffects::Effect>(_e);
314
5.76k
  };
315
316
1.44k
  return SideEffects{
317
1.44k
    evmasm::SemanticInformation::movable(_instruction),
318
1.44k
    evmasm::SemanticInformation::movableApartFromEffects(_instruction),
319
1.44k
    evmasm::SemanticInformation::canBeRemoved(_instruction),
320
1.44k
    evmasm::SemanticInformation::canBeRemovedIfNoMSize(_instruction),
321
1.44k
    true, // cannotLoop
322
1.44k
    translate(evmasm::SemanticInformation::otherState(_instruction)),
323
1.44k
    translate(evmasm::SemanticInformation::storage(_instruction)),
324
1.44k
    translate(evmasm::SemanticInformation::memory(_instruction)),
325
1.44k
    translate(evmasm::SemanticInformation::transientStorage(_instruction)),
326
1.44k
  };
327
1.44k
}