Skip to content

Commit 859e524

Browse files
rodiazetekpyron
andcommitted
eof: Introduce EOF container format support in Assembly::assemble (EIP-3540)
Co-authored-by: Daniel Kirchner <daniel@ekpyron.org>
1 parent 9b33e00 commit 859e524

File tree

9 files changed

+432
-116
lines changed

9 files changed

+432
-116
lines changed

libevmasm/Assembly.cpp

Lines changed: 333 additions & 57 deletions
Large diffs are not rendered by default.

libevmasm/Assembly.h

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,14 @@ class Assembly
4949
{
5050
public:
5151
Assembly(langutil::EVMVersion _evmVersion, bool _creation, std::optional<uint8_t> _eofVersion, std::string _name):
52-
m_evmVersion(_evmVersion),
53-
m_creation(_creation),
54-
m_eofVersion(_eofVersion),
55-
m_name(std::move(_name))
56-
{}
52+
m_evmVersion(_evmVersion),
53+
m_creation(_creation),
54+
m_eofVersion(_eofVersion),
55+
m_name(std::move(_name))
56+
{
57+
// Code section number 0 has to be non-returning.
58+
m_codeSections.emplace_back(CodeSection{0, 0x80, {}});
59+
}
5760

5861
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
5962
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
@@ -103,12 +106,6 @@ class Assembly
103106
/// Appends @a _data literally to the very end of the bytecode.
104107
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
105108

106-
/// Returns the assembly items.
107-
AssemblyItems const& items() const { return m_items; }
108-
109-
/// Returns the mutable assembly items. Use with care!
110-
AssemblyItems& items() { return m_items; }
111-
112109
int deposit() const { return m_deposit; }
113110
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
114111
void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
@@ -170,7 +167,8 @@ class Assembly
170167
static std::pair<std::shared_ptr<Assembly>, std::vector<std::string>> fromJSON(
171168
Json const& _json,
172169
std::vector<std::string> const& _sourceList = {},
173-
size_t _level = 0
170+
size_t _level = 0,
171+
std::optional<uint8_t> _eofVersion = std::nullopt
174172
);
175173

176174
/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
@@ -181,12 +179,30 @@ class Assembly
181179

182180
bool isCreation() const { return m_creation; }
183181

182+
struct CodeSection
183+
{
184+
uint8_t inputs = 0;
185+
uint8_t outputs = 0;
186+
AssemblyItems items{};
187+
};
188+
189+
std::vector<CodeSection>& codeSections()
190+
{
191+
return m_codeSections;
192+
}
193+
194+
std::vector<CodeSection> const& codeSections() const
195+
{
196+
return m_codeSections;
197+
}
198+
184199
protected:
185200
/// Does the same operations as @a optimise, but should only be applied to a sub and
186201
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
187202
/// that are referenced in a super-assembly.
188203
std::map<u256, u256> const& optimiseInternal(OptimiserSettings const& _settings, std::set<size_t> _tagsReferencedFromOutside);
189204

205+
/// For EOF and legacy it calculates approximate size of "pure" code without data.
190206
unsigned codeSize(unsigned subTagSize) const;
191207

192208
/// Add all assembly items from given JSON array. This function imports the items by iterating through
@@ -210,6 +226,11 @@ class Assembly
210226

211227
std::shared_ptr<std::string const> sharedSourceName(std::string const& _name) const;
212228

229+
void appendEOFHeader(bytes& bytecode, std::vector<size_t>& codeSectionSizeOffsets, size_t& startOfContainerSectionHeader, size_t& dataSectionSizeOffset) const;
230+
231+
LinkerObject const& assembleLegacy() const;
232+
LinkerObject const& assembleEOF() const;
233+
213234
protected:
214235
/// 0 is reserved for exception
215236
unsigned m_usedTags = 1;
@@ -223,11 +244,12 @@ class Assembly
223244
};
224245

225246
std::map<std::string, NamedTagInfo> m_namedTags;
226-
AssemblyItems m_items;
227247
std::map<util::h256, bytes> m_data;
228248
/// Data that is appended to the very end of the contract.
229249
bytes m_auxiliaryData;
230250
std::vector<std::shared_ptr<Assembly>> m_subs;
251+
std::vector<CodeSection> m_codeSections;
252+
uint16_t m_currentCodeSection = 0;
231253
std::map<util::h256, std::string> m_strings;
232254
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
233255
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.

