Merge pull request 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:
* 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: 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.

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
always succeed, Solidity uses the ``extcodesize`` opcode to check that
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
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
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
by changing some internal state which causes all functions to revert. This
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.
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalRevert(false, "Target contract does not contain code");
existenceChecked = true;
size_t encodedHeadSize = 0;
for (auto const& t: returnTypes)
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())

View File

@ -2451,8 +2451,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
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
let <pos> := <allocateUnbounded>()
mstore(<pos>, <shl28>(<funSel>))
@ -2477,6 +2479,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
}
)");
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("end", m_context.newYulVariable());
if (_functionCall.annotation().tryCall)
@ -2532,6 +2546,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExtcodesize)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
// Order is important here, STATICCALL might overlap with DELEGATECALL.

View File

@ -285,27 +285,18 @@ sub_0: assembly {
dup2
dup2
sstore
/* \"C\":403:407 this */
address
/* \"C\":403:411 this.f() */
extcodesize
iszero
tag_43
jumpi
/* \"C\":79:428 contract C... */
mload(0x40)
shl(0xe4, 0x026121ff)
/* \"C\":403:411 this.f() */
dup2
mstore
0x20
/* \"C\":79:428 contract C... */
/* \"C\":403:407 this */
dup2
/* \"C\":403:411 this.f() */
0x04
/* \"C\":79:428 contract C... */
dup2
/* \"C\":403:407 this */
dup2
address
/* \"C\":403:411 this.f() */
gas
@ -313,38 +304,38 @@ sub_0: assembly {
swap2
dup3
iszero
tag_45
tag_43
jumpi
dup1
swap3
tag_47
tag_45
jumpi
/* \"C\":304:341 modifier m()... */
tag_48:
tag_46:
/* \"C\":392:411 stateVar + this.f() */
pop
pop
tag_49
tag_47
swap1
/* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50
tag_48
/* \"C\":392:411 stateVar + this.f() */
swap3
tag_5
jump\t// in
tag_49:
tag_47:
/* \"C\":414:422 immutVar */
immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\")
/* \"C\":392:422 stateVar + this.f() + immutVar */
swap1
tag_5
jump\t// in
tag_50:
tag_48:
/* \"C\":304:341 modifier m()... */
swap1
jump\t// out
/* \"C\":403:411 this.f() */
tag_47:
tag_45:
/* \"C\":79:428 contract C... */
swap1
swap2
@ -366,18 +357,18 @@ sub_0: assembly {
dup4
lt
or
tag_51
tag_49
jumpi
pop
swap2
/* \"C\":403:411 this.f() */
tag_53
tag_51
/* \"C\":392:411 stateVar + this.f() */
tag_49
tag_47
/* \"C\":79:428 contract C... */
swap3
/* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50
tag_48
/* \"C\":79:428 contract C... */
swap5
0x40
@ -389,14 +380,14 @@ sub_0: assembly {
swap1
tag_7
jump\t// in
tag_53:
tag_51:
swap2
dup2
swap4
pop
jump(tag_48)
jump(tag_46)
/* \"C\":79:428 contract C... */
tag_51:
tag_49:
shl(0xe0, 0x4e487b71)
dup2
mstore
@ -416,7 +407,7 @@ sub_0: assembly {
/* \"C\":79:428 contract C... */
revert
/* \"C\":403:411 this.f() */
tag_45:
tag_43:
/* \"C\":79:428 contract C... */
swap4
pop
@ -431,20 +422,12 @@ sub_0: assembly {
returndatacopy
returndatasize
swap1
revert
/* \"C\":403:411 this.f() */
tag_43:
/* \"C\":79:428 contract C... */
swap2
pop
pop
dup1
revert
tag_41:
tag_54
tag_52
tag_3
jump\t// in
tag_54:
tag_52:
jump(tag_42)
tag_7:
swap1
@ -453,12 +436,12 @@ sub_0: assembly {
swap2
sub
slt
tag_55
tag_53
jumpi
mload
swap1
jump\t// out
tag_55:
tag_53:
pop
pop
0x00
@ -795,27 +778,18 @@ sub_0: assembly {
dup2
dup2
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)
shl(0xe4, 0x026121ff)
/* \"C\":403:411 this.f() */
dup2
mstore
0x20
/* \"D\":91:166 contract D is C(3)... */
/* \"C\":403:407 this */
dup2
/* \"C\":403:411 this.f() */
0x04
/* \"D\":91:166 contract D is C(3)... */
dup2
/* \"C\":403:407 this */
dup2
address
/* \"C\":403:411 this.f() */
gas
@ -823,38 +797,38 @@ sub_0: assembly {
swap2
dup3
iszero
tag_45
tag_43
jumpi
dup1
swap3
tag_47
tag_45
jumpi
/* \"C\":304:341 modifier m()... */
tag_48:
tag_46:
/* \"C\":392:411 stateVar + this.f() */
pop
pop
tag_49
tag_47
swap1
/* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50
tag_48
/* \"C\":392:411 stateVar + this.f() */
swap3
tag_5
jump\t// in
tag_49:
tag_47:
/* \"C\":414:422 immutVar */
immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\")
/* \"C\":392:422 stateVar + this.f() + immutVar */
swap1
tag_5
jump\t// in
tag_50:
tag_48:
/* \"C\":304:341 modifier m()... */
swap1
jump\t// out
/* \"C\":403:411 this.f() */
tag_47:
tag_45:
/* \"D\":91:166 contract D is C(3)... */
swap1
swap2
@ -876,18 +850,18 @@ sub_0: assembly {
dup4
lt
or
tag_51
tag_49
jumpi
pop
swap2
/* \"C\":403:411 this.f() */
tag_53
tag_51
/* \"C\":392:411 stateVar + this.f() */
tag_49
tag_47
/* \"D\":91:166 contract D is C(3)... */
swap3
/* \"C\":392:422 stateVar + this.f() + immutVar */
tag_50
tag_48
/* \"D\":91:166 contract D is C(3)... */
swap5
0x40
@ -899,14 +873,14 @@ sub_0: assembly {
swap1
tag_7
jump\t// in
tag_53:
tag_51:
swap2
dup2
swap4
pop
jump(tag_48)
jump(tag_46)
/* \"D\":91:166 contract D is C(3)... */
tag_51:
tag_49:
shl(0xe0, 0x4e487b71)
dup2
mstore
@ -926,7 +900,7 @@ sub_0: assembly {
/* \"D\":91:166 contract D is C(3)... */
revert
/* \"C\":403:411 this.f() */
tag_45:
tag_43:
/* \"D\":91:166 contract D is C(3)... */
swap4
pop
@ -941,20 +915,12 @@ sub_0: assembly {
returndatacopy
returndatasize
swap1
revert
/* \"C\":403:411 this.f() */
tag_43:
/* \"D\":91:166 contract D is C(3)... */
swap2
pop
pop
dup1
revert
tag_41:
tag_54
tag_52
tag_3
jump\t// in
tag_54:
tag_52:
jump(tag_42)
tag_7:
swap1
@ -963,12 +929,12 @@ sub_0: assembly {
swap2
sub
slt
tag_55
tag_53
jumpi
mload
swap1
jump\t// out
tag_55:
tag_53:
pop
pop
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_functionSelector := 0x26121ff0
/// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() }
// storage for arguments and returned data
let _10 := allocate_unbounded()
@ -640,7 +639,7 @@ object \"C_54\" {
case 0x26121ff0 {
if callvalue() { revert(_1, _1) }
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...\"
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
@ -664,7 +663,7 @@ object \"C_54\" {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
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)
@ -673,7 +672,7 @@ object \"C_54\" {
{
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)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
@ -690,7 +689,7 @@ object \"C_54\" {
mstore(4, 0x11)
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() }
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)
sstore(_2, ret)
/// @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)
/// @src 0:410:418 \"this.f()\"
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_functionSelector := 0x26121ff0
/// @src 0:410:418 \"this.f()\"
if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() }
// storage for arguments and returned data
let _10 := allocate_unbounded()
@ -1481,7 +1472,7 @@ object \"D_72\" {
case 0x26121ff0 {
if callvalue() { revert(_1, _1) }
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)...\"
let memPos := mload(64)
return(memPos, sub(abi_encode_int256(memPos, ret), memPos))
@ -1505,7 +1496,7 @@ object \"D_72\" {
if callvalue() { revert(_1, _1) }
abi_decode(calldatasize())
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)
@ -1514,7 +1505,7 @@ object \"D_72\" {
{
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)
mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29)
@ -1531,7 +1522,7 @@ object \"D_72\" {
mstore(4, 0x11)
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() }
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)
sstore(_2, ret)
/// @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)
/// @src 0:410:418 \"this.f()\"
mstore(_4, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,4 +13,4 @@ contract D {
// compileViaYul: also
// ----
// 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
// ----
// constructor(), 20 wei
// gas irOptimized: 220113
// gas legacy: 288299
// gas legacyOptimized: 177933
// gas irOptimized: 218775
// gas legacy: 294569
// gas legacyOptimized: 174699
// f(uint256): 20 -> 1370859564726510389319704988634906228201275401179
// x() -> 1
// f(uint256): 20 -> FAILURE
// x() -> 1
// stack(uint256): 1023 -> FAILURE
// gas irOptimized: 345821
// gas legacy: 535367
// gas legacyOptimized: 354656
// gas irOptimized: 296769
// gas legacy: 483942
// gas legacyOptimized: 298807
// x() -> 1
// stack(uint256): 10 -> 693016686122178122849713379390321835634789309880
// x() -> 2

View File

@ -1,13 +1,21 @@
interface Identity {
function selectorAndAppendValue(uint value) external pure returns (uint);
}
interface ReturnMoreData {
function f(uint value) external pure returns (uint, uint, uint);
}
contract C {
Identity constant i = Identity(address(0x0004));
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);
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) {
(bool success, bytes memory ret) =
address(4).staticcall(
@ -18,8 +26,9 @@ contract C {
}
// ====
// compileViaYul: also
// EVMVersion: >=constantinople
// compileViaYul: also
// ----
// testHighLevel() -> FAILURE
// testHighLevel() -> true
// testLowLevel() -> 0xc76596d400000000000000000000000000000000000000000000000000000000
// testHighLevel2() -> FAILURE

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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