test: some tests for push0

1. `push0_disallowed.yul`: checks if `push0()` is a valid builtin in strict Yul
2. `push0_disallowed.sol`: checks if `push0()` is a valid builtin in inline assembly
3. `push0.sol`: simple semantic test that returns 0
4. `evmone_support.sol`: tests if push0 works properly in evmone
5. Updated some bytecode too large tests to use `shanghai` as version
6. Updated various tests where `push1 0` was hardcoded in different forms / expectations on bytecode
size (`Assembler.cpp`, `GasCosts.cpp`, `SolidityCompiler.cpp`, `SolidityExpressionCompiler.cpp`)
This commit is contained in:
hrkrshnn 2023-04-09 16:40:09 +02:00
parent 802f895062
commit 41ce3feb0a
14 changed files with 130 additions and 27 deletions

View File

@ -31,7 +31,7 @@ REPODIR="$(realpath "$(dirname "$0")"/..)"
# shellcheck source=scripts/common.sh # shellcheck source=scripts/common.sh
source "${REPODIR}/scripts/common.sh" source "${REPODIR}/scripts/common.sh"
EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london paris) EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london paris shanghai)
DEFAULT_EVM=paris DEFAULT_EVM=paris
[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]] [[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]]
OPTIMIZE_VALUES=(0 1) OPTIMIZE_VALUES=(0 1)

View File

@ -105,7 +105,7 @@ EVM_VERSIONS="homestead byzantium"
if [ -z "$CI" ] if [ -z "$CI" ]
then then
EVM_VERSIONS+=" constantinople petersburg istanbul berlin london paris" EVM_VERSIONS+=" constantinople petersburg istanbul berlin london paris shanghai"
fi fi
# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer

View File

@ -124,6 +124,8 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
m_evmRevision = EVMC_LONDON; m_evmRevision = EVMC_LONDON;
else if (_evmVersion == langutil::EVMVersion::paris()) else if (_evmVersion == langutil::EVMVersion::paris())
m_evmRevision = EVMC_PARIS; m_evmRevision = EVMC_PARIS;
else if (_evmVersion == langutil::EVMVersion::shanghai())
m_evmRevision = EVMC_SHANGHAI;
else else
assertThrow(false, Exception, "Unsupported EVM version"); assertThrow(false, Exception, "Unsupported EVM version");

View File

