diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index f621b2089e9b..e403d9193881 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -197,6 +197,16 @@ add_library(yul
optimiser/VarDeclInitializer.h
optimiser/VarNameCleaner.cpp
optimiser/VarNameCleaner.h
+ tools/interpreter/Results.h
+ tools/interpreter/PureInterpreterState.h
+ tools/interpreter/PureInterpreterState.cpp
+ tools/interpreter/Scope.h
+ tools/interpreter/Scope.cpp
+ tools/interpreter/types.h
+ tools/interpreter/PureInterpreter.cpp
+ tools/interpreter/PureInterpreter.h
+ tools/interpreter/PureEVMInstructionInterpreter.cpp
+ tools/interpreter/PureEVMInstructionInterpreter.h
)
target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil fmt::fmt-header-only)
diff --git a/libyul/tools/interpreter/PureEVMInstructionInterpreter.cpp b/libyul/tools/interpreter/PureEVMInstructionInterpreter.cpp
new file mode 100644
index 000000000000..e3947eaf39f7
--- /dev/null
+++ b/libyul/tools/interpreter/PureEVMInstructionInterpreter.cpp
@@ -0,0 +1,312 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+// SPDX-License-Identifier: GPL-3.0
+/**
+ * Yul interpreter module that evaluates EVM instructions.
+ */
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+using namespace solidity;
+using namespace solidity::evmasm;
+using namespace solidity::yul;
+using namespace solidity::yul::tools::interpreter;
+
+using solidity::util::h160;
+using solidity::util::h256;
+
+using u512 = boost::multiprecision::number>;
+
+EvaluationResult PureEVMInstructionInterpreter::eval(
+ evmasm::Instruction _instruction,
+ std::vector const& _arguments
+)
+{
+ using namespace solidity::evmasm;
+ using evmasm::Instruction;
+
+ auto info = instructionInfo(_instruction, m_evmVersion);
+ yulAssert(static_cast(info.args) == _arguments.size(), "");
+
+ auto const& arg = _arguments;
+ switch (_instruction)
+ {
+ case Instruction::STOP:
+ return ExplicitlyTerminated();
+ // --------------- arithmetic ---------------
+ case Instruction::ADD:
+ return EvaluationOk(arg[0] + arg[1]);
+ case Instruction::MUL:
+ return EvaluationOk(arg[0] * arg[1]);
+ case Instruction::SUB:
+ return EvaluationOk(arg[0] - arg[1]);
+ case Instruction::DIV:
+ return EvaluationOk(arg[1] == 0 ? 0 : arg[0] / arg[1]);
+ case Instruction::SDIV:
+ return EvaluationOk(arg[1] == 0 ? 0 : s2u(u2s(arg[0]) / u2s(arg[1])));
+ case Instruction::MOD:
+ return EvaluationOk(arg[1] == 0 ? 0 : arg[0] % arg[1]);
+ case Instruction::SMOD:
+ return EvaluationOk(arg[1] == 0 ? 0 : s2u(u2s(arg[0]) % u2s(arg[1])));
+ case Instruction::EXP:
+ return EvaluationOk(exp256(arg[0], arg[1]));
+ case Instruction::NOT:
+ return EvaluationOk(~arg[0]);
+ case Instruction::LT:
+ return EvaluationOk(arg[0] < arg[1] ? u256(1) : u256(0));
+ case Instruction::GT:
+ return EvaluationOk(arg[0] > arg[1] ? u256(1) : u256(0));
+ case Instruction::SLT:
+ return EvaluationOk(u2s(arg[0]) < u2s(arg[1]) ? u256(1) : u256(0));
+ case Instruction::SGT:
+ return EvaluationOk(u2s(arg[0]) > u2s(arg[1]) ? u256(1) : u256(0));
+ case Instruction::EQ:
+ return EvaluationOk(arg[0] == arg[1] ? u256(1) : u256(0));
+ case Instruction::ISZERO:
+ return EvaluationOk(arg[0] == 0 ? u256(1) : u256(0));
+ case Instruction::AND:
+ return EvaluationOk(arg[0] & arg[1]);
+ case Instruction::OR:
+ return EvaluationOk(arg[0] | arg[1]);
+ case Instruction::XOR:
+ return EvaluationOk(arg[0] ^ arg[1]);
+ case Instruction::BYTE:
+ return EvaluationOk(arg[0] >= 32 ? 0 : (arg[1] >> unsigned(8 * (31 - arg[0]))) & 0xff);
+ case Instruction::SHL:
+ return EvaluationOk(arg[0] > 255 ? 0 : (arg[1] << unsigned(arg[0])));
+ case Instruction::SHR:
+ return EvaluationOk(arg[0] > 255 ? 0 : (arg[1] >> unsigned(arg[0])));
+ case Instruction::SAR:
+ {
+ static u256 const hibit = u256(1) << 255;
+ if (arg[0] >= 256)
+ return EvaluationOk(arg[1] & hibit ? u256(-1) : 0);
+ else
+ {
+ unsigned amount = unsigned(arg[0]);
+ u256 v = arg[1] >> amount;
+ if (arg[1] & hibit)
+ v |= u256(-1) << (256 - amount);
+ return EvaluationOk(v);
+ }
+ }
+ case Instruction::ADDMOD:
+ return EvaluationOk(arg[2] == 0 ? 0 : u256((u512(arg[0]) + u512(arg[1])) % arg[2]));
+ case Instruction::MULMOD:
+ return EvaluationOk(arg[2] == 0 ? 0 : u256((u512(arg[0]) * u512(arg[1])) % arg[2]));
+ case Instruction::SIGNEXTEND:
+ if (arg[0] >= 31)
+ return EvaluationOk(arg[1]);
+ else
+ {
+ unsigned testBit = unsigned(arg[0]) * 8 + 7;
+ u256 ret = arg[1];
+ u256 mask = ((u256(1) << testBit) - 1);
+ if (boost::multiprecision::bit_test(ret, testBit))
+ ret |= ~mask;
+ else
+ ret &= mask;
+ return EvaluationOk(ret);
+ }
+ // --------------- blockchain stuff ---------------
+ case Instruction::KECCAK256:
+ case Instruction::ADDRESS:
+ case Instruction::BALANCE:
+ case Instruction::SELFBALANCE:
+ case Instruction::ORIGIN:
+ case Instruction::CALLER:
+ case Instruction::CALLVALUE:
+ case Instruction::CALLDATALOAD:
+ case Instruction::CALLDATASIZE:
+ case Instruction::CALLDATACOPY:
+ case Instruction::CODESIZE:
+ case Instruction::CODECOPY:
+ case Instruction::GASPRICE:
+ case Instruction::CHAINID:
+ case Instruction::BASEFEE:
+ case Instruction::BLOBHASH:
+ case Instruction::BLOBBASEFEE:
+ case Instruction::EXTCODESIZE:
+ case Instruction::EXTCODEHASH:
+ case Instruction::EXTCODECOPY:
+ case Instruction::RETURNDATASIZE:
+ case Instruction::RETURNDATACOPY:
+ case Instruction::MCOPY:
+ case Instruction::BLOCKHASH:
+ case Instruction::COINBASE:
+ case Instruction::TIMESTAMP:
+ case Instruction::NUMBER:
+ case Instruction::PREVRANDAO:
+ case Instruction::GASLIMIT:
+ return ImpureBuiltinEncountered();
+ // --------------- memory / storage / logs ---------------
+ case Instruction::MLOAD:
+ case Instruction::MSTORE:
+ case Instruction::MSTORE8:
+ case Instruction::SLOAD:
+ case Instruction::SSTORE:
+ case Instruction::PC:
+ case Instruction::MSIZE:
+ case Instruction::GAS:
+ case Instruction::LOG0:
+ case Instruction::LOG1:
+ case Instruction::LOG2:
+ case Instruction::LOG3:
+ case Instruction::LOG4:
+ case Instruction::TLOAD:
+ case Instruction::TSTORE:
+ return ImpureBuiltinEncountered();
+ // --------------- calls ---------------
+ case Instruction::CREATE:
+ case Instruction::CREATE2:
+ case Instruction::CALL:
+ case Instruction::CALLCODE:
+ case Instruction::DELEGATECALL:
+ case Instruction::STATICCALL:
+ case Instruction::RETURN:
+ case Instruction::REVERT:
+ case Instruction::INVALID:
+ case Instruction::SELFDESTRUCT:
+ return ImpureBuiltinEncountered();
+
+ case Instruction::POP:
+ return EvaluationOk();
+ // --------------- invalid in strict assembly ---------------
+ case Instruction::JUMP:
+ case Instruction::JUMPI:
+ case Instruction::JUMPDEST:
+ case Instruction::PUSH0:
+ case Instruction::PUSH1:
+ case Instruction::PUSH2:
+ case Instruction::PUSH3:
+ case Instruction::PUSH4:
+ case Instruction::PUSH5:
+ case Instruction::PUSH6:
+ case Instruction::PUSH7:
+ case Instruction::PUSH8:
+ case Instruction::PUSH9:
+ case Instruction::PUSH10:
+ case Instruction::PUSH11:
+ case Instruction::PUSH12:
+ case Instruction::PUSH13:
+ case Instruction::PUSH14:
+ case Instruction::PUSH15:
+ case Instruction::PUSH16:
+ case Instruction::PUSH17:
+ case Instruction::PUSH18:
+ case Instruction::PUSH19:
+ case Instruction::PUSH20:
+ case Instruction::PUSH21:
+ case Instruction::PUSH22:
+ case Instruction::PUSH23:
+ case Instruction::PUSH24:
+ case Instruction::PUSH25:
+ case Instruction::PUSH26:
+ case Instruction::PUSH27:
+ case Instruction::PUSH28:
+ case Instruction::PUSH29:
+ case Instruction::PUSH30:
+ case Instruction::PUSH31:
+ case Instruction::PUSH32:
+ case Instruction::DUP1:
+ case Instruction::DUP2:
+ case Instruction::DUP3:
+ case Instruction::DUP4:
+ case Instruction::DUP5:
+ case Instruction::DUP6:
+ case Instruction::DUP7:
+ case Instruction::DUP8:
+ case Instruction::DUP9:
+ case Instruction::DUP10:
+ case Instruction::DUP11:
+ case Instruction::DUP12:
+ case Instruction::DUP13:
+ case Instruction::DUP14:
+ case Instruction::DUP15:
+ case Instruction::DUP16:
+ case Instruction::SWAP1:
+ case Instruction::SWAP2:
+ case Instruction::SWAP3:
+ case Instruction::SWAP4:
+ case Instruction::SWAP5:
+ case Instruction::SWAP6:
+ case Instruction::SWAP7:
+ case Instruction::SWAP8:
+ case Instruction::SWAP9:
+ case Instruction::SWAP10:
+ case Instruction::SWAP11:
+ case Instruction::SWAP12:
+ case Instruction::SWAP13:
+ case Instruction::SWAP14:
+ case Instruction::SWAP15:
+ case Instruction::SWAP16:
+ {
+ yulAssert(false, "");
+ return EvaluationOk(0);
+ }
+ }
+
+ yulAssert(false, "Unknown instruction with opcode " + std::to_string(static_cast(_instruction)));
+ return EvaluationOk(0);
+}
+
+EvaluationResult PureEVMInstructionInterpreter::evalBuiltin(
+ BuiltinFunctionForEVM const& _fun,
+ std::vector const& /* _arguments */, // This was required to execute some builtin.
+ // But all of them are impure.
+ std::vector const& _evaluatedArguments
+)
+{
+ if (_fun.instruction)
+ return eval(*_fun.instruction, _evaluatedArguments);
+
+ std::string fun = _fun.name.str();
+ bool isVerbatim = boost::starts_with(fun, "verbatim");
+ if (isVerbatim)
+ return ImpureBuiltinEncountered();
+
+ static std::set const NON_INSTRUCTION_BUILTIN_NAME = {
+ "datasize",
+ "dataoffset",
+ "datacopy",
+ "memoryguard",
+ "loadimmutable",
+ "setimmutable",
+ "linkersymbol"
+ };
+ if (NON_INSTRUCTION_BUILTIN_NAME.count(fun))
+ return ImpureBuiltinEncountered();
+
+ yulAssert(false, "Unknown builtin: " + fun);
+ return EvaluationOk(0);
+}
+
diff --git a/libyul/tools/interpreter/PureEVMInstructionInterpreter.h b/libyul/tools/interpreter/PureEVMInstructionInterpreter.h
new file mode 100644
index 000000000000..c720ed87b317
--- /dev/null
+++ b/libyul/tools/interpreter/PureEVMInstructionInterpreter.h
@@ -0,0 +1,76 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+// SPDX-License-Identifier: GPL-3.0
+/**
+ * Yul interpreter module that evaluates EVM instructions.
+ */
+
+#pragma once
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+
+namespace solidity::evmasm
+{
+enum class Instruction: uint8_t;
+}
+
+namespace solidity::yul
+{
+class YulString;
+struct BuiltinFunctionForEVM;
+}
+
+namespace solidity::yul::tools::interpreter
+{
+
+/**
+ * Interprets EVM instructions based on the current state without side-effect.
+ */
+class PureEVMInstructionInterpreter
+{
+public:
+ explicit PureEVMInstructionInterpreter(langutil::EVMVersion _evmVersion):
+ m_evmVersion(_evmVersion)
+ {}
+
+ /// Evaluate instruction
+ EvaluationResult eval(evmasm::Instruction _instruction, std::vector const& _arguments);
+
+ /// Evaluate builtin function
+ EvaluationResult evalBuiltin(
+ BuiltinFunctionForEVM const& _fun,
+ std::vector const& _arguments,
+ std::vector const& _evaluatedArguments
+ );
+
+private:
+
+ langutil::EVMVersion m_evmVersion;
+};
+
+} // solidity::yul::test
diff --git a/libyul/tools/interpreter/PureInterpreter.cpp b/libyul/tools/interpreter/PureInterpreter.cpp
new file mode 100644
index 000000000000..11dcf1f2d089
--- /dev/null
+++ b/libyul/tools/interpreter/PureInterpreter.cpp
@@ -0,0 +1,360 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+// SPDX-License-Identifier: GPL-3.0
+/**
+ * Yul interpreter.
+ */
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+
+#include
+#include
+
+using namespace solidity;
+using namespace solidity::yul;
+using namespace solidity::yul::tools::interpreter;
+
+using solidity::util::h256;
+
+ExecutionResult PureInterpreter::operator()(ExpressionStatement const& _expressionStatement)
+{
+ EvaluationResult res = evaluate(_expressionStatement.expression, 0);
+ if (auto* terminated = std::get_if(&res)) return *terminated;
+ return ExecutionOk{ ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(Assignment const& _assignment)
+{
+ solAssert(_assignment.value, "");
+ EvaluationResult evalRes = evaluate(*_assignment.value, _assignment.variableNames.size());
+ if (auto* terminated = std::get_if(&evalRes)) return *terminated;
+
+ std::vector const& values = std::move(std::get(evalRes).values);
+ for (size_t i = 0; i < values.size(); ++i)
+ {
+ YulName varName = _assignment.variableNames.at(i).name;
+ auto [_, isNew] = m_variables.insert_or_assign(varName, values.at(i));
+ solAssert(!isNew, "");
+ }
+ return ExecutionOk { ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(VariableDeclaration const& _declaration)
+{
+ std::vector values;
+ if (_declaration.value)
+ {
+ EvaluationResult evalRes = evaluate(*_declaration.value, _declaration.variables.size());
+ if (auto* terminated = std::get_if(&evalRes)) return *terminated;
+ values = std::move(std::get(evalRes).values);
+ }
+ else
+ {
+ values.assign(_declaration.variables.size(), 0);
+ }
+
+ solAssert(values.size() == _declaration.variables.size(), "");
+ for (size_t i = 0; i < values.size(); ++i)
+ {
+ YulName varName = _declaration.variables.at(i).name;
+ auto [_, isNew] = m_variables.insert_or_assign(varName, values.at(i));
+ solAssert(isNew, "");
+ m_scope->addDeclaredVariable(varName);
+ }
+ return ExecutionOk { ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(If const& _if)
+{
+ solAssert(_if.condition, "");
+ EvaluationResult conditionRes = evaluate(*_if.condition, 1);
+ if (auto* terminated = std::get_if(&conditionRes)) return *terminated;
+
+ if (std::get(conditionRes).values.at(0) != 0)
+ return (*this)(_if.body);
+ return ExecutionOk { ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(Switch const& _switch)
+{
+ solAssert(_switch.expression, "");
+ solAssert(!_switch.cases.empty(), "");
+
+ EvaluationResult expressionRes = evaluate(*_switch.expression, 1);
+ if (auto* terminated = std::get_if(&expressionRes)) return *terminated;
+
+ u256 val = std::get(expressionRes).values.at(0);
+ for (auto const& c: _switch.cases)
+ {
+ bool caseMatched = false;
+ // Default case has to be last.
+ if (!c.value) caseMatched = true;
+ else
+ {
+ EvaluationResult caseRes = evaluate(*c.value, 1);
+ if (auto* terminated = std::get_if(&caseRes)) return *terminated;
+ caseMatched = std::get(caseRes).values.at(0) == val;
+ }
+ if (caseMatched) return (*this)(c.body);
+ }
+ return ExecutionOk { ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(FunctionDefinition const&)
+{
+ return ExecutionOk{ ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(ForLoop const& _forLoop)
+{
+ solAssert(_forLoop.condition, "");
+
+ enterScope(_forLoop.pre);
+ ScopeGuard g([this]{ leaveScope(); });
+
+ {
+ ExecutionResult execRes = execute(_forLoop.pre.statements);
+ if (execRes == ExecutionResult(ExecutionOk { ControlFlowState::Leave }))
+ return execRes;
+ }
+ while (true)
+ {
+ {
+ EvaluationResult conditionRes = evaluate(*_forLoop.condition, 1);
+ if (auto* terminated = std::get_if(&conditionRes)) return *terminated;
+ if (std::get(conditionRes).values.at(0) == 0)
+ break;
+ }
+
+ // Increment step for each loop iteration for loops with
+ // an empty body and post blocks to prevent a deadlock.
+ if (_forLoop.body.statements.size() == 0 && _forLoop.post.statements.size() == 0)
+ if (auto terminated = incrementStatementStep()) return *terminated;
+
+ {
+ ExecutionResult bodyRes = (*this)(_forLoop.body);
+ if (
+ std::holds_alternative(bodyRes) ||
+ bodyRes == ExecutionResult(ExecutionOk{ ControlFlowState::Leave })
+ ) return bodyRes;
+
+ if (bodyRes == ExecutionResult(ExecutionOk{ ControlFlowState::Break }))
+ return ExecutionOk { ControlFlowState::Default };
+ }
+
+ {
+ ExecutionResult postRes = (*this)(_forLoop.post);
+ if (
+ std::holds_alternative(postRes) ||
+ postRes == ExecutionResult(ExecutionOk{ ControlFlowState::Leave })
+ ) return postRes;
+ }
+ }
+ return ExecutionOk { ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::operator()(Break const&)
+{
+ return ExecutionOk{ ControlFlowState::Break };
+}
+
+ExecutionResult PureInterpreter::operator()(Continue const&)
+{
+ return ExecutionOk{ ControlFlowState::Continue };
+}
+
+ExecutionResult PureInterpreter::operator()(Leave const&)
+{
+ return ExecutionOk{ ControlFlowState::Leave };
+}
+
+ExecutionResult PureInterpreter::operator()(Block const& _block)
+{
+ enterScope(_block);
+ ScopeGuard guard([this] { leaveScope(); });
+
+ return execute(_block.statements);
+}
+
+ExecutionResult PureInterpreter::execute(std::vector const& _statements)
+{
+ for (auto const& statement: _statements)
+ {
+ ExecutionResult statementRes = visit(statement);
+ if (statementRes != ExecutionResult(ExecutionOk {ControlFlowState::Default}))
+ return statementRes;
+ }
+ return ExecutionOk{ ControlFlowState::Default };
+}
+
+ExecutionResult PureInterpreter::visit(Statement const& _st)
+{
+ if (auto terminated = incrementStatementStep()) return *terminated;
+ return std::visit(*this, _st);
+}
+
+EvaluationResult PureInterpreter::operator()(Literal const& _literal)
+{
+ return EvaluationOk(_literal.value.value());
+}
+
+EvaluationResult PureInterpreter::operator()(Identifier const& _identifier)
+{
+ auto it = m_variables.find(_identifier.name);
+ solAssert(it != m_variables.end(), "");
+ return EvaluationOk(it->second);
+}
+
+EvaluationResult PureInterpreter::operator()(FunctionCall const& _funCall)
+{
+ std::vector> const* literalArguments = nullptr;
+ if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name))
+ if (!builtin->literalArguments.empty())
+ literalArguments = &builtin->literalArguments;
+ EvaluationResult argsRes = evaluateArgs(_funCall.arguments, literalArguments);
+ if (auto* terminated = std::get_if(&argsRes)) return *terminated;
+
+ std::vector argsValues = std::move(std::get(argsRes).values);
+
+ if (EVMDialect const* dialect = dynamic_cast(&m_dialect))
+ {
+ if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
+ {
+ PureEVMInstructionInterpreter interpreter(dialect->evmVersion());
+ return interpreter.evalBuiltin(*fun, _funCall.arguments, argsValues);
+ }
+ }
+
+ FunctionDefinition const& fun = m_scope->getFunction(_funCall.functionName.name);
+
+ yulAssert(argsValues.size() == fun.parameters.size(), "");
+ VariableValuesMap variables;
+ for (size_t i = 0; i < fun.parameters.size(); ++i)
+ variables[fun.parameters.at(i).name] = argsValues.at(i);
+ for (size_t i = 0; i < fun.returnVariables.size(); ++i)
+ variables[fun.returnVariables.at(i).name] = 0;
+
+ std::unique_ptr interpreter = makeInterpreterCopy(std::move(variables));
+
+ if (auto terminated = m_state.addTrace(fun, argsValues)) return *terminated;
+
+ ExecutionResult funcBodyRes = (*interpreter)(fun.body);
+ if (auto* terminated = std::get_if(&funcBodyRes)) return *terminated;
+
+ std::vector returnedValues;
+ returnedValues.reserve(fun.returnVariables.size());
+ for (auto const& retVar: fun.returnVariables)
+ returnedValues.emplace_back(interpreter->valueOfVariable(retVar.name));
+
+ if (auto terminated = m_state.addTrace(fun, returnedValues)) return *terminated;
+
+ return EvaluationOk(std::move(returnedValues));
+}
+
+EvaluationResult PureInterpreter::visit(Expression const& _st)
+{
+ if (auto terminated = incrementExpressionStep()) return *terminated;
+ return std::visit(*this, _st);
+}
+
+EvaluationResult PureInterpreter::evaluate(Expression const& _expression, size_t _numReturnVars)
+{
+ EvaluationResult res = visit(_expression);
+ if (auto* resOk = std::get_if(&res))
+ yulAssert(resOk->values.size() == _numReturnVars, "");
+
+ return res;
+}
+
+EvaluationResult PureInterpreter::evaluateArgs(
+ std::vector const& _expr,
+ std::vector> const* _literalArguments
+)
+{
+ std::vector values(_expr.size());
+
+ /// Function arguments are evaluated in reverse.
+ for (size_t i = _expr.size(); i-- > 0; )
+ {
+ auto const& expr = _expr[i];
+ bool isLiteral = _literalArguments && _literalArguments->at(i);
+ if (!isLiteral)
+ {
+ EvaluationResult exprRes = evaluate(expr, 1);
+ if (auto* terminated = std::get_if(&exprRes)) return *terminated;
+ std::vector const& exprValues = std::get(exprRes).values;
+ values[i] = exprValues.at(0);
+ }
+ else
+ {
+ if (std::get(expr).value.unlimited())
+ return UnlimitedLiteralEncountered();
+ else
+ values[i] = std::get(expr).value.value();
+ }
+ }
+ return EvaluationOk(std::move(values));
+}
+
+void PureInterpreter::enterScope(Block const& _block)
+{
+ m_scope = m_scope->getSubscope(_block);
+}
+
+void PureInterpreter::leaveScope()
+{
+ m_scope->cleanupVariables(m_variables);
+ m_scope = m_scope->parent();
+ yulAssert(m_scope, "");
+}
+
+std::optional PureInterpreter::incrementStatementStep()
+{
+ m_state.numSteps++;
+ if (m_state.config.maxSteps > 0 && m_state.numSteps >= m_state.config.maxSteps)
+ return StepLimitReached();
+
+ // Checking recursion depth here because we are sure that a statement
+ // inside the body evaluated.
+ if (m_state.config.maxRecursionDepth > 0 && m_recursionDepth > m_state.config.maxRecursionDepth)
+ return RecursionDepthLimitReached();
+
+ // Reset m_expressionNestingLevel, preparing for new expression.
+ m_expressionNestingLevel = 0;
+ return std::nullopt;
+}
+
+std::optional PureInterpreter::incrementExpressionStep()
+{
+ m_expressionNestingLevel++;
+ if (m_state.config.maxExprNesting > 0 && m_expressionNestingLevel > m_state.config.maxExprNesting)
+ return ExpressionNestingLimitReached();
+ return std::nullopt;
+}
diff --git a/libyul/tools/interpreter/PureInterpreter.h b/libyul/tools/interpreter/PureInterpreter.h
new file mode 100644
index 000000000000..44ed99a817e3
--- /dev/null
+++ b/libyul/tools/interpreter/PureInterpreter.h
@@ -0,0 +1,148 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
+*/
+// SPDX-License-Identifier: GPL-3.0
+/**
+ * Yul interpreter.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+#include