Skip to content

Commit 8b92fee

Browse files
committed
eof: Enable constant optimizer for EOF.
1 parent db53ca5 commit 8b92fee

File tree

9 files changed

+4220
-70
lines changed

9 files changed

+4220
-70
lines changed

Diff for: libevmasm/Assembly.cpp

+21-6
Original file line numberDiff line numberDiff line change
@@ -940,13 +940,13 @@ std::map<u256, u256> const& Assembly::optimiseInternal(
940940
}
941941
}
942942

943-
// TODO: investigate for EOF
944-
if (_settings.runConstantOptimiser && !m_eofVersion.has_value())
943+
if (_settings.runConstantOptimiser)
945944
ConstantOptimisationMethod::optimiseConstants(
946945
isCreation(),
947946
isCreation() ? 1 : _settings.expectedExecutionsPerDeployment,
948947
m_evmVersion,
949-
*this
948+
*this,
949+
m_eofVersion
950950
);
951951

952952
m_tagReplacements = std::move(tagReplacements);
@@ -1601,7 +1601,8 @@ LinkerObject const& Assembly::assembleEOF() const
16011601
ret.bytecode = headerBytecode;
16021602

16031603
m_tagPositionsInBytecode = std::vector<size_t>(m_usedTags, std::numeric_limits<size_t>::max());
1604-
std::map<size_t, uint16_t> dataSectionRef;
1604+
std::map<size_t, uint16_t> staticAuxDataSectionRef;
1605+
std::map<h256, std::vector<size_t>> predeployDataSectionRef;
16051606
std::map<size_t, size_t> tagRef;
16061607

