diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index ab26526e14c2..8f645f6fddcb 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -717,6 +717,11 @@ AssemblyItem Assembly::newImmutableAssignment(std::string const& _identifier) return AssemblyItem{AssignImmutable, h}; } +AssemblyItem Assembly::newAuxDataLoadN(size_t _offset) +{ + return AssemblyItem{AuxDataLoadN, _offset}; +} + Assembly& Assembly::optimise(OptimiserSettings const& _settings) { optimiseInternal(_settings, {}); @@ -922,8 +927,8 @@ void appendBigEndianUint16(bytes& _dest, ValueT _value) std::tuple, size_t> Assembly::createEOFHeader(std::set const& _referencedSubIds) const { bytes retBytecode; - std::vector codeSectionSizeOffsets; - size_t dataSectionSizeOffset; + std::vector codeSectionSizePositions; + size_t dataSectionSizePosition; retBytecode.push_back(0xef); retBytecode.push_back(0x00); @@ -938,7 +943,7 @@ std::tuple, size_t> Assembly::createEOFHeader(std::se for (auto const& codeSection: m_codeSections) { (void) codeSection; - codeSectionSizeOffsets.emplace_back(retBytecode.size()); + codeSectionSizePositions.emplace_back(retBytecode.size()); appendBigEndianUint16(retBytecode, 0u); // placeholder for length of code } @@ -952,7 +957,7 @@ std::tuple, size_t> Assembly::createEOFHeader(std::se } retBytecode.push_back(0x04); // kind=data - dataSectionSizeOffset = retBytecode.size(); + dataSectionSizePosition = retBytecode.size(); appendBigEndianUint16(retBytecode, 0u); // length of data retBytecode.push_back(0x00); // terminator @@ -965,7 +970,7 @@ std::tuple, size_t> Assembly::createEOFHeader(std::se appendBigEndianUint16(retBytecode, 0xFFFFu); } - return {retBytecode, codeSectionSizeOffsets, dataSectionSizeOffset}; + return {retBytecode, codeSectionSizePositions, dataSectionSizePosition}; } LinkerObject const& Assembly::assemble() const @@ -1348,6 +1353,23 @@ std::map Assembly::findReferencedContainers() const return replacements; } +std::optional Assembly::findMaxAuxDataLoadNOffset() const +{ + std::optional maxOffset = std::nullopt; + for (auto&& codeSection: m_codeSections) + for (AssemblyItem const& item: codeSection.items) + if (item.type() == AuxDataLoadN) + { + solAssert(item.data() <= std::numeric_limits::max(), "Invalid auxdataloadn index value."); + auto const offset = static_cast(item.data()); + if (!maxOffset.has_value() || offset > maxOffset.value()) + maxOffset = offset; + + } + + return maxOffset; +} + LinkerObject const& Assembly::assembleEOF() const { solAssert(m_eofVersion.has_value() && m_eofVersion == 1); @@ -1362,11 +1384,14 @@ LinkerObject const& Assembly::assembleEOF() const "Expected the first code section to have zero inputs and be non-returning." ); + auto const maxAuxDataLoadNOffset = findMaxAuxDataLoadNOffset(); + // Insert EOF1 header. - auto [headerBytecode, codeSectionSizeOffsets, dataSectionSizeOffset] = createEOFHeader(referencedSubIds); + auto [headerBytecode, codeSectionSizePositions, dataSectionSizePosition] = createEOFHeader(referencedSubIds); ret.bytecode = headerBytecode; m_tagPositionsInBytecode = std::vector(m_usedTags, std::numeric_limits::max()); + std::map dataSectionRef; for (auto&& [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate) { @@ -1380,6 +1405,8 @@ LinkerObject const& Assembly::assembleEOF() const switch (item.type()) { case Operation: + solAssert(item.instruction() != Instruction::DATALOADN); + solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32)); ret.bytecode += assembleOperation(item); break; case Push: @@ -1401,12 +1428,21 @@ LinkerObject const& Assembly::assembleEOF() const case Tag: ret.bytecode += assembleTag(item, ret.bytecode.size(), false); break; + case AuxDataLoadN: + { + // In findMaxAuxDataLoadNOffset we already verified that unsigned data value fits 2 bytes + solAssert(item.data() <= std::numeric_limits::max(), "Invalid auxdataloadn position."); + ret.bytecode.push_back(uint8_t(Instruction::DATALOADN)); + dataSectionRef[ret.bytecode.size()] = static_cast(item.data()); + appendBigEndianUint16(ret.bytecode, item.data()); + break; + } default: solThrow(InvalidOpcode, "Unexpected opcode while assembling."); } } - setBigEndianUint16(ret.bytecode, codeSectionSizeOffsets[codeSectionIndex], ret.bytecode.size() - sectionStart); + setBigEndianUint16(ret.bytecode, codeSectionSizePositions[codeSectionIndex], ret.bytecode.size() - sectionStart); } for (auto i: referencedSubIds) @@ -1422,8 +1458,25 @@ LinkerObject const& Assembly::assembleEOF() const ret.bytecode += m_auxiliaryData; - auto dataLength = ret.bytecode.size() - dataStart; - setBigEndianUint16(ret.bytecode, dataSectionSizeOffset, dataLength); + auto const preDeployDataSectionSize = ret.bytecode.size() - dataStart; + // DATALOADN loads 32 bytes from EOF data section zero padded if reading out of data bounds. + // In our case we do not allow DATALOADN with offsets which reads out of data bounds. + auto const staticAuxDataSize = maxAuxDataLoadNOffset.has_value() ? (*maxAuxDataLoadNOffset + 32u) : 0u; + solRequire(preDeployDataSectionSize + staticAuxDataSize < std::numeric_limits::max(), AssemblyException, + "Invalid DATALOADN offset."); + + // If some data was already added to data section we need to update data section refs accordingly + if (preDeployDataSectionSize > 0) + for (auto [refPosition, staticAuxDataOffset] : dataSectionRef) + { + // staticAuxDataOffset + preDeployDataSectionSize value is already verified to fit 2 bytes because + // staticAuxDataOffset < staticAuxDataSize + setBigEndianUint16(ret.bytecode, refPosition, staticAuxDataOffset + preDeployDataSectionSize); + } + + auto const preDeployAndStaticAuxDataSize = preDeployDataSectionSize + staticAuxDataSize; + + setBigEndianUint16(ret.bytecode, dataSectionSizePosition, preDeployAndStaticAuxDataSize); return ret; } diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index de1ac20f2015..9b15936d642f 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -79,6 +79,7 @@ class Assembly AssemblyItem newPushLibraryAddress(std::string const& _identifier); AssemblyItem newPushImmutable(std::string const& _identifier); AssemblyItem newImmutableAssignment(std::string const& _identifier); + AssemblyItem newAuxDataLoadN(size_t offset); AssemblyItem const& append(AssemblyItem _i); AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } @@ -91,6 +92,7 @@ class Assembly void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); } void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); } void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); } + void appendAuxDataLoadN(uint16_t _offset) { append(newAuxDataLoadN(_offset));} void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) { @@ -240,6 +242,8 @@ class Assembly /// Returns map from m_subs to an index of subcontainer in the final EOF bytecode std::map findReferencedContainers() const; + /// Returns max AuxDataLoadN offset for the assembly. + std::optional findMaxAuxDataLoadNOffset() const; /// Assemble bytecode for AssemblyItem type. [[nodiscard]] bytes assembleOperation(AssemblyItem const& _item) const; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 1bc805780145..1f4d92507647 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -102,9 +102,13 @@ std::pair AssemblyItem::nameAndData(langutil::EVMVersi return {"PUSH data", toStringInHex(data())}; case VerbatimBytecode: return {"VERBATIM", util::toHex(verbatimData())}; - default: - assertThrow(false, InvalidOpcode, ""); + case AuxDataLoadN: + return {"AUXDATALOADN", util::toString(data())}; + case UndefinedItem: + solAssert(false); } + + util::unreachable(); } void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) @@ -161,10 +165,13 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _ } case VerbatimBytecode: return std::get<2>(*m_verbatimBytecode).size(); - default: - break; + case AuxDataLoadN: + return 1 + 2; + case UndefinedItem: + solAssert(false); } - assertThrow(false, InvalidOpcode, ""); + + util::unreachable(); } size_t AssemblyItem::arguments() const @@ -203,7 +210,10 @@ size_t AssemblyItem::returnValues() const return 0; case VerbatimBytecode: return std::get<1>(*m_verbatimBytecode); - default: + case AuxDataLoadN: + return 1; + case AssignImmutable: + case UndefinedItem: break; } return 0; @@ -226,10 +236,13 @@ bool AssemblyItem::canBeFunctional() const case PushLibraryAddress: case PushDeployTimeAddress: case PushImmutable: + case AuxDataLoadN: return true; case Tag: return false; - default: + case AssignImmutable: + case VerbatimBytecode: + case UndefinedItem: break; } return false; @@ -327,8 +340,10 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const case VerbatimBytecode: text = std::string("verbatimbytecode_") + util::toHex(std::get<2>(*m_verbatimBytecode)); break; - default: - assertThrow(false, InvalidOpcode, ""); + case AuxDataLoadN: + assertThrow(data() <= std::numeric_limits::max(), AssemblyException, "Invalid auxdataloadn argument."); + text = "auxdataloadn(" + std::to_string(static_cast(data())) + ")"; + break; } if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) { @@ -396,11 +411,12 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons case VerbatimBytecode: _out << " Verbatim " << util::toHex(_item.verbatimData()); break; + case AuxDataLoadN: + _out << "AuxDataLoadN " << util::toString(_item.data()); + break; case UndefinedItem: _out << " ???"; break; - default: - assertThrow(false, InvalidOpcode, ""); } return _out; } diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index f06c72bac2ff..226a126c368c 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -51,6 +51,10 @@ enum AssemblyItemType PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. + + /// Loads 32 bytes from static auxiliary data of EOF data section. The offset does *not* have to be always from the beginning + /// of the data EOF section. More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle + AuxDataLoadN, VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification. }; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 943f34ee4c1f..b9bb21227c29 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -168,6 +168,7 @@ std::map const solidity::evmasm::c_instructions = { "LOG2", Instruction::LOG2 }, { "LOG3", Instruction::LOG3 }, { "LOG4", Instruction::LOG4 }, + { "DATALOADN", Instruction::DATALOADN }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, { "CALLCODE", Instruction::CALLCODE }, @@ -252,6 +253,7 @@ static std::map const c_instructionInfo = {Instruction::MSIZE, {"MSIZE", 0, 0, 1, false, Tier::Base}}, {Instruction::GAS, {"GAS", 0, 0, 1, false, Tier::Base}}, {Instruction::JUMPDEST, {"JUMPDEST", 0, 0, 0, true, Tier::Special}}, + {Instruction::DATALOADN, {"DATALOADN", 2, 0, 1, true, Tier::Low}}, {Instruction::PUSH0, {"PUSH0", 0, 0, 1, false, Tier::Base}}, {Instruction::PUSH1, {"PUSH1", 1, 0, 1, false, Tier::VeryLow}}, {Instruction::PUSH2, {"PUSH2", 2, 0, 1, false, Tier::VeryLow}}, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index a3629eec2db8..2ed773cc9e7b 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -182,6 +182,7 @@ enum class Instruction: uint8_t LOG3, ///< Makes a log entry; 3 topics. LOG4, ///< Makes a log entry; 4 topics. + DATALOADN = 0xd1, ///< load data from EOF data section CREATE = 0xf0, ///< create a new account with associated code CALL, ///< message-call into an account CALLCODE, ///< message-call with another account's code only diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 6435e0f15455..bdd663c411ac 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -76,6 +76,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional _eofVersi case Instruction::EXTCODECOPY: case Instruction::GAS: return !_eofVersion.has_value(); + // Instructions below available only in EOF + case Instruction::DATALOADN: + return _eofVersion.has_value(); default: return true; } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 682931dc414d..9eb5465475cb 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -428,6 +428,20 @@ size_t AsmAnalyzer::operator()(FunctionCall const& _funCall) expectUnlimitedStringLiteral(std::get(arg)); continue; } + else if (*literalArgumentKind == LiteralKind::Number) + { + std::string functionName = _funCall.functionName.name.str(); + if (functionName == "auxdataloadn") + { + auto const& argumentAsLiteral = std::get(arg); + if (argumentAsLiteral.value.value() > std::numeric_limits::max()) + m_errorReporter.typeError( + 5202_error, + nativeLocationOf(arg), + "Invalid auxdataloadn argument value. Offset must be in range 0...0xFFFF" + ); + } + } } expectExpression(arg); } diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index fe45008962c8..ed06bebac90b 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -112,7 +112,14 @@ class AbstractAssembly /// Appends an assignment to an immutable variable. virtual void appendImmutableAssignment(std::string const& _identifier) = 0; + /// Appends an operation that loads 32 bytes of data from a known offset relative to the start of the static_aux_data area of the EOF data section. + /// Note that static_aux_data is only a part or the data section. + /// It is preceded by the pre_deploy_data, whose size is not determined before the bytecode is assembled, and which cannot be accessed using this function. + /// The function is meant to allow indexing into static_aux_data in a way that's independent of the size of pre_deploy_data. + virtual void appendAuxDataLoadN(uint16_t _offset) = 0; + /// Appends data to the very end of the bytecode. Repeated calls concatenate. + /// EOF auxiliary data in data section and the auxiliary data are different things. virtual void appendToAuxiliaryData(bytes const& _data) = 0; /// Mark this assembly as invalid. Any attempt to request bytecode from it should throw. diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 6bfdc9cb9de3..9e9d5e542ca6 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -199,6 +199,7 @@ std::map createBuiltins(langutil::EVMVersion _ev opcode != evmasm::Instruction::JUMP && opcode != evmasm::Instruction::JUMPI && opcode != evmasm::Instruction::JUMPDEST && + opcode != evmasm::Instruction::DATALOADN && _evmVersion.hasOpcode(opcode, _eofVersion) && !prevRandaoException(name) ) @@ -235,7 +236,7 @@ std::map createBuiltins(langutil::EVMVersion _ev }) ); - if (!_eofVersion.has_value()) + if (!_eofVersion.has_value()) // non-EOF context { builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, @@ -345,6 +346,27 @@ std::map createBuiltins(langutil::EVMVersion _ev } )); } + else // EOF context + { + builtins.emplace(createFunction( + "auxdataloadn", + 1, + 1, + SideEffects{}, + {LiteralKind::Number}, + []( + FunctionCall const& _call, + AbstractAssembly& _assembly, + BuiltinContext& + ) { + yulAssert(_call.arguments.size() == 1, ""); + Literal const* literal = std::get_if(&_call.arguments.front()); + yulAssert(literal, ""); + yulAssert(literal->value.value() <= std::numeric_limits::max(), ""); + _assembly.appendAuxDataLoadN(static_cast(literal->value.value())); + } + )); + } } return builtins; } diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index e3a2e01e1b1f..36a437c8cecf 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -175,6 +175,11 @@ void EthAssemblyAdapter::appendImmutableAssignment(std::string const& _identifie m_assembly.appendImmutableAssignment(_identifier); } +void EthAssemblyAdapter::appendAuxDataLoadN(uint16_t _offset) +{ + m_assembly.appendAuxDataLoadN(_offset); +} + void EthAssemblyAdapter::markAsInvalid() { m_assembly.markAsInvalid(); diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 011081dedaac..7dc89e38e0e9 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -65,6 +65,8 @@ class EthAssemblyAdapter: public AbstractAssembly void appendImmutable(std::string const& _identifier) override; void appendImmutableAssignment(std::string const& _identifier) override; + void appendAuxDataLoadN(uint16_t _offset) override; + void markAsInvalid() override; langutil::EVMVersion evmVersion() const override; diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index c444a71435c0..9d8bd73a3465 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -129,6 +129,11 @@ void NoOutputAssembly::appendImmutableAssignment(std::string const&) yulAssert(false, "setimmutable not implemented."); } +void NoOutputAssembly::appendAuxDataLoadN(uint16_t) +{ + yulAssert(false, "auxdataloadn not implemented."); +} + NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): EVMDialect(_copyFrom.evmVersion(), _copyFrom.eofVersion(), _copyFrom.providesObjectAccess()) { diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 8d7dda0bb50c..6d3aacb248a0 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -75,6 +75,8 @@ class NoOutputAssembly: public AbstractAssembly void appendImmutable(std::string const& _identifier) override; void appendImmutableAssignment(std::string const& _identifier) override; + void appendAuxDataLoadN(uint16_t) override; + void markAsInvalid() override {} langutil::EVMVersion evmVersion() const override { return m_evmVersion; } diff --git a/test/cmdlineTests/strict_asm_eof_dataloadn_prague/args b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/args new file mode 100644 index 000000000000..0078e6e68e36 --- /dev/null +++ b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/args @@ -0,0 +1 @@ + --strict-assembly --experimental-eof-version 1 --evm-version prague --asm --ir-optimized --bin --debug-info none diff --git a/test/cmdlineTests/strict_asm_eof_dataloadn_prague/input.yul b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/input.yul new file mode 100644 index 000000000000..bd29cf65979c --- /dev/null +++ b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/input.yul @@ -0,0 +1,9 @@ +object "a" { + code { + mstore(0, auxdataloadn(0)) + return(0, 32) + } + + data "data1" "Hello, World!" +} + diff --git a/test/cmdlineTests/strict_asm_eof_dataloadn_prague/output b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/output new file mode 100644 index 000000000000..5f768feebd32 --- /dev/null +++ b/test/cmdlineTests/strict_asm_eof_dataloadn_prague/output @@ -0,0 +1,27 @@ + +======= strict_asm_eof_dataloadn_prague/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + { + mstore(0, auxdataloadn(0)) + return(0, 32) + } + } + data "data1" hex"48656c6c6f2c20576f726c6421" +} + + +Binary representation: +ef0001010004020001000904002d000080ffffd1000d5f5260205ff348656c6c6f2c20576f726c6421 + +Text representation: + auxdataloadn(0) + 0x00 + mstore + 0x20 + 0x00 + return +stop +data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 diff --git a/test/libyul/objectCompiler/dataloadn.yul b/test/libyul/objectCompiler/dataloadn.yul new file mode 100644 index 000000000000..8b1c7ab8015c --- /dev/null +++ b/test/libyul/objectCompiler/dataloadn.yul @@ -0,0 +1,32 @@ +object "a" { + code { + { + mstore(0, auxdataloadn(0)) + return(0, 32) + } + } + data "data1" hex"48656c6c6f2c20576f726c6421" +} + +// ==== +// bytecodeFormat: >=EOFv1 +// EVMVersion: >=prague +// ---- +// Assembly: +// /* "source":56:71 */ +// auxdataloadn(0) +// /* "source":53:54 */ +// 0x00 +// /* "source":46:72 */ +// mstore +// /* "source":95:97 */ +// 0x20 +// /* "source":92:93 */ +// 0x00 +// /* "source":85:98 */ +// return +// stop +// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421 +// Bytecode: ef0001010004020001000904002d000080ffffd1000d5f5260205ff348656c6c6f2c20576f726c6421 +// Opcodes: 0xEF STOP ADD ADD STOP DIV MUL STOP ADD STOP MULMOD DIV STOP 0x2D STOP STOP DUP1 SELFDESTRUCT SELFDESTRUCT DATALOADN 0xD PUSH0 MSTORE PUSH1 0x20 PUSH0 RETURN BASEFEE PUSH6 0x6C6C6F2C2057 PUSH16 0x726C6421000000000000000000000000 +// SourceMappings: 56:15:0:-:0;53:1;46:26;95:2;92:1;85:13 \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof.yul b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof.yul new file mode 100644 index 000000000000..552620a72a0e --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof.yul @@ -0,0 +1,12 @@ +object "a" { + code { + mstore(0, auxdataloadn(0)) + return(0, 32) + } + + data "data1" "Hello, World!" +} + +// ==== +// EVMVersion: >=shanghai +// bytecodeFormat: >=EOFv1 \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_literal_type.yul b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_literal_type.yul new file mode 100644 index 000000000000..3e77b9377953 --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_literal_type.yul @@ -0,0 +1,14 @@ +object "a" { + code { + mstore(0, auxdataloadn("0")) + return(0, 32) + } + + data "data1" "Hello, World!" + } + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: >=EOFv1 +// ---- +// TypeError 5859: (49-52): Function expects number literal. \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_value.yul b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_value.yul new file mode 100644 index 000000000000..344fa5a687af --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_eof_invalid_value.yul @@ -0,0 +1,14 @@ +object "a" { + code { + mstore(0, auxdataloadn(0x01FFFF)) + return(0, 32) + } + + data "data1" "Hello, World!" +} + +// ==== +// EVMVersion: >=shanghai +// bytecodeFormat: >=EOFv1 +// ---- +// TypeError 5202: (49-57): Invalid auxdataloadn argument value. Offset must be in range 0...0xFFFF diff --git a/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_legacy.yul b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_legacy.yul new file mode 100644 index 000000000000..8aba21dd5394 --- /dev/null +++ b/test/libyul/yulSyntaxTests/eof/auxdataloadn_in_legacy.yul @@ -0,0 +1,15 @@ +object "a" { + code { + mstore(0, auxdataloadn(0)) + return(0, 32) + } + + data "data1" "Hello, World!" +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: legacy +// ---- +// DeclarationError 4619: (36-48): Function "auxdataloadn" not found. +// TypeError 3950: (36-51): Expected expression to evaluate to one value, but got 0 values instead. \ No newline at end of file diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index d2cbdb11b360..768734458bec 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -417,7 +418,7 @@ u256 EVMInstructionInterpreter::eval( m_state.trace.clear(); BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); case Instruction::POP: - break; + return 0; // --------------- invalid in strict assembly --------------- case Instruction::JUMP: case Instruction::JUMPI: @@ -487,13 +488,12 @@ u256 EVMInstructionInterpreter::eval( case Instruction::SWAP14: case Instruction::SWAP15: case Instruction::SWAP16: - { - yulAssert(false, ""); - return 0; - } + yulAssert(false, "Impossible in strict assembly."); + case Instruction::DATALOADN: + solUnimplemented("DATALOADN unimplemented in yul interpreter."); } - return 0; + util::unreachable(); } u256 EVMInstructionInterpreter::evalBuiltin(