Skip to content

Commit

Permalink
eof: Support DATALOADN (EIP-7480)
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Oct 1, 2024
1 parent d01832c commit 25276e2
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 15 deletions.
55 changes: 49 additions & 6 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, {});
Expand Down Expand Up @@ -923,7 +928,7 @@ std::tuple<bytes, std::vector<size_t>, size_t> Assembly::createEOFHeader(std::se
{
bytes retBytecode;
std::vector<size_t> codeSectionSizeOffsets;
size_t dataSectionSizeOffset;
size_t dataSectionSizePosition;

retBytecode.push_back(0xef);
retBytecode.push_back(0x00);
Expand Down Expand Up @@ -952,7 +957,7 @@ std::tuple<bytes, std::vector<size_t>, 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
Expand All @@ -965,7 +970,7 @@ std::tuple<bytes, std::vector<size_t>, size_t> Assembly::createEOFHeader(std::se
appendBigEndianUint16(retBytecode, 0xFFFFu);
}

return {retBytecode, codeSectionSizeOffsets, dataSectionSizeOffset};
return {retBytecode, codeSectionSizeOffsets, dataSectionSizePosition};
}

LinkerObject const& Assembly::assemble() const
Expand Down Expand Up @@ -1348,6 +1353,23 @@ std::map<uint16_t, uint16_t> Assembly::findReferencedContainers() const
return replacements;
}

std::optional<uint16_t> Assembly::findMaxAuxDataLoadNOffset() const
{
std::optional<unsigned> result = std::nullopt;
for (auto&& codeSection: m_codeSections)
for (AssemblyItem const& i: codeSection.items)
if (i.type() == AuxDataLoadN)
{
solAssert(i.data() <= std::numeric_limits<uint16_t>::max(), "Invalid auxdataloadn index value.");
auto const offset = static_cast<unsigned>(i.data());
if (!result.has_value() || offset > result.value())
result = offset;

}

return result;
}

LinkerObject const& Assembly::assembleEOF() const
{
solAssert(m_eofVersion.has_value() && m_eofVersion == 1);
Expand All @@ -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, codeSectionSizeOffsets, dataSectionSizePosition] = createEOFHeader(referencedSubIds);
ret.bytecode = headerBytecode;

m_tagPositionsInBytecode = std::vector<size_t>(m_usedTags, std::numeric_limits<size_t>::max());
std::map<size_t, uint16_t> dataSectionRef;

for (auto&& [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate)
{
Expand All @@ -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:
Expand All @@ -1401,6 +1428,14 @@ LinkerObject const& Assembly::assembleEOF() const
case Tag:
ret.bytecode += assembleTag(item, ret.bytecode.size(), false);
break;
case AuxDataLoadN:
{
assertThrow(item.data() <= std::numeric_limits<uint16_t>::max(), AssemblyException, "Invalid auxdataloadn position.");
ret.bytecode.push_back(uint8_t(Instruction::DATALOADN));
dataSectionRef[ret.bytecode.size()] = static_cast<uint16_t>(item.data());
appendBigEndianUint16(ret.bytecode, item.data());
break;
}
default:
solThrow(InvalidOpcode, "Unexpected opcode while assembling.");
}
Expand All @@ -1422,8 +1457,16 @@ LinkerObject const& Assembly::assembleEOF() const

ret.bytecode += m_auxiliaryData;

auto dataLength = ret.bytecode.size() - dataStart;
setBigEndianUint16(ret.bytecode, dataSectionSizeOffset, dataLength);
auto appendedDataAndAuxDataSize = ret.bytecode.size() - dataStart;

// If some data was already added to data section we need to update data section refs accordigly
if (appendedDataAndAuxDataSize > 0)
for (auto [refPosition, originalDataOffset] : dataSectionRef)
setBigEndian(ret.bytecode, refPosition, 2, originalDataOffset + appendedDataAndAuxDataSize);

auto dataLength = appendedDataAndAuxDataSize + (maxAuxDataLoadNOffset.has_value() ? (maxAuxDataLoadNOffset.value() + 32u) : 0u);

setBigEndianUint16(ret.bytecode, dataSectionSizePosition, dataLength);

return ret;
}
Expand Down
5 changes: 5 additions & 0 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)); }
Expand All @@ -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(size_t offset) { append(newAuxDataLoadN(offset));}

