From b1cc886a8012e23725e6f4e9a647242ff4ca2530 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Sat, 30 Nov 2024 22:13:12 +0100 Subject: [PATCH] eof: Implement and use `ext*call` --- libevmasm/Instruction.cpp | 6 ++ libevmasm/Instruction.h | 6 ++ libevmasm/SemanticInformation.cpp | 49 +++++++++ liblangutil/EVMVersion.cpp | 3 + .../codegen/ir/IRGeneratorForStatements.cpp | 99 +++++++++++++++---- libyul/AsmAnalysis.cpp | 17 ++++ test/libyul/objectCompiler/eof/extcall.yul | 90 +++++++++++++++++ test/libyul/yulSyntaxTests/eof/extcalls.yul | 25 +++++ .../eof/extcalls_invalid_in_legacy.yul | 28 ++++++ .../eof/legacy_calls_in_eof.yul | 26 +++++ .../EVMInstructionInterpreter.cpp | 3 + 11 files changed, 334 insertions(+), 18 deletions(-) create mode 100644 test/libyul/objectCompiler/eof/extcall.yul create mode 100644 test/libyul/yulSyntaxTests/eof/extcalls.yul create mode 100644 test/libyul/yulSyntaxTests/eof/extcalls_invalid_in_legacy.yul create mode 100644 test/libyul/yulSyntaxTests/eof/legacy_calls_in_eof.yul diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 82ed73cc994e..649a6c863486 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -182,6 +182,9 @@ std::map const solidity::evmasm::c_instructions = { "STATICCALL", Instruction::STATICCALL }, { "RETURN", Instruction::RETURN }, { "DELEGATECALL", Instruction::DELEGATECALL }, + { "EXTCALL", Instruction::EXTCALL }, + { "EXTSTATICCALL", Instruction::EXTSTATICCALL }, + { "EXTDELEGATECALL", Instruction::EXTDELEGATECALL }, { "CREATE2", Instruction::CREATE2 }, { "REVERT", Instruction::REVERT }, { "INVALID", Instruction::INVALID }, @@ -344,6 +347,9 @@ static std::map const c_instructionInfo = {Instruction::RETURN, {"RETURN", 0, 2, 0, true, Tier::Zero}}, {Instruction::DELEGATECALL, {"DELEGATECALL", 0, 6, 1, true, Tier::Special}}, {Instruction::STATICCALL, {"STATICCALL", 0, 6, 1, true, Tier::Special}}, + {Instruction::EXTCALL, {"EXTCALL", 0, 4, 1, true, Tier::Special}}, + {Instruction::EXTDELEGATECALL,{"EXTDELEGATECALL", 0, 3, 1, true, Tier::Special}}, + {Instruction::EXTSTATICCALL, {"EXTSTATICCALL", 0, 3, 1, true, Tier::Special}}, {Instruction::CREATE2, {"CREATE2", 0, 4, 1, true, Tier::Special}}, {Instruction::REVERT, {"REVERT", 0, 2, 0, true, Tier::Zero}}, {Instruction::INVALID, {"INVALID", 0, 0, 0, true, Tier::Zero}}, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 351a6981c070..a1b9af364a0d 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -198,7 +198,10 @@ enum class Instruction: uint8_t RETURN, ///< halt execution returning output data DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender CREATE2 = 0xf5, ///< create new account with associated code at address `sha3(0xff + sender + salt + init code) % 2**160` + EXTCALL = 0xf8, ///< EOF message-call into an account + EXTDELEGATECALL = 0xf9, ///< EOF delegate call STATICCALL = 0xfa, ///< like CALL but disallow state modifications + EXTSTATICCALL = 0xfb, ///< like EXTCALL but disallow state modifications REVERT = 0xfd, ///< halt execution, revert state and return output data INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) @@ -214,6 +217,9 @@ constexpr bool isCallInstruction(Instruction _inst) noexcept case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: + case Instruction::EXTCALL: + case Instruction::EXTSTATICCALL: + case Instruction::EXTDELEGATECALL: return true; default: return false; diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index c27945b82e51..509094670d09 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -169,6 +169,37 @@ std::vector SemanticInformation::readWriteOperat }); return operations; } + case Instruction::EXTSTATICCALL: + case Instruction::EXTDELEGATECALL: + { + size_t paramCount = static_cast(instructionInfo(_instruction, langutil::EVMVersion()).args); + std::vector operations{ + Operation{Location::Memory, Effect::Read, paramCount - 2, paramCount - 1, {}}, + Operation{Location::Storage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}} + }; + if (_instruction == Instruction::EXTDELEGATECALL) + { + operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}}); + operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}); + } + + return operations; + } + case Instruction::EXTCALL: + { + size_t paramCount = static_cast(instructionInfo(_instruction, langutil::EVMVersion()).args); + std::vector operations{ + Operation{Location::Memory, Effect::Read, paramCount - 3, paramCount - 2, {}}, + Operation{Location::Storage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}} + }; + + operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}}); + operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}); + + return operations; + } case Instruction::CREATE: case Instruction::CREATE2: return std::vector{ @@ -380,6 +411,9 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item) case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: + case Instruction::EXTSTATICCALL: case Instruction::CREATE: case Instruction::CREATE2: case Instruction::GAS: @@ -459,6 +493,9 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: + case Instruction::EXTSTATICCALL: return SemanticInformation::Write; case Instruction::CREATE: @@ -514,10 +551,13 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio case Instruction::SSTORE: case Instruction::EOFCREATE: case Instruction::RETURNCONTRACT: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: return SemanticInformation::Write; case Instruction::SLOAD: case Instruction::STATICCALL: + case Instruction::EXTSTATICCALL: return SemanticInformation::Read; default: @@ -537,10 +577,13 @@ SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _i case Instruction::TSTORE: case Instruction::EOFCREATE: case Instruction::RETURNCONTRACT: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: return SemanticInformation::Write; case Instruction::TLOAD: case Instruction::STATICCALL: + case Instruction::EXTSTATICCALL: return SemanticInformation::Read; default: @@ -559,7 +602,10 @@ SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruc case Instruction::CREATE2: case Instruction::EOFCREATE: case Instruction::RETURNCONTRACT: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: case Instruction::SELFDESTRUCT: + case Instruction::EXTSTATICCALL: case Instruction::STATICCALL: // because it can affect returndatasize // Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they // are just marked as having 'other side effects.' @@ -606,6 +652,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::NUMBER: case Instruction::PREVRANDAO: case Instruction::GASLIMIT: + case Instruction::EXTSTATICCALL: case Instruction::STATICCALL: case Instruction::SLOAD: case Instruction::TLOAD: @@ -635,6 +682,8 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) case Instruction::CALL: case Instruction::CALLCODE: case Instruction::DELEGATECALL: + case Instruction::EXTCALL: + case Instruction::EXTDELEGATECALL: // According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#eofcreate case Instruction::EOFCREATE: // According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#returncontract diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 26364f2af2a2..6f7ac91397d9 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -85,6 +85,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional _eofVersi case Instruction::CALLF: case Instruction::JUMPF: case Instruction::RETF: + case Instruction::EXTCALL: + case Instruction::EXTSTATICCALL: + case Instruction::EXTDELEGATECALL: return _eofVersion.has_value(); default: return true; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 9b2e0ea7723e..8f4404b9b554 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1622,11 +1622,16 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) Whiskers templ(R"( let := 0 if iszero() { := } - let := call(,
, , 0, 0, 0, 0) + + let := iszero(extcall(
, 0, 0, )) + + let := call(,
, , 0, 0, 0, 0) + if iszero() { () } )"); + templ("eof", m_context.eofVersion().has_value()); templ("gas", m_context.newYulVariable()); templ("callStipend", toString(evmasm::GasCosts::callStipend)); templ("address", address); @@ -1669,17 +1674,29 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) mstore(0, 0) - let := (,
, 0, , sub(, ), 0, 32) + + let := iszero((
, , sub(, ) , 0)) + + let := (,
, 0, , sub(, ), 0, 32) + if iszero() { () } + + if eq(returndatasize(), 32) { returndatacopy(0, 0, 32) } + let := (mload(0)) )"); - templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call"); + auto const eof = m_context.eofVersion().has_value(); + if (!eof) + templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call"); + else + templ("call", "extstaticcall"); // EOF always has static call templ("isCall", !m_context.evmVersion().hasStaticCall()); templ("shl", m_utils.shiftLeftFunction(offset * 8)); templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind()); + templ("eof", eof); if (FunctionType::Kind::ECRecover == functionType->kind()) templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes)); else @@ -1844,7 +1861,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable); define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); } - else if (std::set{"call", "callcode", "delegatecall", "staticcall"}.count(member)) + else if (std::set{"call", "extcall", "callcode", "delegatecall" , "extdelegatecall", "staticcall", "extstaticcall"}.count(member)) define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); else solAssert(false, "Invalid member access to address"); @@ -2642,7 +2659,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( } // NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically. - // When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy(). + // When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy(). Whiskers templ(R"( if iszero(extcodesize(
)) { () } @@ -2652,7 +2669,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall( mstore(, ()) let := (add(, 4) ) - let := (,
, , , sub(, ), , ) + + let := iszero((
, , sub(, ) , )) + + let := (,
, , , sub(, ), , ) + if iszero() { () } @@ -2667,6 +2688,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall( if gt(, returndatasize()) { := returndatasize() } + + returndatacopy(, 0, ) + @@ -2678,6 +2702,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall( } )"); templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code")); + auto const eof = m_context.eofVersion().has_value(); + templ("eof", eof); // 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. @@ -2685,9 +2711,12 @@ void IRGeneratorForStatements::appendExternalFunctionCall( for (auto const& t: returnInfo.returnTypes) encodedHeadSize += t->decodingType()->calldataHeadSize(); bool const checkExtcodesize = - encodedHeadSize == 0 || - !m_context.evmVersion().supportsReturndata() || - m_context.revertStrings() >= RevertStrings::Debug; + !m_context.eofVersion().has_value() && + ( + encodedHeadSize == 0 || + !m_context.evmVersion().supportsReturndata() || + m_context.revertStrings() >= RevertStrings::Debug + ); templ("checkExtcodesize", checkExtcodesize); templ("pos", m_context.newYulVariable()); @@ -2747,12 +2776,25 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); } // Order is important here, STATICCALL might overlap with DELEGATECALL. - if (isDelegateCall) - templ("call", "delegatecall"); - else if (useStaticCall) - templ("call", "staticcall"); + if (!eof) + { + if (isDelegateCall) + templ("call", "delegatecall"); + else if (useStaticCall) + templ("call", "staticcall"); + else + templ("call", "call"); + } else - templ("call", "call"); + { + if (isDelegateCall) + templ("call", "extdelegatecall"); + else if (useStaticCall) + templ("call", "extstaticcall"); + else + templ("call", "extcall"); + } + templ("forwardingRevert", m_utils.forwardingRevertFunction()); @@ -2791,13 +2833,21 @@ void IRGeneratorForStatements::appendBareCall( let := mload() - let := (,
, , , , 0, 0) + + let := (
, , , ) + := iszero() + + let := (,
, , , , 0, 0) + + let := () )"); templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); templ("pos", m_context.newYulVariable()); templ("length", m_context.newYulVariable()); + auto const eof = m_context.eofVersion().has_value(); + templ("eof", eof); templ("arg", IRVariable(*_arguments.front()).commaSeparatedList()); Type const& argType = type(*_arguments.front()); @@ -2819,16 +2869,29 @@ void IRGeneratorForStatements::appendBareCall( if (funKind == FunctionType::Kind::BareCall) { templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); - templ("call", "call"); + if (eof) + templ("call", "extcall"); + else + templ("call", "call"); } else { solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall."); templ("value", ""); if (funKind == FunctionType::Kind::BareStaticCall) - templ("call", "staticcall"); + { + if (eof) + templ("call", "extstaticcall"); + else + templ("call", "staticcall"); + } else - templ("call", "delegatecall"); + { + if (eof) + templ("call", "extdelegatecall"); + else + templ("call", "delegatecall"); + } } if (funType.gasSet()) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 491effe8e619..70b55e28d570 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -771,10 +771,27 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio "PC instruction is a low-level EVM feature. " "Because of that PC is disallowed in strict assembly." ); + else if (!m_eofVersion.has_value() && ( + _instr == evmasm::Instruction::EXTCALL || + _instr == evmasm::Instruction::EXTDELEGATECALL || + _instr == evmasm::Instruction::EXTSTATICCALL + )) + { + m_errorReporter.typeError( + 4328_error, + _location, + fmt::format( + "The \"{instruction}\" instruction is {kind} VMs.", + fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)), + fmt::arg("kind", "not available in legacy bytecode") + ) + ); + } else if (m_eofVersion.has_value() && ( _instr == evmasm::Instruction::CALL || _instr == evmasm::Instruction::CALLCODE || _instr == evmasm::Instruction::DELEGATECALL || + _instr == evmasm::Instruction::STATICCALL || _instr == evmasm::Instruction::SELFDESTRUCT || _instr == evmasm::Instruction::JUMP || _instr == evmasm::Instruction::JUMPI || diff --git a/test/libyul/objectCompiler/eof/extcall.yul b/test/libyul/objectCompiler/eof/extcall.yul new file mode 100644 index 000000000000..c11ded5c52fb --- /dev/null +++ b/test/libyul/objectCompiler/eof/extcall.yul @@ -0,0 +1,90 @@ +object "a" { + code { + if extcall(address(), 0, 0, 10) { + return(0, 32) + } + + if extdelegatecall(address(), 0, 0) { + return(0, 32) + } + + if extstaticcall(address(), 0, 0) { + return(0, 32) + } + + revert(0, 0) + } +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: >=EOFv1 +// ---- +// Assembly: +// /* "source":74:76 */ +// 0x0a +// /* "source":71:72 */ +// 0x00 +// /* "source":57:66 */ +// dup1 +// address +// /* "source":49:77 */ +// extcall +// /* "source":46:95 */ +// rjumpi{tag_1} +// /* "source":22:266 */ +// tag_2: +// /* "source":141:142 */ +// 0x00 +// /* "source":127:136 */ +// dup1 +// address +// /* "source":111:143 */ +// extdelegatecall +// /* "source":108:161 */ +// rjumpi{tag_3} +// /* "source":22:266 */ +// tag_4: +// /* "source":205:206 */ +// 0x00 +// /* "source":191:200 */ +// dup1 +// address +// /* "source":177:207 */ +// extstaticcall +// /* "source":174:225 */ +// rjumpi{tag_5} +// /* "source":22:266 */ +// tag_6: +// /* "source":248:249 */ +// 0x00 +// /* "source":238:250 */ +// dup1 +// revert +// /* "source":208:225 */ +// tag_5: +// /* "source":220:222 */ +// 0x20 +// /* "source":217:218 */ +// 0x00 +// /* "source":210:223 */ +// return +// /* "source":144:161 */ +// tag_3: +// /* "source":156:158 */ +// 0x20 +// /* "source":153:154 */ +// 0x00 +// /* "source":146:159 */ +// return +// /* "source":78:95 */ +// tag_1: +// /* "source":90:92 */ +// 0x20 +// /* "source":87:88 */ +// 0x00 +// /* "source":80:93 */ +// return +// Bytecode: ef000101000402000100260400000000800004600a5f8030f8e100195f8030f9e1000e5f8030fbe100035f80fd60205ff360205ff360205ff3 +// Opcodes: 0xEF STOP ADD ADD STOP DIV MUL STOP ADD STOP 0x26 DIV STOP STOP STOP STOP DUP1 STOP DIV PUSH1 0xA PUSH0 DUP1 ADDRESS EXTCALL RJUMPI 0x19 PUSH0 DUP1 ADDRESS EXTDELEGATECALL RJUMPI 0xE PUSH0 DUP1 ADDRESS EXTSTATICCALL RJUMPI 0x3 PUSH0 DUP1 REVERT PUSH1 0x20 PUSH0 RETURN PUSH1 0x20 PUSH0 RETURN PUSH1 0x20 PUSH0 RETURN +// SourceMappings: 74:2:0:-:0;71:1;57:9;;49:28;46:49;22:244;141:1;127:9;;111:32;108:53;22:244;205:1;191:9;;177:30;174:51;22:244;248:1;238:12;;208:17;220:2;217:1;210:13;144:17;156:2;153:1;146:13;78:17;90:2;87:1;80:13 diff --git a/test/libyul/yulSyntaxTests/eof/extcalls.yul b/test/libyul/yulSyntaxTests/eof/extcalls.yul new file mode 100644 index 000000000000..4923e9275103 --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/extcalls.yul @@ -0,0 +1,25 @@ +object "a" { + code { + if extcall(address(), 0, 0) { + return(0, 32) + } + + if extdelegatecall(address(), 0, 0, 10) { + return(0, 32) + } + + if extstaticcall(address(), 0, 0, 10) { + return(0, 32) + } + + revert(0, 0) + } +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: >=EOFv1 +// ---- +// TypeError 7000: (35-42): Function "extcall" expects 4 arguments but got 3. +// TypeError 7000: (110-125): Function "extdelegatecall" expects 3 arguments but got 4. +// TypeError 7000: (197-210): Function "extstaticcall" expects 3 arguments but got 4. diff --git a/test/libyul/yulSyntaxTests/eof/extcalls_invalid_in_legacy.yul b/test/libyul/yulSyntaxTests/eof/extcalls_invalid_in_legacy.yul new file mode 100644 index 000000000000..9b26f4e27de0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/extcalls_invalid_in_legacy.yul @@ -0,0 +1,28 @@ +object "a" { + code { + if extcall(address(), 0, 0, 0) { + return(0, 32) + } + + if extdelegatecall(address(), 0, 0) { + return(0, 32) + } + + if extstaticcall(address(), 0, 0) { + return(0, 32) + } + + revert(0, 0) + } +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: legacy +// ---- +// TypeError 4328: (35-42): The "extcall" instruction is not available in legacy bytecode VMs. +// TypeError 3950: (35-62): Expected expression to evaluate to one value, but got 0 values instead. +// TypeError 4328: (113-128): The "extdelegatecall" instruction is not available in legacy bytecode VMs. +// TypeError 3950: (113-145): Expected expression to evaluate to one value, but got 0 values instead. +// TypeError 4328: (196-209): The "extstaticcall" instruction is not available in legacy bytecode VMs. +// TypeError 3950: (196-226): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libyul/yulSyntaxTests/eof/legacy_calls_in_eof.yul b/test/libyul/yulSyntaxTests/eof/legacy_calls_in_eof.yul new file mode 100644 index 000000000000..d9c1772b3170 --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/legacy_calls_in_eof.yul @@ -0,0 +1,26 @@ +object "a" { + code { + if call(address(), 0, 0, 10) { + return(0, 32) + } + if staticcall(address(), 0, 0) { + return(0, 32) + } + if delegatecall(address(), 0, 0) { + return(0, 32) + } + + revert(0, 0) + } +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: >=EOFv1 +// ---- +// TypeError 9132: (35-39): The "call" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF). +// TypeError 3950: (35-60): Expected expression to evaluate to one value, but got 0 values instead. +// TypeError 9132: (110-120): The "staticcall" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF). +// TypeError 3950: (110-137): Expected expression to evaluate to one value, but got 0 values instead. +// TypeError 9132: (187-199): The "delegatecall" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF). +// TypeError 3950: (187-216): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 92ac56d6919f..94a6c3b1ee3f 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -495,6 +495,9 @@ u256 EVMInstructionInterpreter::eval( case Instruction::RETURNCONTRACT: case Instruction::RJUMP: case Instruction::RJUMPI: + case Instruction::EXTCALL: + case Instruction::EXTSTATICCALL: + case Instruction::EXTDELEGATECALL: solUnimplemented("EOF not yet supported by Yul interpreter."); }