Merge pull request #12205 from ethereum/optimizeextocedsizecheck

Skip extcodesize check if return data is expected.
This commit is contained in:
chriseth 2021-11-08 16:05:03 +01:00 committed by GitHub
commit 4f8719326c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 315 additions and 198 deletions

View File

@ -5,6 +5,7 @@ Language Features:
Compiler Features: Compiler Features:
* Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist.
* Commandline Interface: Accept nested brackets in step sequences passed to ``--yul-optimizations``. * Commandline Interface: Accept nested brackets in step sequences passed to ``--yul-optimizations``.
* Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code. * Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
* Commandline Interface: Use different colors when printing errors, warnings and infos. * Commandline Interface: Use different colors when printing errors, warnings and infos.

View File

@ -114,10 +114,19 @@ otherwise, the ``value`` option would not be available.
Due to the fact that the EVM considers a call to a non-existing contract to Due to the fact that the EVM considers a call to a non-existing contract to
always succeed, Solidity uses the ``extcodesize`` opcode to check that always succeed, Solidity uses the ``extcodesize`` opcode to check that
the contract that is about to be called actually exists (it contains code) the contract that is about to be called actually exists (it contains code)
and causes an exception if it does not. and causes an exception if it does not. This check is skipped if the return
data will be decoded after the call and thus the ABI decoder will catch the
case of a non-existing contract.
Note that this check is not performed in case of :ref:`low-level calls <address_related>` which Note that this check is not performed in case of :ref:`low-level calls <address_related>` which
operate on addresses rather than contract instances. operate on addresses rather than contract instances.
.. note::
Be careful when using high-level calls to
:ref:`precompiled contracts <precompiledContracts>`,
since the compiler considers them non-existing according to the
above logic even though they execute code and can return data.
Function calls also cause exceptions if the called contract itself Function calls also cause exceptions if the called contract itself
throws an exception or goes out of gas. throws an exception or goes out of gas.

View File

@ -565,3 +565,24 @@ contracts, the Ether is forever lost.
If you want to deactivate your contracts, you should instead **disable** them If you want to deactivate your contracts, you should instead **disable** them
by changing some internal state which causes all functions to revert. This by changing some internal state which causes all functions to revert. This
makes it impossible to use the contract, as it returns Ether immediately. makes it impossible to use the contract, as it returns Ether immediately.
.. index:: ! precompiled contracts, ! precompiles, ! contract;precompiled
.. _precompiledContracts:
Precompiled Contracts
=====================
There is a small set of contract addresses that are special:
The address range between ``1`` and (including) ``8`` contains
"precompiled contracts" that can be called as any other contract
but their behaviour (and their gas consumption) is not defined
by EVM code stored at that address (they do not contain code)
but instead is implemented in the EVM execution environment itself.
Different EVM-compatible chains might use a different set of
precompiled contracts. It might also be possible that new
precompiled contracts are added to the Ethereum main chain in the future,
but you can reasonabyly expect them to always be in the range between
``1`` and ``0xffff`` (inclusive).

View File

@ -2630,9 +2630,21 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Check the target contract exists (has code) for non-low-level calls. // Check the target contract exists (has code) for non-low-level calls.
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
{ {
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; size_t encodedHeadSize = 0;
m_context.appendConditionalRevert(false, "Target contract does not contain code"); for (auto const& t: returnTypes)
existenceChecked = true; encodedHeadSize += t->decodingType()->calldataHeadSize();
// We do not need to check extcodesize if we expect return data, since if there is no
// code, the call will return empty data and the ABI decoder will revert.
if (
encodedHeadSize == 0 ||
!haveReturndatacopy ||
m_context.revertStrings() >= RevertStrings::Debug
)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalRevert(false, "Target contract does not contain code");
existenceChecked = true;
}
} }
if (_functionType.gasSet()) if (_functionType.gasSet())

View File