libevmasm/ConstantOptimiser.cpp

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,46 +35,49 @@ unsigned ConstantOptimisationMethod::optimiseConstants(
3535
)
3636
{
3737
// TODO: design the optimiser in a way this is not needed
38-
AssemblyItems& _items = _assembly.items();
39-
4038
unsigned optimisations = 0;
41-
std::map<AssemblyItem, size_t> pushes;
42-
for (AssemblyItem const& item: _items)
43-
if (item.type() == Push)
44-
pushes[item]++;
45-
std::map<u256, AssemblyItems> pendingReplacements;
46-
for (auto it: pushes)
39+
for (auto& codeSection: _assembly.codeSections())
4740
{
48-
AssemblyItem const& item = it.first;
49-
if (item.data() < 0x100)
50-
continue;
51-
Params params;
52-
params.multiplicity = it.second;
53-
params.isCreation = _isCreation;
54-
params.runs = _runs;
55-
params.evmVersion = _evmVersion;
56-
LiteralMethod lit(params, item.data());
57-
bigint literalGas = lit.gasNeeded();
58-
CodeCopyMethod copy(params, item.data());
59-
bigint copyGas = copy.gasNeeded();
60-
ComputeMethod compute(params, item.data());
61-
bigint computeGas = compute.gasNeeded();
62-
AssemblyItems replacement;
63-
if (copyGas < literalGas && copyGas < computeGas)
64-
{
65-
replacement = copy.execute(_assembly);
66-
optimisations++;
67-
}
68-
else if (computeGas < literalGas && computeGas <= copyGas)
41+
AssemblyItems& _items = codeSection.items;
42+
43+
std::map<AssemblyItem, size_t> pushes;
44+
for (AssemblyItem const& item: _items)
45+
if (item.type() == Push)
46+
pushes[item]++;
47+
std::map<u256, AssemblyItems> pendingReplacements;
48+
for (auto it: pushes)
6949
{
70-
replacement = compute.execute(_assembly);
71-
optimisations++;
50+
AssemblyItem const& item = it.first;
51+
if (item.data() < 0x100)
52+
continue;
53+
Params params;
54+
params.multiplicity = it.second;
55+
params.isCreation = _isCreation;
56+
params.runs = _runs;
57+
params.evmVersion = _evmVersion;
58+
LiteralMethod lit(params, item.data());
59+
bigint literalGas = lit.gasNeeded();
60+
CodeCopyMethod copy(params, item.data());
61+
bigint copyGas = copy.gasNeeded();
62+
ComputeMethod compute(params, item.data());
63+
bigint computeGas = compute.gasNeeded();
64+
AssemblyItems replacement;
65+
if (copyGas < literalGas && copyGas < computeGas)
66+
{
67+
replacement = copy.execute(_assembly);
68+
optimisations++;
69+
}
70+
else if (computeGas < literalGas && computeGas <= copyGas)
71+
{
72+
replacement = compute.execute(_assembly);
73+
optimisations++;
74+
}
75+
if (!replacement.empty())
76+
pendingReplacements[item.data()] = replacement;
7277
}
73-
if (!replacement.empty())
74-
pendingReplacements[item.data()] = replacement;
78+
if (!pendingReplacements.empty())
79+
replaceConstants(_items, pendingReplacements);
7580
}
76-
if (!pendingReplacements.empty())
77-
replaceConstants(_items, pendingReplacements);
7881
return optimisations;
7982
}
8083

libevmasm/EVMAssemblyStack.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,16 @@ void EVMAssemblyStack::assemble()
5656
solAssert(!m_evmRuntimeAssembly);
5757

5858
m_object = m_evmAssembly->assemble();
59-
m_sourceMapping = AssemblyItem::computeSourceMapping(m_evmAssembly->items(), sourceIndices());
59+
// TODO: Check for EOF
60+
solAssert(m_evmAssembly->codeSections().size() == 1);
61+
m_sourceMapping = AssemblyItem::computeSourceMapping(m_evmAssembly->codeSections().front().items, sourceIndices());
6062
if (m_evmAssembly->numSubs() > 0)
6163
{
6264
m_evmRuntimeAssembly = std::make_shared<evmasm::Assembly>(m_evmAssembly->sub(0));
6365
solAssert(m_evmRuntimeAssembly && !m_evmRuntimeAssembly->isCreation());
64-
m_runtimeSourceMapping = AssemblyItem::computeSourceMapping(m_evmRuntimeAssembly->items(), sourceIndices());
66+
// TODO: Check for EOF
67+
solAssert(m_evmRuntimeAssembly->codeSections().size() == 1);
68+
m_runtimeSourceMapping = AssemblyItem::computeSourceMapping(m_evmRuntimeAssembly->codeSections().front().items, sourceIndices());
6569
m_runtimeObject = m_evmRuntimeAssembly->assemble();
6670
}
6771
}