void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables)
{
Expand Down Expand Up @@ -240,6 +242,9 @@ class Assembly

/// Returns map from m_subs to an index of subcontainer in the final EOF bytecode
std::map<uint16_t, uint16_t> findReferencedContainers() const;
/// Returns max AuxDataLoadN offset for the assembly.
/// TODO: Can and should be merged with findReferencedContainers to avoid additional run
std::optional<uint16_t> findMaxAuxDataLoadNOffset() const;

/// Assemble bytecode for AssemblyItem type.
[[nodiscard]] bytes assembleOperation(AssemblyItem const& _item) const;
Expand Down
28 changes: 20 additions & 8 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
return {"PUSH data", toStringInHex(data())};
case VerbatimBytecode:
return {"VERBATIM", util::toHex(verbatimData())};
default:
case AuxDataLoadN:
return {"AUXDATALOADN", util::toString(data())};
case UndefinedItem:
assertThrow(false, InvalidOpcode, "");
}
}
Expand Down Expand Up @@ -161,7 +163,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
}
case VerbatimBytecode:
return std::get<2>(*m_verbatimBytecode).size();
default:
case AuxDataLoadN:
return 1 + 2;
case UndefinedItem:
break;
}
assertThrow(false, InvalidOpcode, "");
Expand Down Expand Up @@ -203,7 +207,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;
Expand All @@ -226,10 +233,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;
Expand Down Expand Up @@ -327,8 +337,9 @@ 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:
text = "auxdataloadn(" + std::to_string(static_cast<size_t>(data())) + ")";
break;
}
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
{
Expand Down Expand Up @@ -396,11 +407,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;
}
Expand Down
4 changes: 4 additions & 0 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};

Expand Down
2 changes: 2 additions & 0 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ std::map<std::string, Instruction> 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 },
Expand Down Expand Up @@ -252,6 +253,7 @@ static std::map<Instruction, InstructionInfo> 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}},
Expand Down
1 change: 1 addition & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions liblangutil/EVMVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
case Instruction::EXTCODECOPY:
case Instruction::GAS:
return !_eofVersion.has_value();
// Instructions below available only in EOF
case Instruction::DATALOADN:
return _eofVersion.has_value() && this->m_version >= prague();
default:
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions libyul/backends/evm/AbstractAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ class AbstractAssembly
/// Appends an assignment to an immutable variable.
virtual void appendImmutableAssignment(std::string const& _identifier) = 0;

/// Appends 32 bytes data load from EOF data section in dataOffset pos
virtual void appendAuxDataLoadN(size_t _dataOffset) = 0;

/// Appends data to the very end of the bytecode. Repeated calls concatenate.
virtual void appendToAuxiliaryData(bytes const& _data) = 0;

Expand Down
21 changes: 20 additions & 1 deletion libyul/backends/evm/EVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ std::map<YulName, BuiltinFunctionForEVM> 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)
)
Expand Down Expand Up @@ -235,7 +236,7 @@ std::map<YulName, BuiltinFunctionForEVM> 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,
Expand Down Expand Up @@ -345,6 +346,24 @@ std::map<YulName, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _ev
}
));
}
else // EOF context
{
builtins.emplace(createFunction(
"auxdataloadn",
1,
1,
SideEffects{},
{LiteralKind::String},
[](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext&
) {
yulAssert(_call.arguments.size() == 1, "");
_assembly.appendAuxDataLoadN(std::stoul(formatLiteral(std::get<Literal>(_call.arguments.front()))));
}
));
}
}
return builtins;
}
Expand Down
5 changes: 5 additions & 0 deletions libyul/backends/evm/EthAssemblyAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ void EthAssemblyAdapter::appendImmutableAssignment(std::string const& _identifie
m_assembly.appendImmutableAssignment(_identifier);
}

void EthAssemblyAdapter::appendAuxDataLoadN(size_t _dataOffset)
{
m_assembly.appendAuxDataLoadN(_dataOffset);
}

void EthAssemblyAdapter::markAsInvalid()
{
m_assembly.markAsInvalid();
Expand Down
2 changes: 2 additions & 0 deletions libyul/backends/evm/EthAssemblyAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class EthAssemblyAdapter: public AbstractAssembly
void appendImmutable(std::string const& _identifier) override;
void appendImmutableAssignment(std::string const& _identifier) override;

void appendAuxDataLoadN(size_t dataOffset) override;

void markAsInvalid() override;

langutil::EVMVersion evmVersion() const override;
Expand Down
5 changes: 5 additions & 0 deletions libyul/backends/evm/NoOutputAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ void NoOutputAssembly::appendImmutableAssignment(std::string const&)
yulAssert(false, "setimmutable not implemented.");
}

void NoOutputAssembly::appendAuxDataLoadN(size_t)
{
yulAssert(false, "auxdataloadn not implemented.");
}

NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom):
EVMDialect(_copyFrom.evmVersion(), _copyFrom.eofVersion(), _copyFrom.providesObjectAccess())
{
Expand Down
2 changes: 2 additions & 0 deletions libyul/backends/evm/NoOutputAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class NoOutputAssembly: public AbstractAssembly
void appendImmutable(std::string const& _identifier) override;
void appendImmutableAssignment(std::string const& _identifier) override;

void appendAuxDataLoadN(size_t) override;

void markAsInvalid() override {}

langutil::EVMVersion evmVersion() const override { return m_evmVersion; }
Expand Down
1 change: 1 addition & 0 deletions test/cmdlineTests/strict_asm_eof_dataloadn_prague/args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--strict-assembly --experimental-eof-version 1 --evm-version prague --asm --ir-optimized --bin --debug-info none
9 changes: 9 additions & 0 deletions test/cmdlineTests/strict_asm_eof_dataloadn_prague/input.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
object "a" {
code {
mstore(0, auxdataloadn("0"))
return(0, 32)
}

data "data1" "Hello, World!"
}

Loading

0 comments on commit 25276e2

Please sign in to comment.