@ -2451,8 +2451,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n"; appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
} }
Whiskers templ(R"(if iszero(extcodesize(<address>)) { <revertNoCode>() } Whiskers templ(R"(
<?checkExtcodesize>
if iszero(extcodesize(<address>)) { <revertNoCode>() }
</checkExtcodesize>
// storage for arguments and returned data // storage for arguments and returned data
let <pos> := <allocateUnbounded>() let <pos> := <allocateUnbounded>()
mstore(<pos>, <shl28>(<funSel>)) mstore(<pos>, <shl28>(<funSel>))
@ -2477,6 +2479,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
} }
)"); )");
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code")); templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
// We do not need to check extcodesize if we expect return data: If there is no
// code, the call will return empty data and the ABI decoder will revert.
size_t encodedHeadSize = 0;
for (auto const& t: returnInfo.returnTypes)
encodedHeadSize += t->decodingType()->calldataHeadSize();
bool const checkExtcodesize =
encodedHeadSize == 0 ||
!m_context.evmVersion().supportsReturndata() ||
m_context.revertStrings() >= RevertStrings::Debug;
templ("checkExtcodesize", checkExtcodesize);
templ("pos", m_context.newYulVariable()); templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable()); templ("end", m_context.newYulVariable());
if (_functionCall.annotation().tryCall) if (_functionCall.annotation().tryCall)
@ -2532,6 +2546,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet()) if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExtcodesize)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
} }
// Order is important here, STATICCALL might overlap with DELEGATECALL. // Order is important here, STATICCALL might overlap with DELEGATECALL.

View File