16071608
for (auto&& [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate)
@@ -1677,12 +1678,19 @@ LinkerObject const& Assembly::assembleEOF() const
16771678
case Tag:
16781679
ret.bytecode += assembleTag(item, ret.bytecode.size(), false);
16791680
break;
1681+
case DataLoadN:
1682+
{
1683+
ret.bytecode += assembleOperation(item);
1684+
predeployDataSectionRef[item.data()].push_back(ret.bytecode.size());
1685+
appendBigEndianUint16(ret.bytecode, 0u);
1686+
break;
1687+
}
16801688
case AuxDataLoadN:
16811689
{
16821690
// In findMaxAuxDataLoadNOffset we already verified that unsigned data value fits 2 bytes
16831691
solAssert(item.data() <= std::numeric_limits<uint16_t>::max(), "Invalid auxdataloadn position.");
16841692
ret.bytecode.push_back(uint8_t(Instruction::DATALOADN));
1685-
dataSectionRef[ret.bytecode.size()] = static_cast<uint16_t>(item.data());
1693+
staticAuxDataSectionRef[ret.bytecode.size()] = static_cast<uint16_t>(item.data());
16861694
appendBigEndianUint16(ret.bytecode, item.data());
16871695
break;
16881696
}
@@ -1765,7 +1773,14 @@ LinkerObject const& Assembly::assembleEOF() const
17651773
auto const dataStart = ret.bytecode.size();
17661774

17671775
for (auto const& dataItem: m_data)
1776+
{
1777+
for (auto const pos: predeployDataSectionRef[dataItem.first])
1778+
{
1779+
solAssert(dataItem.second.size() == 32);
1780+
setBigEndianUint16(ret.bytecode, pos, ret.bytecode.size() - dataStart);
1781+
}
17681782
ret.bytecode += dataItem.second;
1783+
}
17691784

17701785
ret.bytecode += m_auxiliaryData;
17711786

@@ -1784,7 +1799,7 @@ LinkerObject const& Assembly::assembleEOF() const
17841799

17851800
// If some data was already added to data section we need to update data section refs accordingly
17861801
if (preDeployDataSectionSize > 0)
1787-
for (auto [refPosition, staticAuxDataOffset] : dataSectionRef)
1802+
for (auto [refPosition, staticAuxDataOffset] : staticAuxDataSectionRef)
17881803
{
17891804
// staticAuxDataOffset + preDeployDataSectionSize value is already verified to fit 2 bytes because
17901805
// staticAuxDataOffset < staticAuxDataSize

Diff for: libevmasm/Assembly.h

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Assembly
8888
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
8989
AssemblyItem newPushImmutable(std::string const& _identifier);
9090
AssemblyItem newImmutableAssignment(std::string const& _identifier);
91+
AssemblyItem newDataLoadN(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(DataLoadN, Instruction::DATALOADN, h); }
9192
AssemblyItem newAuxDataLoadN(size_t offset) const;
9293
AssemblyItem newSwapN(size_t _depth) const;
9394
AssemblyItem newDupN(size_t _depth) const;

Diff for: libevmasm/AssemblyItem.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
121121
return {"PUSH data", toStringInHex(data())};
122122
case VerbatimBytecode:
123123
return {"VERBATIM", util::toHex(verbatimData())};
124+
case DataLoadN:
125+
return {"DATALOADN", util::toString(data())};
124126
case AuxDataLoadN:
125127
return {"AUXDATALOADN", util::toString(data())};
126128
case UndefinedItem:
@@ -188,6 +190,7 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
188190
return std::get<2>(*m_verbatimBytecode).size();
189191
case RelativeJump:
190192
case ConditionalRelativeJump:
193+
case DataLoadN:
191194
case AuxDataLoadN:
192195
case JumpF:
193196
case CallF:
@@ -260,6 +263,7 @@ size_t AssemblyItem::returnValues() const
260263
return 0;
261264
case VerbatimBytecode:
262265
return std::get<1>(*m_verbatimBytecode);
266+
case DataLoadN:
263267
case AuxDataLoadN:
264268
return 1;
265269
case JumpF:
@@ -298,6 +302,7 @@ bool AssemblyItem::canBeFunctional() const
298302
case PushLibraryAddress:
299303
case PushDeployTimeAddress:
300304
case PushImmutable:
305+
case DataLoadN:
301306
case AuxDataLoadN:
302307
return true;
303308
case Tag:
@@ -406,6 +411,9 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
406411
assertThrow(data() <= std::numeric_limits<size_t>::max(), AssemblyException, "Invalid auxdataloadn argument.");
407412
text = "auxdataloadn{" + std::to_string(static_cast<size_t>(data())) + "}";
408413
break;
414+
case DataLoadN:
415+
text = "dataloadn{" + util::toHex(toCompactBigEndian(data(), 1)) + "}";
416+
break;
409417
case EOFCreate:
410418
text = "eofcreate{" + std::to_string(static_cast<size_t>(data())) + "}";
411419
break;
@@ -509,6 +517,9 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
509517
case VerbatimBytecode:
510518
_out << " Verbatim " << util::toHex(_item.verbatimData());
511519
break;
520+
case DataLoadN:
521+
_out << " DataLoadN " << util::toString(_item.data());
522+
break;
512523
case AuxDataLoadN:
513524
_out << " AuxDataLoadN " << util::toString(_item.data());
514525
break;

Diff for: libevmasm/AssemblyItem.h

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ enum AssemblyItemType
5252
PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor.
5353
AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code.
5454

55+
/// Loads 32 bytes from static data of EOF data section.
56+
/// More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle
57+
DataLoadN,
5558
/// Loads 32 bytes from static auxiliary data of EOF data section. The offset does *not* have to be always from the beginning
5659
/// of the data EOF section. More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle
5760
AuxDataLoadN,
@@ -200,6 +203,7 @@ class AssemblyItem
200203
m_type == ReturnContract ||
201204
m_type == RelativeJump ||
202205
m_type == ConditionalRelativeJump ||
206+
m_type == DataLoadN ||
203207
m_type == CallF ||
204208
m_type == JumpF ||
205209
m_type == RetF ||

Diff for: libevmasm/ConstantOptimiser.cpp

+79-58
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
3131
bool _isCreation,
3232
size_t _runs,
3333
langutil::EVMVersion _evmVersion,
34-
Assembly& _assembly
34+
Assembly& _assembly,
35+
std::optional<uint8_t> _eofVersion
3536
)
3637
{
3738
// TODO: design the optimiser in a way this is not needed
@@ -55,6 +56,7 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
5556
params.isCreation = _isCreation;
5657
params.runs = _runs;
5758
params.evmVersion = _evmVersion;
59+
params.eofVersion = _eofVersion;
5860
LiteralMethod lit(params, item.data());
5961
bigint literalGas = lit.gasNeeded();
6062
CodeCopyMethod copy(params, item.data());
@@ -161,73 +163,92 @@ AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const
161163
{
162164
bytes data = toBigEndian(m_value);
163165
assertThrow(data.size() == 32, OptimizerException, "Invalid number encoding.");
164-
AssemblyItem newPushData = _assembly.newData(data);
165-
return copyRoutine(&newPushData);
166+
167+
if (m_params.eofVersion.has_value())
168+
return AssemblyItems {_assembly.newDataLoadN(data)};
169+
else
170+
{
171+
AssemblyItem newPushData = _assembly.newData(data);
172+
return copyRoutine(&newPushData);
173+
}
166174
}
167175

168-
AssemblyItems CodeCopyMethod::copyRoutine(AssemblyItem* _pushData) const
176+
AssemblyItems CodeCopyMethod::copyRoutine(AssemblyItem* _dataItem) const
169177
{
170-
if (_pushData)
171-
assertThrow(_pushData->type() == PushData, OptimizerException, "Invalid Assembly Item.");
172-
173-
AssemblyItem dataUsed = _pushData ? *_pushData : AssemblyItem(PushData, u256(1) << 16);
174-
175-
// PUSH0 is cheaper than PUSHn/DUP/SWAP.
176-
if (m_params.evmVersion.hasPush0())
178+
if (m_params.eofVersion.has_value())
177179
{
178-
// This costs ~29 gas.
180+
// This case is used only for gas calculation.
181+
solAssert(_dataItem == nullptr);
179182
AssemblyItems copyRoutine{
180-
// back up memory
181-
// mload(0)
182-
u256(0),
183-
Instruction::MLOAD,
184-
185-
// codecopy(0, <offset>, 32)
186-
u256(32),
187-
dataUsed,
188-
u256(0),
189-
Instruction::CODECOPY,
190-
191-
// mload(0)
192-
u256(0),
193-
Instruction::MLOAD,
194-
195-
// restore original memory
196-
// mstore(0, x)
197-
Instruction::SWAP1,
198-
u256(0),
199-
Instruction::MSTORE
183+
AssemblyItem(DataLoadN, Instruction::DATALOADN, u256(1) << 16)
200184
};
201185
return copyRoutine;
202186
}
203187
else
204188
{
205-
// This costs ~33 gas.
206-
AssemblyItems copyRoutine{
207-
// constant to be reused 3+ times
208-
u256(0),
209-
210-
// back up memory
211-
// mload(0)
212-
Instruction::DUP1,
213-
Instruction::MLOAD,
214-
215-
// codecopy(0, <offset>, 32)
216-
u256(32),
217-
dataUsed,
218-
Instruction::DUP4,
219-
Instruction::CODECOPY,
220-
221-
// mload(0)
222-
Instruction::DUP2,
223-
Instruction::MLOAD,
224-
225-
// restore original memory
226-
// mstore(0, x)
227-
Instruction::SWAP2,
228-
Instruction::MSTORE
229-
};
230-
return copyRoutine;
189+
AssemblyItem* _pushData = _dataItem;
190+
if (_pushData)
191+
assertThrow(_pushData->type() == PushData, OptimizerException, "Invalid Assembly Item.");
192+
193+
AssemblyItem dataUsed = _pushData ? *_pushData : AssemblyItem(PushData, u256(1) << 16);
194+
195+
// PUSH0 is cheaper than PUSHn/DUP/SWAP.
196+
if (m_params.evmVersion.hasPush0())
197+
{
198+
// This costs ~29 gas.
199+
AssemblyItems copyRoutine{
200+
// back up memory
201+
// mload(0)
202+
u256(0),
203+
Instruction::MLOAD,
204+
205+
// codecopy(0, <offset>, 32)
206+
u256(32),
207+
dataUsed,
208+
u256(0),
209+
Instruction::CODECOPY,
210+
211+
// mload(0)
212+
u256(0),
213+
Instruction::MLOAD,
214+
215+
// restore original memory
216+
// mstore(0, x)
217+
Instruction::SWAP1,
218+
u256(0),
219+
Instruction::MSTORE
220+
};
221+
return copyRoutine;
222+
}
223+
else
224+
{
225+
// This costs ~33 gas.
226+
AssemblyItems copyRoutine{
227+
// constant to be reused 3+ times
228+
u256(0),
229+
230+
// back up memory
231+
// mload(0)
232+
Instruction::DUP1,
233+
Instruction::MLOAD,
234+
235+
// codecopy(0, <offset>, 32)
236+
u256(32),
237+
dataUsed,
238+
Instruction::DUP4,
239+
Instruction::CODECOPY,
240+
241+
// mload(0)
242+
Instruction::DUP2,
243+
Instruction::MLOAD,
244+
245+
// restore original memory
246+
// mstore(0, x)
247+
Instruction::SWAP2,
248+
Instruction::MSTORE
249+
};
250+
return copyRoutine;
251+
}
231252
}
232253
}
233254

Diff for: libevmasm/ConstantOptimiser.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class ConstantOptimisationMethod
5151
bool _isCreation,
5252
size_t _runs,
5353
langutil::EVMVersion _evmVersion,
54-
Assembly& _assembly
54+
Assembly& _assembly,
55+
std::optional<uint8_t> _eofVersion
5556
);
5657

5758
protected:
@@ -63,6 +64,7 @@ class ConstantOptimisationMethod
6364
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
6465
size_t multiplicity; ///< Number of times the constant appears in the code.
6566
langutil::EVMVersion evmVersion; ///< Version of the EVM
67+
std::optional<uint8_t> eofVersion; ///< Version of EOF. Legacy EVM if not set.
6668
};
6769

6870
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):

Diff for: test/libsolidity/syntaxTests/sizeLimits/eof/bytecode_too_large.sol

+3-4
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)