Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Side-effect-free Yul interpreter #15464

Open
wants to merge 78 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
36cbf76
Copy interpreter tool from test as basis
quangloc99 Sep 18, 2024
b69895e
Add new Result type
quangloc99 Sep 18, 2024
821a5e0
Change interface of the interpreter to have custom return type
quangloc99 Sep 18, 2024
8c23fcc
Implement visit function with new return type
quangloc99 Sep 18, 2024
bbc1211
Remove runExternalCall
quangloc99 Sep 18, 2024
cfd22d0
Minor comment edit
quangloc99 Sep 24, 2024
30e799c
Implement visit(Expression) for ExpressionEvaluator
quangloc99 Sep 24, 2024
e251707
Force incrementStep logic to be done at visit function
quangloc99 Sep 24, 2024
d57b0bc
Merge ExpressionEvaluator into Interpreter
quangloc99 Sep 24, 2024
2b1cf63
Reorder methods
quangloc99 Sep 24, 2024
ae58a7c
Reset expressionNestingLevel after each incrementStatementStep
quangloc99 Sep 24, 2024
109caa6
Remove redundant makeInterpreterNew function
quangloc99 Sep 24, 2024
b75de8d
Remove unused disableMemoryTrace
quangloc99 Sep 24, 2024
0880bda
Remove state with side effect from InterpreterState
quangloc99 Sep 24, 2024
17dc623
Move Interpreter Config to a separate struct
quangloc99 Sep 24, 2024
ec032d9
Remove redundant numInstance
quangloc99 Sep 24, 2024
6751981
Add recursion depth check
quangloc99 Sep 24, 2024
6d9ac08
Remove functions with side-effect from EVMInstructionInterpreter
quangloc99 Sep 24, 2024
168047b
Declare ImpureBuiltinEncountered and EVMInstructionInterpretedResult
quangloc99 Sep 24, 2024
220ab2c
Return ImpureBuiltinEncountered for all builtin with side-effect
quangloc99 Sep 24, 2024
22ccc63
Use EVMInstructionInterpretedResult in Interpreter
quangloc99 Sep 24, 2024
7662ebf
Bring back empty body and post check when visiting ForLoop
quangloc99 Sep 24, 2024
2f1f412
Remove unused function in EVMInstructionInterpreter
quangloc99 Sep 24, 2024
f505219
Move result types into a separate file
quangloc99 Sep 24, 2024
1882cc4
Add Pure* prefix
quangloc99 Sep 28, 2024
8b61c79
Move PureInterpreterState into a separate file
quangloc99 Sep 28, 2024
f745b08
Add Results and PureInterpreterState into CMakeLists
quangloc99 Sep 28, 2024
f37d2f8
Separate function definition and variable declaration in Scope
quangloc99 Sep 28, 2024
3be2783
Make Scope parent constant pointer
quangloc99 Sep 28, 2024
7775bbe
Move Scope to a separate file
quangloc99 Sep 28, 2024
73517e2
Make Scope class. Add utilities to Scope.
quangloc99 Sep 28, 2024
b3ec60f
Use new Scope class in PureInterpreter
quangloc99 Sep 28, 2024
16e046d
Create VariableValuesMap alias
quangloc99 Sep 28, 2024
2bf2150
Fix behavior of POP instruction
quangloc99 Sep 28, 2024
736831f
Add stack trace to state
quangloc99 Sep 28, 2024
d6581e5
Remove TraceLimitReached from ExecutionTerminated
quangloc99 Sep 28, 2024
5ed803c
Add dumpTraces function
quangloc99 Sep 28, 2024
861b1d4
Add execute statements function
quangloc99 Sep 28, 2024
2b1792d
Add UnlimitedLiteralEncountered result type
quangloc99 Sep 28, 2024
7fa7390
Change calculation flow for evaluateArgs
quangloc99 Sep 28, 2024
91292f6
Add move constructor for EvaluationOk
quangloc99 Sep 28, 2024
29ab4ab
Disable EvaluationOk lvalue constructor
quangloc99 Sep 28, 2024
efb2ec1
Make EvaluationOk constructor explicit
quangloc99 Sep 28, 2024
65beb35
Optimize std::vector<u256> usage
quangloc99 Sep 28, 2024
6f77a48
Copy YulInterpreterTest suite to YulPureInterpreterTest
quangloc99 Sep 28, 2024
a1a02da
Read interpreter config from test settings
quangloc99 Sep 28, 2024
eb9865b
Make PureInterpreter.run returns ExecutionResult
quangloc99 Sep 28, 2024
09b050d
Add default value for traces
quangloc99 Sep 28, 2024
d798ba3
Use new PureInterpreter in test
quangloc99 Sep 28, 2024
8c3407b
Make TraceLimitReached a ExecutionTerminated result again
quangloc99 Sep 28, 2024
5ff9bdd
Remove PureInterpreter.run method
quangloc99 Sep 29, 2024
b313be5
Add function to get allVariables from interpreter
quangloc99 Sep 29, 2024
eef65a1
Dump more data after interpreter
quangloc99 Sep 29, 2024
cb7b6d8
Register YulPureInterpreterTest
quangloc99 Sep 29, 2024
ab9cdcb
Add hex printing and sort variable names
quangloc99 Sep 29, 2024
f7fdc92
Filter and add existing interpreter tests to pure interpreterTest
quangloc99 Sep 29, 2024
c3b2ba5
Add tests for all impure instructions
quangloc99 Sep 29, 2024
3f82ab5
Add test for all pure instructions
quangloc99 Sep 29, 2024
84b6198
Change expr_nesting_depth_not_exceeded to run expression multiple time
quangloc99 Sep 29, 2024
46f8979
Add test for StepLimitReached
quangloc99 Sep 29, 2024
13d8001
Add test for impure non instructions
quangloc99 Sep 29, 2024
c1cd467
Add check for verbatim
quangloc99 Sep 29, 2024
0f1e1d5
Add verbatim test
quangloc99 Sep 29, 2024
9fcd79f
Remove redundant public:
quangloc99 Sep 29, 2024
ec0dd94
Optimize map usage
quangloc99 Sep 29, 2024
5aa7dc6
Remove redundant terminated check
quangloc99 Sep 29, 2024
a5e6d92
Remove redundant terminated result
quangloc99 Sep 29, 2024
02ea3a7
Add test for stop instruction
quangloc99 Sep 29, 2024
24c2fe4
Merge branch 'develop' into yul-side-effect-free-interpreter
quangloc99 Sep 29, 2024
4b4e463
Update tests to remove trailing space
quangloc99 Sep 29, 2024
c32754f
Fix styling for addTrace
quangloc99 Sep 29, 2024
7b2206b
Fix spelling
quangloc99 Sep 29, 2024
5289dd3
Fix linting error for generate-test.py
quangloc99 Sep 29, 2024
30538f2
Update EVMVersion in tests
quangloc99 Sep 29, 2024
a490149
Add encoding when open file when generate test
quangloc99 Sep 29, 2024
a93794a
Fix spelling Expection -> Expectation
quangloc99 Sep 29, 2024
7640a22
Remove virtual modifier from PureInterpreter functions
quangloc99 Sep 29, 2024
53a39bc
Remove unused m_state
quangloc99 Sep 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions libyul/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
312 changes: 312 additions & 0 deletions libyul/tools/interpreter/PureEVMInstructionInterpreter.cpp
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Yul interpreter module that evaluates EVM instructions.
*/

#include <libyul/tools/interpreter/PureEVMInstructionInterpreter.h>

#include <libyul/tools/interpreter/PureInterpreter.h>

#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AST.h>
#include <libyul/Utilities.h>

#include <libevmasm/Instruction.h>
#include <libevmasm/SemanticInformation.h>

#include <libsolutil/Keccak256.h>
#include <libsolutil/Numeric.h>
#include <libsolutil/picosha2.h>

#include <boost/algorithm/string.hpp>

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<boost::multiprecision::cpp_int_backend<512, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

EvaluationResult PureEVMInstructionInterpreter::eval(
evmasm::Instruction _instruction,
std::vector<u256> const& _arguments
)
{
using namespace solidity::evmasm;
using evmasm::Instruction;

auto info = instructionInfo(_instruction, m_evmVersion);
yulAssert(static_cast<size_t>(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<uint8_t>(_instruction)));
return EvaluationOk(0);
}

EvaluationResult PureEVMInstructionInterpreter::evalBuiltin(
BuiltinFunctionForEVM const& _fun,
std::vector<Expression> const& /* _arguments */, // This was required to execute some builtin.
// But all of them are impure.
std::vector<u256> 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<std::string> 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);
}

Loading