@ -285,27 +285,18 @@ sub_0: assembly {
dup2 dup2
dup2 dup2
sstore sstore
/* \"C\":403:407 this */
address
/* \"C\":403:411 this.f() */
extcodesize
iszero
tag_43
jumpi
/* \"C\":79:428 contract C... */
mload(0x40) mload(0x40)
shl(0xe4, 0x026121ff) shl(0xe4, 0x026121ff)
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
dup2 dup2
mstore mstore
0x20 0x20
/* \"C\":79:428 contract C... */ /* \"C\":403:407 this */
dup2 dup2
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
0x04 0x04
/* \"C\":79:428 contract C... */
dup2
/* \"C\":403:407 this */ /* \"C\":403:407 this */
dup2
address address
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
gas gas
@ -313,38 +304,38 @@ sub_0: assembly {
swap2 swap2
dup3 dup3
iszero iszero
tag_45 tag_43
jumpi jumpi
dup1 dup1
swap3 swap3
tag_47 tag_45
jumpi jumpi
/* \"C\":304:341 modifier m()... */ /* \"C\":304:341 modifier m()... */
tag_48: tag_46:
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
pop pop
pop pop
tag_49 tag_47
swap1 swap1
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50 tag_48
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
swap3 swap3
tag_5 tag_5
jump\t// in jump\t// in
tag_49: tag_47:
/* \"C\":414:422 immutVar */ /* \"C\":414:422 immutVar */
immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\")
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
swap1 swap1
tag_5 tag_5
jump\t// in jump\t// in
tag_50: tag_48:
/* \"C\":304:341 modifier m()... */ /* \"C\":304:341 modifier m()... */
swap1 swap1
jump\t// out jump\t// out
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_47: tag_45:
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
swap1 swap1
swap2 swap2
@ -366,18 +357,18 @@ sub_0: assembly {
dup4 dup4
lt lt
or or
tag_51 tag_49
jumpi jumpi
pop pop
swap2 swap2
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_53 tag_51
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
tag_49 tag_47
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
swap3 swap3
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50 tag_48
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
swap5 swap5
0x40 0x40
@ -389,14 +380,14 @@ sub_0: assembly {
swap1 swap1
tag_7 tag_7
jump\t// in jump\t// in
tag_53: tag_51:
swap2 swap2
dup2 dup2
swap4 swap4
pop pop
jump(tag_48) jump(tag_46)
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
tag_51: tag_49:
shl(0xe0, 0x4e487b71) shl(0xe0, 0x4e487b71)
dup2 dup2
mstore mstore
@ -416,7 +407,7 @@ sub_0: assembly {
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
revert revert
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_45: tag_43:
/* \"C\":79:428 contract C... */ /* \"C\":79:428 contract C... */
swap4 swap4
pop pop
@ -431,20 +422,12 @@ sub_0: assembly {
returndatacopy returndatacopy
returndatasize returndatasize
swap1 swap1
revert
/* \"C\":403:411 this.f() */
tag_43:
/* \"C\":79:428 contract C... */
swap2
pop
pop
dup1
revert revert
tag_41: tag_41:
tag_54 tag_52
tag_3 tag_3
jump\t// in jump\t// in
tag_54: tag_52:
jump(tag_42) jump(tag_42)
tag_7: tag_7:
swap1 swap1
@ -453,12 +436,12 @@ sub_0: assembly {
swap2 swap2
sub sub
slt slt
tag_55 tag_53
jumpi jumpi
mload mload
swap1 swap1
jump\t// out jump\t// out
tag_55: tag_53:
pop pop
pop pop
0x00 0x00
@ -795,27 +778,18 @@ sub_0: assembly {
dup2 dup2
dup2 dup2
sstore sstore
/* \"C\":403:407 this */
address
/* \"C\":403:411 this.f() */
extcodesize
iszero
tag_43
jumpi
/* \"D\":91:166 contract D is C(3)... */
mload(0x40) mload(0x40)
shl(0xe4, 0x026121ff) shl(0xe4, 0x026121ff)
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
dup2 dup2
mstore mstore
0x20 0x20
/* \"D\":91:166 contract D is C(3)... */ /* \"C\":403:407 this */
dup2 dup2
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
0x04 0x04
/* \"D\":91:166 contract D is C(3)... */
dup2
/* \"C\":403:407 this */ /* \"C\":403:407 this */
dup2
address address
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
gas gas
@ -823,38 +797,38 @@ sub_0: assembly {
swap2 swap2
dup3 dup3
iszero iszero
tag_45 tag_43
jumpi jumpi
dup1 dup1
swap3 swap3
tag_47 tag_45
jumpi jumpi
/* \"C\":304:341 modifier m()... */ /* \"C\":304:341 modifier m()... */
tag_48: tag_46:
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
pop pop
pop pop
tag_49 tag_47
swap1 swap1
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50 tag_48
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
swap3 swap3
tag_5 tag_5
jump\t// in jump\t// in
tag_49: tag_47:
/* \"C\":414:422 immutVar */ /* \"C\":414:422 immutVar */
immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\")
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
swap1 swap1
tag_5 tag_5
jump\t// in jump\t// in
tag_50: tag_48:
/* \"C\":304:341 modifier m()... */ /* \"C\":304:341 modifier m()... */
swap1 swap1
jump\t// out jump\t// out
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_47: tag_45:
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
swap1 swap1
swap2 swap2
@ -876,18 +850,18 @@ sub_0: assembly {
dup4 dup4
lt lt
or or
tag_51 tag_49
jumpi jumpi
pop pop
swap2 swap2
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_53 tag_51
/* \"C\":392:411 stateVar + this.f() */ /* \"C\":392:411 stateVar + this.f() */
tag_49 tag_47
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
swap3 swap3
/* \"C\":392:422 stateVar + this.f() + immutVar */ /* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50 tag_48
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
swap5 swap5
0x40 0x40
@ -899,14 +873,14 @@ sub_0: assembly {
swap1 swap1
tag_7 tag_7
jump\t// in jump\t// in
tag_53: tag_51:
swap2 swap2
dup2 dup2
swap4 swap4
pop pop
jump(tag_48) jump(tag_46)
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
tag_51: tag_49:
shl(0xe0, 0x4e487b71) shl(0xe0, 0x4e487b71)
dup2 dup2
mstore mstore
@ -926,7 +900,7 @@ sub_0: assembly {
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
revert revert
/* \"C\":403:411 this.f() */ /* \"C\":403:411 this.f() */
tag_45: tag_43:
/* \"D\":91:166 contract D is C(3)... */ /* \"D\":91:166 contract D is C(3)... */
swap4 swap4
pop pop
@ -941,20 +915,12 @@ sub_0: assembly {
returndatacopy returndatacopy
returndatasize returndatasize
swap1 swap1
revert
/* \"C\":403:411 this.f() */
tag_43:
/* \"D\":91:166 contract D is C(3)... */
swap2
pop
pop
dup1
revert revert
tag_41: tag_41:
tag_54 tag_52
tag_3 tag_3
jump\t// in jump\t// in
tag_54: tag_52:
jump(tag_42) jump(tag_42)
tag_7: tag_7:
swap1 swap1
@ -963,12 +929,12 @@ sub_0: assembly {
swap2 swap2
sub sub
slt slt
tag_55 tag_53
jumpi jumpi
mload mload
swap1 swap1
jump\t// out jump\t// out
tag_55: tag_53:
pop pop
pop pop
0x00 0x00

View File

@ -526,7 +526,6 @@ object \"C_54\" {
let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address)
let expr_46_functionSelector := 0x26121ff0 let expr_46_functionSelector := 0x26121ff0
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() }
// storage for arguments and returned data // storage for arguments and returned data
let _10 := allocate_unbounded() let _10 := allocate_unbounded()
@ -640,7 +639,7 @@ object \"C_54\" {
case 0x26121ff0 { case 0x26121ff0 {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_1, _1) }
abi_decode(calldatasize()) abi_decode(calldatasize())
let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_566(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\")) let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_556(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\"))
/// @src 0:79:435 \"contract C...\" /// @src 0:79:435 \"contract C...\"
let memPos := mload(64) let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos)) return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
@ -664,7 +663,7 @@ object \"C_54\" {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_1, _1) }
abi_decode(calldatasize()) abi_decode(calldatasize())
let memPos_3 := mload(64) let memPos_3 := mload(64)
return(memPos_3, sub(abi_encode_int256_565(memPos_3), memPos_3)) return(memPos_3, sub(abi_encode_int256_555(memPos_3), memPos_3))
} }
} }
revert(0, 0) revert(0, 0)
@ -673,7 +672,7 @@ object \"C_54\" {
{ {
if slt(add(dataEnd, not(3)), 0) { revert(0, 0) } if slt(add(dataEnd, not(3)), 0) { revert(0, 0) }
} }
function abi_encode_int256_565(headStart) -> tail function abi_encode_int256_555(headStart) -> tail
{ {
tail := add(headStart, 32) tail := add(headStart, 32)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29) mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
@ -690,7 +689,7 @@ object \"C_54\" {
mstore(4, 0x11) mstore(4, 0x11)
revert(0, 0x24) revert(0, 0x24)
} }
function checked_add_int256_566(y) -> sum function checked_add_int256_556(y) -> sum
{ {
if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() } if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() }
sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 0:79:435 \"contract C...\" */ y) sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 0:79:435 \"contract C...\" */ y)
@ -712,13 +711,6 @@ object \"C_54\" {
let ret := add(_3, 1) let ret := add(_3, 1)
sstore(_2, ret) sstore(_2, ret)
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(/** @src 0:410:414 \"this\" */ address()))
/// @src 0:410:418 \"this.f()\"
{
/// @src 0:79:435 \"contract C...\"
revert(_2, _2)
}
/// @src 0:410:418 \"this.f()\"
let _4 := /** @src 0:79:435 \"contract C...\" */ mload(64) let _4 := /** @src 0:79:435 \"contract C...\" */ mload(64)
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
mstore(_4, /** @src 0:79:435 \"contract C...\" */ shl(228, 0x026121ff)) mstore(_4, /** @src 0:79:435 \"contract C...\" */ shl(228, 0x026121ff))
@ -1359,7 +1351,6 @@ object \"D_72\" {
let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address)
let expr_46_functionSelector := 0x26121ff0 let expr_46_functionSelector := 0x26121ff0
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() }
// storage for arguments and returned data // storage for arguments and returned data
let _10 := allocate_unbounded() let _10 := allocate_unbounded()
@ -1481,7 +1472,7 @@ object \"D_72\" {
case 0x26121ff0 { case 0x26121ff0 {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_1, _1) }
abi_decode(calldatasize()) abi_decode(calldatasize())
let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_566(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\")) let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_556(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\"))
/// @src 1:91:166 \"contract D is C(3)...\" /// @src 1:91:166 \"contract D is C(3)...\"
let memPos := mload(64) let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos)) return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
@ -1505,7 +1496,7 @@ object \"D_72\" {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_1, _1) }
abi_decode(calldatasize()) abi_decode(calldatasize())
let memPos_3 := mload(64) let memPos_3 := mload(64)
return(memPos_3, sub(abi_encode_int256_565(memPos_3), memPos_3)) return(memPos_3, sub(abi_encode_int256_555(memPos_3), memPos_3))
} }
} }
revert(0, 0) revert(0, 0)
@ -1514,7 +1505,7 @@ object \"D_72\" {
{ {
if slt(add(dataEnd, not(3)), 0) { revert(0, 0) } if slt(add(dataEnd, not(3)), 0) { revert(0, 0) }
} }
function abi_encode_int256_565(headStart) -> tail function abi_encode_int256_555(headStart) -> tail
{ {
tail := add(headStart, 32) tail := add(headStart, 32)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29) mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
@ -1531,7 +1522,7 @@ object \"D_72\" {
mstore(4, 0x11) mstore(4, 0x11)
revert(0, 0x24) revert(0, 0x24)
} }
function checked_add_int256_566(y) -> sum function checked_add_int256_556(y) -> sum
{ {
if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() } if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() }
sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 1:91:166 \"contract D is C(3)...\" */ y) sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 1:91:166 \"contract D is C(3)...\" */ y)
@ -1553,13 +1544,6 @@ object \"D_72\" {
let ret := add(_3, 1) let ret := add(_3, 1)
sstore(_2, ret) sstore(_2, ret)
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(/** @src 0:410:414 \"this\" */ address()))
/// @src 0:410:418 \"this.f()\"
{
/// @src 1:91:166 \"contract D is C(3)...\"
revert(_2, _2)
}
/// @src 0:410:418 \"this.f()\"
let _4 := /** @src 1:91:166 \"contract D is C(3)...\" */ mload(64) let _4 := /** @src 1:91:166 \"contract D is C(3)...\" */ mload(64)
/// @src 0:410:418 \"this.f()\" /// @src 0:410:418 \"this.f()\"
mstore(_4, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff)) mstore(_4, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff))

