Skip to content

Commit efc00be

Browse files
authored
Merge pull request #12748 from ethereum/markCreationAssembly
Store whether an evmasm Assembly is creation code.
2 parents a5a65ee + e7a3814 commit efc00be

32 files changed

+518
-74
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Compiler Features:
88
* JSON-AST: Added selector field for errors and events.
99

1010
Bugfixes:
11+
* Yul IR Code Generation: Optimize embedded creation code with correct settings. This fixes potential mismatches between the constructor code of a contract compiled in isolation and the bytecode in ``type(C).creationCode``, resp. the bytecode used for ``new C(...)``.
1112

1213

1314
### 0.8.12 (2022-02-16)

docs/yul.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,11 @@ regular strings in native encoding. For code,
11241124
11251125
Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter.
11261126

1127+
.. note::
1128+
1129+
An object with a name that ends in ``_deployed`` is treated as deployed code by the Yul optimizer.
1130+
The only consequence of this is a different gas cost heuristic in the optimizer.
1131+
11271132
.. note::
11281133

11291134
Data objects or sub-objects whose names contain a ``.`` can be defined
@@ -1172,17 +1177,17 @@ An example Yul Object is shown below:
11721177
11731178
// now return the runtime object (the currently
11741179
// executing code is the constructor code)
1175-
size := datasize("runtime")
1180+
size := datasize("Contract1_deployed")
11761181
offset := allocate(size)
11771182
// This will turn into a memory->memory copy for Ewasm and
11781183
// a codecopy for EVM
1179-
datacopy(offset, dataoffset("runtime"), size)
1184+
datacopy(offset, dataoffset("Contract1_deployed"), size)
11801185
return(offset, size)
11811186
}
11821187
11831188
data "Table2" hex"4123"
11841189
1185-
object "runtime" {
1190+
object "Contract1_deployed" {
11861191
code {
11871192
function allocate(size) -> ptr {
11881193
ptr := mload(0x40)
@@ -1204,7 +1209,7 @@ An example Yul Object is shown below:
12041209
// code here ...
12051210
}
12061211
1207-
object "runtime" {
1212+
object "Contract2_deployed" {
12081213
code {
12091214
// code here ...
12101215
}

libevmasm/Assembly.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,8 @@ map<u256, u256> const& Assembly::optimiseInternal(
415415
for (size_t subId = 0; subId < m_subs.size(); ++subId)
416416
{
417417
OptimiserSettings settings = _settings;
418-
// Disable creation mode for sub-assemblies.
419-
settings.isCreation = false;
420-
map<u256, u256> const& subTagReplacements = m_subs[subId]->optimiseInternal(
418+
Assembly& sub = *m_subs[subId];
419+
map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
421420
settings,
422421
JumpdestRemover::referencedTags(m_items, subId)
423422
);
@@ -436,7 +435,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
436435
m_items,
437436
_tagsReferencedFromOutside,
438437
_settings.expectedExecutionsPerDeployment,
439-
_settings.isCreation,
438+
isCreation(),
440439
_settings.evmVersion
441440
}.optimise();
442441

@@ -537,8 +536,8 @@ map<u256, u256> const& Assembly::optimiseInternal(
537536

538537
if (_settings.runConstantOptimiser)
539538
ConstantOptimisationMethod::optimiseConstants(
540-
_settings.isCreation,
541-
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment,
539+
isCreation(),
540+
isCreation() ? 1 : _settings.expectedExecutionsPerDeployment,
542541
_settings.evmVersion,
543542
*this
544543
);

libevmasm/Assembly.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
4848
class Assembly
4949
{
5050
public:
51-
explicit Assembly(std::string _name = std::string()):m_name(std::move(_name)) { }
51+
Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { }
5252

5353
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
5454
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
@@ -117,7 +117,6 @@ class Assembly
117117

118118
struct OptimiserSettings
119119
{
120-
bool isCreation = false;
121120
bool runInliner = false;
122121
bool runJumpdestRemover = false;
123122
bool runPeephole = false;
@@ -157,6 +156,8 @@ class Assembly
157156
std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
158157
size_t encodeSubPath(std::vector<size_t> const& _subPath);
159158

159+
bool isCreation() const { return m_creation; }
160+
160161
protected:
161162
/// Does the same operations as @a optimise, but should only be applied to a sub and
162163
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
@@ -214,6 +215,8 @@ class Assembly
214215
mutable std::vector<size_t> m_tagPositionsInBytecode;
215216

216217
int m_deposit = 0;
218+
/// True, if the assembly contains contract creation code.
219+
bool const m_creation = false;
217220
/// Internal name of the assembly object, only used with the Yul backend
218221
/// currently
219222
std::string m_name;

libsolidity/codegen/CompilerContext.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,7 @@ void CompilerContext::updateSourceLocation()
572572
evmasm::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings)
573573
{
574574
// Constructing it this way so that we notice changes in the fields.
575-
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, m_evmVersion, 0};
576-
asmSettings.isCreation = true;
575+
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0};
577576
asmSettings.runInliner = _settings.runInliner;
578577
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
579578
asmSettings.runPeephole = _settings.runPeephole;

libsolidity/codegen/CompilerContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class CompilerContext
6565
RevertStrings _revertStrings,
6666
CompilerContext* _runtimeContext = nullptr
6767
):
68-
m_asm(std::make_shared<evmasm::Assembly>()),
68+
m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
6969
m_evmVersion(_evmVersion),
7070
m_revertStrings(_revertStrings),
7171
m_reservedMemory{0},

libyul/AssemblyStack.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include <libevmasm/Assembly.h>
4040
#include <liblangutil/Scanner.h>
41+
#include <boost/algorithm/string.hpp>
4142
#include <optional>
4243

4344
using namespace std;
@@ -71,8 +72,7 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings(
7172
)
7273
{
7374
// Constructing it this way so that we notice changes in the fields.
74-
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, _evmVersion, 0};
75-
asmSettings.isCreation = true;
75+
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, _evmVersion, 0};
7676
asmSettings.runInliner = _settings.runInliner;
7777
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
7878
asmSettings.runPeephole = _settings.runPeephole;
@@ -194,7 +194,10 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
194194
yulAssert(_object.analysisInfo, "");
195195
for (auto& subNode: _object.subObjects)
196196
if (auto subObject = dynamic_cast<Object*>(subNode.get()))
197-
optimize(*subObject, false);
197+
{
198+
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
199+
optimize(*subObject, isCreation);
200+
}
198201

199202
Dialect const& dialect = languageToDialect(m_language, m_evmVersion);
200203
unique_ptr<GasMeter> meter;
@@ -281,7 +284,7 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
281284
yulAssert(m_parserResult->code, "");
282285
yulAssert(m_parserResult->analysisInfo, "");
283286

284-
evmasm::Assembly assembly;
287+
evmasm::Assembly assembly(true, {});
285288
EthAssemblyAdapter adapter(assembly);
286289
compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation);
287290

libyul/backends/evm/AbstractAssembly.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class AbstractAssembly
9898
/// Append the assembled size as a constant.
9999
virtual void appendAssemblySize() = 0;
100100
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
101-
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") = 0;
101+
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = "") = 0;
102102
/// Appends the offset of the given sub-assembly or data.
103103
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
104104
/// Appends the size of the given sub-assembly or data.

libyul/backends/evm/EVMObjectCompiler.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <libyul/Object.h>
3131
#include <libyul/Exceptions.h>
3232

33+
#include <boost/algorithm/string.hpp>
34+
3335
using namespace solidity::yul;
3436
using namespace std;
3537

@@ -48,7 +50,8 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
4850
for (auto const& subNode: _object.subObjects)
4951
if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
5052
{
51-
auto subAssemblyAndID = m_assembly.createSubAssembly(subObject->name.str());
53+
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
54+
auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name.str());
5255
context.subIDs[subObject->name] = subAssemblyAndID.second;
5356
subObject->subId = subAssemblyAndID.second;
5457
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize);

libyul/backends/evm/EthAssemblyAdapter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ void EthAssemblyAdapter::appendAssemblySize()
122122
m_assembly.appendProgramSize();
123123
}
124124

125-
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(string _name)
125+
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name)
126126
{
127-
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(std::move(_name))};
127+
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(_creation, std::move(_name))};
128128
auto sub = m_assembly.newSub(assembly);
129129
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
130130
}

0 commit comments

Comments
 (0)