libsolidity/interface/CompilerStack.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -808,15 +808,19 @@ evmasm::AssemblyItems const* CompilerStack::assemblyItems(std::string const& _co
808808
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
809809

810810
Contract const& currentContract = contract(_contractName);
811-
return currentContract.evmAssembly ? &currentContract.evmAssembly->items() : nullptr;
811+
if (currentContract.evmAssembly)
812+
solAssert(currentContract.evmAssembly->codeSections().size() == 1, "Expected a single code section in legacy codegen.");
813+
return currentContract.evmAssembly ? &currentContract.evmAssembly->codeSections().front().items : nullptr;
812814
}
813815

814816
evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(std::string const& _contractName) const
815817
{
816818
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
817819

818820
Contract const& currentContract = contract(_contractName);
819-
return currentContract.evmRuntimeAssembly ? &currentContract.evmRuntimeAssembly->items() : nullptr;
821+
if (currentContract.evmRuntimeAssembly)
822+
solAssert(currentContract.evmRuntimeAssembly->codeSections().size() == 1, "Expected a single code section in legacy codegen.");
823+
return currentContract.evmRuntimeAssembly ? &currentContract.evmRuntimeAssembly->codeSections().front().items : nullptr;
820824
}
821825

822826
Json CompilerStack::generatedSources(std::string const& _contractName, bool _runtime) const

libyul/YulStack.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,11 @@ YulStack::assembleWithDeployed(std::optional<std::string_view> _deployName)
262262
creationObject.bytecode = std::make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
263263
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
264264
creationObject.assembly = creationAssembly;
265+
solAssert(creationAssembly->codeSections().size() == 1);
265266
creationObject.sourceMappings = std::make_unique<std::string>(
267+
// TODO: fix for EOF
266268
evmasm::AssemblyItem::computeSourceMapping(
267-
creationAssembly->items(),
269+
creationAssembly->codeSections().front().items,
268270
{{m_charStream->name(), 0}}
269271
)
270272
);
@@ -273,11 +275,12 @@ YulStack::assembleWithDeployed(std::optional<std::string_view> _deployName)
273275
{
274276
deployedObject.bytecode = std::make_shared<evmasm::LinkerObject>(deployedAssembly->assemble());
275277
deployedObject.assembly = deployedAssembly;
278+
solAssert(deployedAssembly->codeSections().size() == 1);
276279
deployedObject.sourceMappings = std::make_unique<std::string>(
277280
evmasm::AssemblyItem::computeSourceMapping(
278-
deployedAssembly->items(),
281+
deployedAssembly->codeSections().front().items,
279282
{{m_charStream->name(), 0}}
280-
)
283+
)
281284
);
282285
}
283286
}

test/libevmasm/Assembler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
270270

271271
checkCompilation(assembly);
272272

273-
std::string const sourceMappings = AssemblyItem::computeSourceMapping(assembly.items(), indices);
273+
BOOST_REQUIRE(assembly.codeSections().size() == 1);
274+
std::string const sourceMappings = AssemblyItem::computeSourceMapping(assembly.codeSections().at(0).items, indices);
274275
auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';');
275276

276277
LinkerObject const& obj = assembly.assemble();

test/libevmasm/Optimiser.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,16 +1381,18 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
13811381
t1.toSubAssemblyTag(subId).pushTag(),
13821382
u256(8)
13831383
};
1384+
BOOST_REQUIRE(main.codeSections().size() == 1);
13841385
BOOST_CHECK_EQUAL_COLLECTIONS(
1385-
main.items().begin(), main.items().end(),
1386+
main.codeSections().at(0).items.begin(),main.codeSections().at(0).items.end(),
13861387
expectationMain.begin(), expectationMain.end()
13871388
);
13881389

13891390
AssemblyItems expectationSub{
13901391
u256(1), t1.tag(), u256(2), Instruction::JUMP, t4.tag(), u256(7), t4.pushTag(), Instruction::JUMP
13911392
};
1393+
BOOST_REQUIRE(sub->codeSections().size() == 1);
13921394
BOOST_CHECK_EQUAL_COLLECTIONS(
1393-
sub->items().begin(), sub->items().end(),
1395+
sub->codeSections().at(0).items.begin(), sub->codeSections().at(0).items.end(),
13941396
expectationSub.begin(), expectationSub.end()
13951397
);
13961398
}

test/libsolidity/Assembly.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
9191
);
9292
compiler.compileContract(*contract, std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>{}, bytes());
9393

94-
return compiler.runtimeAssembly().items();
94+
BOOST_REQUIRE(compiler.runtimeAssembly().codeSections().size() == 1);
95+
return compiler.runtimeAssembly().codeSections().at(0).items;
9596
}
9697
BOOST_FAIL("No contract found in source.");
9798
return AssemblyItems();

0 commit comments

Comments
 (0)