View File

@ -60,10 +60,10 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test_bytes() -> // test_bytes() ->
// gas irOptimized: 377545 // gas irOptimized: 373483
// gas legacy: 423563 // gas legacy: 418955
// gas legacyOptimized: 331391 // gas legacyOptimized: 326783
// test_uint256() -> // test_uint256() ->
// gas irOptimized: 528726 // gas irOptimized: 524664
// gas legacy: 591392 // gas legacy: 586784
// gas legacyOptimized: 456137 // gas legacyOptimized: 451529

View File

@ -26,6 +26,6 @@ contract C {
// ---- // ----
// library: L // library: L
// f() -> 8, 7, 1, 2, 7, 12 // f() -> 8, 7, 1, 2, 7, 12
// gas irOptimized: 167580 // gas irOptimized: 167446
// gas legacy: 169475 // gas legacy: 169347
// gas legacyOptimized: 167397 // gas legacyOptimized: 167269

View File

@ -61,10 +61,10 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test_bytes() -> // test_bytes() ->
// gas irOptimized: 377545 // gas irOptimized: 373483
// gas legacy: 423563 // gas legacy: 418955
// gas legacyOptimized: 331391 // gas legacyOptimized: 326783
// test_uint256() -> // test_uint256() ->
// gas irOptimized: 528726 // gas irOptimized: 524664
// gas legacy: 591392 // gas legacy: 586784
// gas legacyOptimized: 456137 // gas legacyOptimized: 451529

View File

@ -32,6 +32,6 @@ contract C is B {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test() -> 77 // test() -> 77
// gas irOptimized: 120044 // gas irOptimized: 119931
// gas legacy: 155221 // gas legacy: 155093
// gas legacyOptimized: 111678 // gas legacyOptimized: 111550

View File

@ -40,5 +40,5 @@ contract C is B {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test() -> 5, 10 // test() -> 5, 10
// gas irOptimized: 87578 // gas irOptimized: 87337
// gas legacy: 99137 // gas legacy: 98881

View File

@ -21,6 +21,6 @@ contract C {
// f(uint256[][1]): 32, 32, 0 -> true // f(uint256[][1]): 32, 32, 0 -> true
// f(uint256[][1]): 32, 32, 1, 42 -> true // f(uint256[][1]): 32, 32, 1, 42 -> true
// f(uint256[][1]): 32, 32, 8, 421, 422, 423, 424, 425, 426, 427, 428 -> true // f(uint256[][1]): 32, 32, 8, 421, 422, 423, 424, 425, 426, 427, 428 -> true
// gas irOptimized: 172204 // gas irOptimized: 171964
// gas legacy: 141900 // gas legacy: 141644
// gas legacyOptimized: 121788 // gas legacyOptimized: 121532

View File

@ -18,4 +18,4 @@ contract D {
// ---- // ----
// f() -> FAILURE, hex"4e487b71", 0x11 // f() -> FAILURE, hex"4e487b71", 0x11
// g(), 100 wei -> 1 // g(), 100 wei -> 1
// gas legacy: 101918 // gas legacy: 101790

View File

@ -21,6 +21,6 @@ contract B {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004
// gas irOptimized: 130328 // gas irOptimized: 130097
// gas legacy: 235199 // gas legacy: 234943
// gas legacyOptimized: 133119 // gas legacyOptimized: 132863

View File

@ -45,6 +45,6 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test() -> 5, 6, 7 // test() -> 5, 6, 7
// gas irOptimized: 302321 // gas irOptimized: 290947
// gas legacy: 462080 // gas legacy: 452172
// gas legacyOptimized: 294938 // gas legacyOptimized: 285017

View File

@ -26,6 +26,6 @@ contract Main {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1
// gas irOptimized: 113776 // gas irOptimized: 113581
// gas legacy: 126852 // gas legacy: 126596
// gas legacyOptimized: 114079 // gas legacyOptimized: 113823

View File

@ -26,6 +26,6 @@ contract Creator {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
// gas irOptimized: 456873 // gas irOptimized: 456668
// gas legacy: 590939 // gas legacy: 590683
// gas legacyOptimized: 448582 // gas legacyOptimized: 448326

View File

@ -26,6 +26,6 @@ contract Creator {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h"
// gas irOptimized: 308702 // gas irOptimized: 308497
// gas legacy: 429173 // gas legacy: 428917
// gas legacyOptimized: 298384 // gas legacyOptimized: 298128

View File

@ -19,4 +19,4 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 16 // f() -> 16
// gas legacy: 103744 // gas legacy: 103488

View File

@ -15,4 +15,4 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 2 // f() -> 2
// gas legacy: 101754 // gas legacy: 101626

View File

@ -13,4 +13,4 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 2 // f() -> 2
// gas legacy: 101727 // gas legacy: 101599

View File

@ -0,0 +1,26 @@
// This tests skipping the extcodesize check.
contract T {
constructor() { this.f(); }
function f() external {}
}
contract U {
constructor() { this.f(); }
function f() external returns (uint) {}
}
contract C {
function f(uint c) external returns (uint) {
if (c == 0) new T();
else if (c == 1) new U();
return 1 + c;
}
}
// ====
// EVMVersion: >=byzantium
// compileViaYul: also
// ----
// f(uint256): 0 -> FAILURE
// f(uint256): 1 -> FAILURE
// f(uint256): 2 -> 3

View File

@ -0,0 +1,36 @@
// This tests skipping the extcodesize check.
interface I {
function a() external pure;
function b() external;
function c() external payable;
function x() external returns (uint);
function y() external returns (string memory);
}
contract C {
I i = I(address(0xcafecafe));
constructor() payable {}
function f(uint c) external returns (uint) {
if (c == 0) i.a();
else if (c == 1) i.b();
else if (c == 2) i.c();
else if (c == 3) i.c{value: 1}();
else if (c == 4) i.x();
else if (c == 5) i.y();
return 1 + c;
}
}
// ====
// compileViaYul: also
// ----
// constructor(), 1 ether ->
// gas legacy: 465314
// gas legacyOptimized: 510004
// f(uint256): 0 -> FAILURE
// f(uint256): 1 -> FAILURE
// f(uint256): 2 -> FAILURE
// f(uint256): 3 -> FAILURE
// f(uint256): 4 -> FAILURE
// f(uint256): 5 -> FAILURE
// f(uint256): 6 -> 7

View File

@ -0,0 +1,37 @@
// This tests skipping the extcodesize check.
interface I {
function a() external pure;
function b() external;
function c() external payable;
function x() external returns (uint);
function y() external returns (string memory);
}
contract C {
I i = I(address(0xcafecafe));
constructor() payable {}
function f(uint c) external returns (uint) {
if (c == 0) i.a();
else if (c == 1) i.b();
else if (c == 2) i.c();
else if (c == 3) i.c{value: 1}();
else if (c == 4) i.x();
else if (c == 5) i.y();
return 1 + c;
}
}
// ====
// EVMVersion: >=byzantium
// compileViaYul: also
// revertStrings: debug
// ----
// constructor(), 1 ether ->
// gas legacyOptimized: 510004
// f(uint256): 0 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 1 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 2 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 3 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 4 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 5 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code"
// f(uint256): 6 -> 7

View File

@ -18,17 +18,17 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// constructor(), 20 wei // constructor(), 20 wei
// gas irOptimized: 220113 // gas irOptimized: 218775
// gas legacy: 288299 // gas legacy: 294569
// gas legacyOptimized: 177933 // gas legacyOptimized: 174699
// f(uint256): 20 -> 1370859564726510389319704988634906228201275401179 // f(uint256): 20 -> 1370859564726510389319704988634906228201275401179
// x() -> 1 // x() -> 1
// f(uint256): 20 -> FAILURE // f(uint256): 20 -> FAILURE
// x() -> 1 // x() -> 1
// stack(uint256): 1023 -> FAILURE // stack(uint256): 1023 -> FAILURE
// gas irOptimized: 345821 // gas irOptimized: 296769
// gas legacy: 535367 // gas legacy: 483942
// gas legacyOptimized: 354656 // gas legacyOptimized: 298807
// x() -> 1 // x() -> 1
// stack(uint256): 10 -> 693016686122178122849713379390321835634789309880 // stack(uint256): 10 -> 693016686122178122849713379390321835634789309880
// x() -> 2 // x() -> 2

View File

@ -1,13 +1,21 @@
interface Identity { interface Identity {
function selectorAndAppendValue(uint value) external pure returns (uint); function selectorAndAppendValue(uint value) external pure returns (uint);
} }
interface ReturnMoreData {
function f(uint value) external pure returns (uint, uint, uint);
}
contract C { contract C {
Identity constant i = Identity(address(0x0004)); Identity constant i = Identity(address(0x0004));
function testHighLevel() external pure returns (bool) { function testHighLevel() external pure returns (bool) {
// Should fail because `extcodesize(4) = 0` // Works because the extcodesize check is skipped
// and the precompiled contract returns actual data.
i.selectorAndAppendValue(5); i.selectorAndAppendValue(5);
return true; return true;
} }
function testHighLevel2() external pure returns (uint, uint, uint) {
// Fails because the identity contract does not return enough data.
return ReturnMoreData(address(4)).f(2);
}
function testLowLevel() external view returns (uint value) { function testLowLevel() external view returns (uint value) {
(bool success, bytes memory ret) = (bool success, bytes memory ret) =
address(4).staticcall( address(4).staticcall(
@ -18,8 +26,9 @@ contract C {
} }
// ==== // ====
// compileViaYul: also
// EVMVersion: >=constantinople // EVMVersion: >=constantinople
// compileViaYul: also
// ---- // ----
// testHighLevel() -> FAILURE // testHighLevel() -> true
// testLowLevel() -> 0xc76596d400000000000000000000000000000000000000000000000000000000 // testLowLevel() -> 0xc76596d400000000000000000000000000000000000000000000000000000000
// testHighLevel2() -> FAILURE

View File

@ -28,6 +28,6 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// t() -> 9 // t() -> 9
// gas irOptimized: 99186 // gas irOptimized: 99064
// gas legacy: 159083 // gas legacy: 158955
// gas legacyOptimized: 108916 // gas legacyOptimized: 108788

View File

@ -29,8 +29,8 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 3, 7, 5 // f() -> 3, 7, 5
// gas irOptimized: 127592 // gas irOptimized: 127387
// gas legacy: 151590 // gas legacy: 151334
// gas legacyOptimized: 125422 // gas legacyOptimized: 125166
// x() -> 7 // x() -> 7
// y() -> 5 // y() -> 5

View File

@ -23,8 +23,8 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 1 // f() -> 1
// gas irOptimized: 77164 // gas irOptimized: 77051
// gas legacy: 115012 // gas legacy: 114884
// g() -> 5 // g() -> 5
// gas irOptimized: 77231 // gas irOptimized: 77106
// gas legacy: 115558 // gas legacy: 115430

View File

@ -25,5 +25,5 @@ contract B {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// g() -> 42 // g() -> 42
// gas irOptimized: 80945 // gas irOptimized: 80813
// gas legacy: 125609 // gas legacy: 125481

View File

@ -25,6 +25,6 @@ contract B {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// g() -> 42 // g() -> 42
// gas irOptimized: 111913 // gas irOptimized: 111781
// gas legacy: 185181 // gas legacy: 185053
// gas legacyOptimized: 114726 // gas legacyOptimized: 114598

View File

@ -22,6 +22,6 @@ contract A {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// g(int256): -1 -> -1 // g(int256): -1 -> -1
// gas legacy: 103622 // gas legacy: 103494
// g(int256): 10 -> 10 // g(int256): 10 -> 10
// gas legacy: 103250 // gas legacy: 103122

View File

@ -37,10 +37,10 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// convertParent() -> 1 // convertParent() -> 1
// gas irOptimized: 85640 // gas irOptimized: 85524
// convertSubA() -> 1, 2 // convertSubA() -> 1, 2
// gas irOptimized: 86395 // gas irOptimized: 86155
// gas legacy: 99303 // gas legacy: 99047
// convertSubB() -> 1, 3 // convertSubB() -> 1, 3
// gas irOptimized: 86338 // gas irOptimized: 86098
// gas legacy: 99237 // gas legacy: 98981

View File

@ -22,6 +22,6 @@ contract A {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(), 10 ether -> 3007, 3008, 3009 // f(), 10 ether -> 3007, 3008, 3009
// gas irOptimized: 273275 // gas irOptimized: 272947
// gas legacy: 422885 // gas legacy: 422501
// gas legacyOptimized: 287856 // gas legacyOptimized: 287472

View File

@ -27,5 +27,5 @@ contract D {
// stateDecimal() -> right(42) // stateDecimal() -> right(42)
// stateBytes() -> left(0x4200ef) // stateBytes() -> left(0x4200ef)
// internalStateDecimal() -> 0x20 // internalStateDecimal() -> 0x20
// gas legacy: 101807 // gas legacy: 101679
// update(bool,uint256,bytes32): false, -23, left(0x2300ef) -> false, -23, left(0x2300ef) // update(bool,uint256,bytes32): false, -23, left(0x2300ef) -> false, -23, left(0x2300ef)

View File

@ -42,4 +42,4 @@ contract C {
// testRuntime() -> true // testRuntime() -> true
// gas legacy: 101579 // gas legacy: 101579
// testCreation() -> true // testCreation() -> true
// gas legacy: 102137 // gas legacy: 102009

View File

@ -26,4 +26,4 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test() -> 7 // test() -> 7
// gas legacy: 102392 // gas legacy: 102264

View File

@ -27,5 +27,5 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// test() -> 9, 7 // test() -> 9, 7
// gas legacy: 130016 // gas legacy: 129760
// t2() -> 9 // t2() -> 9

View File

@ -22,6 +22,6 @@ contract C {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// g() -> 2, 6 // g() -> 2, 6
// gas irOptimized: 178953 // gas irOptimized: 178835
// gas legacy: 180890 // gas legacy: 180762
// gas legacyOptimized: 179609 // gas legacyOptimized: 179481

View File

@ -36,12 +36,12 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f() -> 0x1 # This should work, next should throw # // f() -> 0x1 # This should work, next should throw #
// gas legacy: 103844 // gas legacy: 103716
// fview() -> FAILURE // fview() -> FAILURE
// gas irOptimized: 98438627 // gas irOptimized: 98438625
// gas legacy: 98438803 // gas legacy: 98438801
// gas legacyOptimized: 98438596 // gas legacyOptimized: 98438594
// fpure() -> FAILURE // fpure() -> FAILURE
// gas irOptimized: 98438627 // gas irOptimized: 98438626
// gas legacy: 98438803 // gas legacy: 98438801
// gas legacyOptimized: 98438597 // gas legacyOptimized: 98438595