@ -330,12 +330,16 @@ BOOST_AUTO_TEST_CASE(immutable)
checkCompilation(_assembly); checkCompilation(_assembly);
string genericPush0 = evmVersion.hasPush0() ? "5f" : "6000";
// PUSH1 0x1b v/s PUSH1 0x19
string dataOffset = evmVersion.hasPush0() ? "6019" : "601b" ;
BOOST_CHECK_EQUAL( BOOST_CHECK_EQUAL(
_assembly.assemble().toHex(), _assembly.assemble().toHex(),
// root.asm // root.asm
// assign "someImmutable" // assign "someImmutable"
"602a" // PUSH1 42 - value for someImmutable "602a" + // PUSH1 42 - value for someImmutable
"6000" // PUSH1 0 - offset of code into which to insert the immutable genericPush0 + // PUSH1 0 - offset of code into which to insert the immutable
"8181" // DUP2 DUP2 "8181" // DUP2 DUP2
"6001" // PUSH1 1 - offset of first someImmutable in sub_0 "6001" // PUSH1 1 - offset of first someImmutable in sub_0
"01" // ADD - add offset of immutable to offset of code "01" // ADD - add offset of immutable to offset of code
@ -344,13 +348,13 @@ BOOST_AUTO_TEST_CASE(immutable)
"01" // ADD - add offset of immutable to offset of code "01" // ADD - add offset of immutable to offset of code
"52" // MSTORE "52" // MSTORE
// assign "someOtherImmutable" // assign "someOtherImmutable"
"6017" // PUSH1 23 - value for someOtherImmutable "6017" + // PUSH1 23 - value for someOtherImmutable
"6000" // PUSH1 0 - offset of code into which to insert the immutable genericPush0 + // PUSH1 0 - offset of code into which to insert the immutable
"6022" // PUSH1 34 - offset of someOtherImmutable in sub_0 "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0
"01" // ADD - add offset of immutable to offset of code "01" // ADD - add offset of immutable to offset of code
"52" // MSTORE "52" // MSTORE
"6063" // PUSH1 0x63 - dataSize(sub_0) "6063" + // PUSH1 0x63 - dataSize(sub_0)
"601b" // PUSH1 0x23 - dataOffset(sub_0) dataOffset + // PUSH1 0x23 - dataOffset(sub_0)
"fe" // INVALID "fe" // INVALID
// end of root.asm // end of root.asm
// sub.asm // sub.asm

View File

@ -112,15 +112,21 @@ BOOST_AUTO_TEST_CASE(string_storage)
// Costs with 0 are cases which cannot be triggered in tests. // Costs with 0 are cases which cannot be triggered in tests.
if (evmVersion < EVMVersion::istanbul()) if (evmVersion < EVMVersion::istanbul())
CHECK_DEPLOY_GAS(0, 109241, evmVersion); CHECK_DEPLOY_GAS(0, 109241, evmVersion);
else else if (evmVersion < EVMVersion::shanghai())
CHECK_DEPLOY_GAS(0, 97697, evmVersion); CHECK_DEPLOY_GAS(0, 97697, evmVersion);
// Shanghai is cheaper due to `push0`
else
CHECK_DEPLOY_GAS(0, 97071, evmVersion);
} }
else else
{ {
if (evmVersion < EVMVersion::istanbul()) if (evmVersion < EVMVersion::istanbul())
CHECK_DEPLOY_GAS(139013, 123969, evmVersion); CHECK_DEPLOY_GAS(139013, 123969, evmVersion);
else else if (evmVersion < EVMVersion::shanghai())
CHECK_DEPLOY_GAS(123361, 110969, evmVersion); CHECK_DEPLOY_GAS(123361, 110969, evmVersion);
// Shanghai is cheaper due to `push0`
else
CHECK_DEPLOY_GAS(121493, 110969, evmVersion);
} }
} }
else if (evmVersion < EVMVersion::istanbul()) else if (evmVersion < EVMVersion::istanbul())
@ -198,7 +204,11 @@ BOOST_AUTO_TEST_CASE(single_callvaluecheck)
size_t bytecodeSizeNonpayable = m_compiler.object("Nonpayable").bytecode.size(); size_t bytecodeSizeNonpayable = m_compiler.object("Nonpayable").bytecode.size();
size_t bytecodeSizePayable = m_compiler.object("Payable").bytecode.size(); size_t bytecodeSizePayable = m_compiler.object("Payable").bytecode.size();
auto evmVersion = solidity::test::CommonOptions::get().evmVersion();
if (evmVersion < EVMVersion::shanghai())
BOOST_CHECK_EQUAL(bytecodeSizePayable - bytecodeSizeNonpayable, 26); BOOST_CHECK_EQUAL(bytecodeSizePayable - bytecodeSizeNonpayable, 26);
else
BOOST_CHECK_EQUAL(bytecodeSizePayable - bytecodeSizeNonpayable, 24);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -47,7 +47,9 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions)
bytes const& runtimeBytecode = solidity::test::bytecodeSansMetadata(compiler().runtimeObject("C").bytecode); bytes const& runtimeBytecode = solidity::test::bytecodeSansMetadata(compiler().runtimeObject("C").bytecode);
BOOST_CHECK(creationBytecode.size() >= 90); BOOST_CHECK(creationBytecode.size() >= 90);
BOOST_CHECK(creationBytecode.size() <= 120); BOOST_CHECK(creationBytecode.size() <= 120);
BOOST_CHECK(runtimeBytecode.size() >= 10); auto evmVersion = solidity::test::CommonOptions::get().evmVersion();
unsigned threshold = evmVersion.hasPush0() ? 9 : 10;
BOOST_CHECK(runtimeBytecode.size() >= threshold);
BOOST_CHECK(runtimeBytecode.size() <= 30); BOOST_CHECK(runtimeBytecode.size() <= 30);
} }

View File

@ -210,7 +210,10 @@ BOOST_AUTO_TEST_CASE(literal_false)
)"; )";
bytes code = compileFirstExpression(sourceCode); bytes code = compileFirstExpression(sourceCode);
bytes expectation({uint8_t(Instruction::PUSH1), 0x0}); bytes expectation = solidity::test::CommonOptions::get().evmVersion().hasPush0() ?
bytes{uint8_t(Instruction::PUSH0)} :
bytes{uint8_t(Instruction::PUSH1), 0x0};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -344,21 +347,27 @@ BOOST_AUTO_TEST_CASE(arithmetic)
} }
)"; )";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bool hasPush0 = solidity::test::CommonOptions::get().evmVersion().hasPush0();
bytes push0Bytes = hasPush0 ?
bytes{uint8_t(Instruction::PUSH0)} :
bytes{uint8_t(Instruction::PUSH1), 0x0};
uint8_t size = hasPush0 ? 0x65: 0x67;
bytes panic = bytes panic =
bytes{ bytes{
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::PUSH32) uint8_t(Instruction::PUSH32)
} + } +
util::fromHex("4E487B7100000000000000000000000000000000000000000000000000000000") + util::fromHex("4E487B7100000000000000000000000000000000000000000000000000000000") +
push0Bytes +
bytes{ bytes{
uint8_t(Instruction::PUSH1), 0x0,
uint8_t(Instruction::MSTORE), uint8_t(Instruction::MSTORE),
uint8_t(Instruction::PUSH1), 0x12, uint8_t(Instruction::PUSH1), 0x12,
uint8_t(Instruction::PUSH1), 0x4, uint8_t(Instruction::PUSH1), 0x4,
uint8_t(Instruction::MSTORE), uint8_t(Instruction::MSTORE),
uint8_t(Instruction::PUSH1), 0x24, uint8_t(Instruction::PUSH1), 0x24
uint8_t(Instruction::PUSH1), 0x0, } +
push0Bytes +
bytes{
uint8_t(Instruction::REVERT), uint8_t(Instruction::REVERT),
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::JUMP), uint8_t(Instruction::JUMP),
@ -405,7 +414,7 @@ BOOST_AUTO_TEST_CASE(arithmetic)
uint8_t(Instruction::DIV), uint8_t(Instruction::DIV),
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::MUL), uint8_t(Instruction::MUL),
uint8_t(Instruction::PUSH1), 0x67, uint8_t(Instruction::PUSH1), size,
uint8_t(Instruction::JUMP) uint8_t(Instruction::JUMP)
} + panic; } + panic;
else else
@ -447,7 +456,7 @@ BOOST_AUTO_TEST_CASE(arithmetic)
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::DIV), uint8_t(Instruction::DIV),
uint8_t(Instruction::MUL), uint8_t(Instruction::MUL),
uint8_t(Instruction::PUSH1), 0x67, uint8_t(Instruction::PUSH1), size,
uint8_t(Instruction::JUMP) uint8_t(Instruction::JUMP)
} + panic; } + panic;
@ -463,11 +472,17 @@ BOOST_AUTO_TEST_CASE(unary_operators)
)"; )";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bytes push0Bytes = solidity::test::CommonOptions::get().evmVersion().hasPush0() ?
bytes{uint8_t(Instruction::PUSH0)} :
bytes{uint8_t(Instruction::PUSH1), 0x0};
bytes expectation; bytes expectation;
if (solidity::test::CommonOptions::get().optimize) if (solidity::test::CommonOptions::get().optimize)
expectation = { expectation = bytes{
uint8_t(Instruction::DUP1), uint8_t(Instruction::DUP1),
uint8_t(Instruction::PUSH1), 0x0, } +
push0Bytes +
bytes{
uint8_t(Instruction::SUB), uint8_t(Instruction::SUB),
uint8_t(Instruction::NOT), uint8_t(Instruction::NOT),
uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::PUSH1), 0x2,
@ -475,10 +490,12 @@ BOOST_AUTO_TEST_CASE(unary_operators)
uint8_t(Instruction::ISZERO) uint8_t(Instruction::ISZERO)
}; };
else else
expectation = { expectation = bytes{
uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::DUP2), uint8_t(Instruction::DUP2),
uint8_t(Instruction::PUSH1), 0x0, } +
push0Bytes +
bytes{
uint8_t(Instruction::SUB), uint8_t(Instruction::SUB),
uint8_t(Instruction::NOT), uint8_t(Instruction::NOT),
uint8_t(Instruction::EQ), uint8_t(Instruction::EQ),

View File

@ -0,0 +1,31 @@
contract ShortReturn {
constructor() {
assembly {
// return(0, 32)
// PUSH1 0x20 PUSH0 RETURN
mstore(0, hex"60205ff3")
return(0, 4)
}
}
}
interface DoesItReturnZero {
function foo() external pure returns (uint256);
}
contract Test {
ShortReturn immutable shortReturn = new ShortReturn();
function bytecode() external view returns(bytes memory) {
return address(shortReturn).code;
}
function isPush0Supported() external view returns (bool) {
assert(DoesItReturnZero(address(shortReturn)).foo() == 0);
return true;
}
}
// ====
// compileViaYul: also
// EVMVersion: >=shanghai
// ----
// bytecode() -> 0x20, 4, 0x60205ff300000000000000000000000000000000000000000000000000000000
// isPush0Supported() -> true

View File

@ -0,0 +1,11 @@
contract C {
function zero() external returns (uint) {
return 0;
}
}
// ====
// compileViaYul: also
// EVMVersion: >=shanghai
// ----
// zero() -> 0

View File

@ -7,6 +7,6 @@ contract test {
} }
} }
// ==== // ====
// EVMVersion: >byzantium // EVMVersion: >=shanghai
// ---- // ----
// Warning 5574: (21-27154): Contract code size is 27192 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. // Warning 5574: (21-27154): Contract code size is 27186 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.

View File

@ -7,6 +7,6 @@ contract test {
} }
} }
// ==== // ====
// EVMVersion: >byzantium // EVMVersion: >=shanghai
// ---- // ----
// Warning 5574: (21-27154): Contract code size is 27209 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. // Warning 5574: (21-27154): Contract code size is 27205 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.

View File

@ -0,0 +1,19 @@
// Ok
contract push0 {}
contract A {
// Ok, warning about shadowing
function push0() external {}
}
contract C {
function f() external {
assembly {
// Not okay
push0()
}
}
}
// ----
// Warning 2519: (77-105): This declaration shadows an existing declaration.
// DeclarationError 4619: (205-210): Function "push0" not found.

View File

@ -0,0 +1,6 @@
// Based on ./push_disallowed.yul (which already includes push0).
{
push0()
}
// ----
// DeclarationError 4619: (72-77): Function "push0" not found.

View File

@ -381,6 +381,7 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::JUMP: case Instruction::JUMP:
case Instruction::JUMPI: case Instruction::JUMPI:
case Instruction::JUMPDEST: case Instruction::JUMPDEST:
case Instruction::PUSH0:
case Instruction::PUSH1: case Instruction::PUSH1:
case Instruction::PUSH2: case Instruction::PUSH2:
case Instruction::PUSH3: case Instruction::PUSH3: