Merge pull request #12748 from ethereum/markCreationAssembly

Store whether an evmasm Assembly is creation code.
This commit is contained in:
Daniel Kirchner 2022-03-09 18:30:31 +01:00 committed by GitHub
commit efc00bef55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 518 additions and 74 deletions

View File

@ -8,6 +8,7 @@ Compiler Features:
* JSON-AST: Added selector field for errors and events. * JSON-AST: Added selector field for errors and events.
Bugfixes: Bugfixes:
* 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(...)``.
### 0.8.12 (2022-02-16) ### 0.8.12 (2022-02-16)

View File

@ -1124,6 +1124,11 @@ regular strings in native encoding. For code,
Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter. Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter.
.. note::
An object with a name that ends in ``_deployed`` is treated as deployed code by the Yul optimizer.
The only consequence of this is a different gas cost heuristic in the optimizer.
.. note:: .. note::
Data objects or sub-objects whose names contain a ``.`` can be defined Data objects or sub-objects whose names contain a ``.`` can be defined
@ -1172,17 +1177,17 @@ An example Yul Object is shown below:
// now return the runtime object (the currently // now return the runtime object (the currently
// executing code is the constructor code) // executing code is the constructor code)
size := datasize("runtime") size := datasize("Contract1_deployed")
offset := allocate(size) offset := allocate(size)
// This will turn into a memory->memory copy for Ewasm and // This will turn into a memory->memory copy for Ewasm and
// a codecopy for EVM // a codecopy for EVM
datacopy(offset, dataoffset("runtime"), size) datacopy(offset, dataoffset("Contract1_deployed"), size)
return(offset, size) return(offset, size)
} }
data "Table2" hex"4123" data "Table2" hex"4123"
object "runtime" { object "Contract1_deployed" {
code { code {
function allocate(size) -> ptr { function allocate(size) -> ptr {
ptr := mload(0x40) ptr := mload(0x40)
@ -1204,7 +1209,7 @@ An example Yul Object is shown below:
// code here ... // code here ...
} }
object "runtime" { object "Contract2_deployed" {
code { code {
// code here ... // code here ...
} }

View File

@ -415,9 +415,8 @@ map<u256, u256> const& Assembly::optimiseInternal(
for (size_t subId = 0; subId < m_subs.size(); ++subId) for (size_t subId = 0; subId < m_subs.size(); ++subId)
{ {
OptimiserSettings settings = _settings; OptimiserSettings settings = _settings;
// Disable creation mode for sub-assemblies. Assembly& sub = *m_subs[subId];
settings.isCreation = false; map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
map<u256, u256> const& subTagReplacements = m_subs[subId]->optimiseInternal(
settings, settings,
JumpdestRemover::referencedTags(m_items, subId) JumpdestRemover::referencedTags(m_items, subId)
); );
@ -436,7 +435,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
m_items, m_items,
_tagsReferencedFromOutside, _tagsReferencedFromOutside,
_settings.expectedExecutionsPerDeployment, _settings.expectedExecutionsPerDeployment,
_settings.isCreation, isCreation(),
_settings.evmVersion _settings.evmVersion
}.optimise(); }.optimise();
@ -537,8 +536,8 @@ map<u256, u256> const& Assembly::optimiseInternal(
if (_settings.runConstantOptimiser) if (_settings.runConstantOptimiser)
ConstantOptimisationMethod::optimiseConstants( ConstantOptimisationMethod::optimiseConstants(
_settings.isCreation, isCreation(),
_settings.isCreation ? 1 : _settings.expectedExecutionsPerDeployment, isCreation() ? 1 : _settings.expectedExecutionsPerDeployment,
_settings.evmVersion, _settings.evmVersion,
*this *this
); );

View File

@ -48,7 +48,7 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly class Assembly
{ {
public: public:
explicit Assembly(std::string _name = std::string()):m_name(std::move(_name)) { } Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { }
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
@ -117,7 +117,6 @@ public:
struct OptimiserSettings struct OptimiserSettings
{ {
bool isCreation = false;
bool runInliner = false; bool runInliner = false;
bool runJumpdestRemover = false; bool runJumpdestRemover = false;
bool runPeephole = false; bool runPeephole = false;
@ -157,6 +156,8 @@ public:
std::vector<size_t> decodeSubPath(size_t _subObjectId) const; std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
size_t encodeSubPath(std::vector<size_t> const& _subPath); size_t encodeSubPath(std::vector<size_t> const& _subPath);
bool isCreation() const { return m_creation; }
protected: protected:
/// Does the same operations as @a optimise, but should only be applied to a sub and /// Does the same operations as @a optimise, but should only be applied to a sub and
/// returns the replaced tags. Also takes an argument containing the tags of this assembly /// returns the replaced tags. Also takes an argument containing the tags of this assembly
@ -214,6 +215,8 @@ protected:
mutable std::vector<size_t> m_tagPositionsInBytecode; mutable std::vector<size_t> m_tagPositionsInBytecode;
int m_deposit = 0; int m_deposit = 0;
/// True, if the assembly contains contract creation code.
bool const m_creation = false;
/// Internal name of the assembly object, only used with the Yul backend /// Internal name of the assembly object, only used with the Yul backend
/// currently /// currently
std::string m_name; std::string m_name;

View File

@ -572,8 +572,7 @@ void CompilerContext::updateSourceLocation()
evmasm::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings) evmasm::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings)
{ {
// Constructing it this way so that we notice changes in the fields. // Constructing it this way so that we notice changes in the fields.
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, m_evmVersion, 0}; evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0};
asmSettings.isCreation = true;
asmSettings.runInliner = _settings.runInliner; asmSettings.runInliner = _settings.runInliner;
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover; asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
asmSettings.runPeephole = _settings.runPeephole; asmSettings.runPeephole = _settings.runPeephole;

View File

@ -65,7 +65,7 @@ public:
RevertStrings _revertStrings, RevertStrings _revertStrings,
CompilerContext* _runtimeContext = nullptr CompilerContext* _runtimeContext = nullptr
): ):
m_asm(std::make_shared<evmasm::Assembly>()), m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_revertStrings(_revertStrings), m_revertStrings(_revertStrings),
m_reservedMemory{0}, m_reservedMemory{0},

View File

@ -38,6 +38,7 @@
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
#include <boost/algorithm/string.hpp>
#include <optional> #include <optional>
using namespace std; using namespace std;
@ -71,8 +72,7 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings(
) )
{ {
// Constructing it this way so that we notice changes in the fields. // Constructing it this way so that we notice changes in the fields.
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, _evmVersion, 0}; evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, _evmVersion, 0};
asmSettings.isCreation = true;
asmSettings.runInliner = _settings.runInliner; asmSettings.runInliner = _settings.runInliner;
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover; asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
asmSettings.runPeephole = _settings.runPeephole; asmSettings.runPeephole = _settings.runPeephole;
@ -194,7 +194,10 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
yulAssert(_object.analysisInfo, ""); yulAssert(_object.analysisInfo, "");
for (auto& subNode: _object.subObjects) for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<Object*>(subNode.get())) if (auto subObject = dynamic_cast<Object*>(subNode.get()))
optimize(*subObject, false); {
bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
optimize(*subObject, isCreation);
}
Dialect const& dialect = languageToDialect(m_language, m_evmVersion); Dialect const& dialect = languageToDialect(m_language, m_evmVersion);
unique_ptr<GasMeter> meter; unique_ptr<GasMeter> meter;
@ -281,7 +284,7 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
yulAssert(m_parserResult->code, ""); yulAssert(m_parserResult->code, "");
yulAssert(m_parserResult->analysisInfo, ""); yulAssert(m_parserResult->analysisInfo, "");
evmasm::Assembly assembly; evmasm::Assembly assembly(true, {});
EthAssemblyAdapter adapter(assembly); EthAssemblyAdapter adapter(assembly);
compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation); compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation);

View File

@ -98,7 +98,7 @@ public:
/// Append the assembled size as a constant. /// Append the assembled size as a constant.
virtual void appendAssemblySize() = 0; virtual void appendAssemblySize() = 0;
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset. /// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") = 0; virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = "") = 0;
/// Appends the offset of the given sub-assembly or data. /// Appends the offset of the given sub-assembly or data.
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0; virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
/// Appends the size of the given sub-assembly or data. /// Appends the size of the given sub-assembly or data.

View File

@ -30,6 +30,8 @@
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <boost/algorithm/string.hpp>
using namespace solidity::yul; using namespace solidity::yul;
using namespace std; using namespace std;
@ -48,7 +50,8 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
for (auto const& subNode: _object.subObjects) for (auto const& subNode: _object.subObjects)
if (auto* subObject = dynamic_cast<Object*>(subNode.get())) if (auto* subObject = dynamic_cast<Object*>(subNode.get()))
{ {
auto subAssemblyAndID = m_assembly.createSubAssembly(subObject->name.str()); bool isCreation = !boost::ends_with(subObject->name.str(), "_deployed");
auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name.str());
context.subIDs[subObject->name] = subAssemblyAndID.second; context.subIDs[subObject->name] = subAssemblyAndID.second;
subObject->subId = subAssemblyAndID.second; subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize); compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize);

View File

@ -122,9 +122,9 @@ void EthAssemblyAdapter::appendAssemblySize()
m_assembly.appendProgramSize(); m_assembly.appendProgramSize();
} }
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(string _name) pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name)
{ {
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(std::move(_name))}; shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(_creation, std::move(_name))};
auto sub = m_assembly.newSub(assembly); auto sub = m_assembly.newSub(assembly);
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())}; return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
} }

View File

@ -55,7 +55,7 @@ public:
void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override;
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendAssemblySize() override; void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = {}) override; std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = {}) override;
void appendDataOffset(std::vector<SubID> const& _subPath) override; void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override; void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override; SubID appendData(bytes const& _data) override;

View File

@ -98,7 +98,7 @@ void NoOutputAssembly::appendAssemblySize()
appendInstruction(evmasm::Instruction::PUSH1); appendInstruction(evmasm::Instruction::PUSH1);
} }
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly(std::string) pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly(bool, std::string)
{ {
yulAssert(false, "Sub assemblies not implemented."); yulAssert(false, "Sub assemblies not implemented.");
return {}; return {};

View File

@ -65,7 +65,7 @@ public:
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendAssemblySize() override; void appendAssemblySize() override;
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(std::string _name = "") override; std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = "") override;
void appendDataOffset(std::vector<SubID> const& _subPath) override; void appendDataOffset(std::vector<SubID> const& _subPath) override;
void appendDataSize(std::vector<SubID> const& _subPath) override; void appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override; SubID appendData(bytes const& _data) override;

View File

@ -0,0 +1 @@
--experimental-via-ir --optimize --asm

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.0.0;
contract C {
constructor(uint x) {
// In earlier versions of the compiler, the resulting assembly pushed the constant
// 0xFFFFFFFFFFFFFFFF42 directly in the subassembly of D, while it was optimized to
// ``sub(shl(0x48, 0x01), 0xbe)`` when C was compiled in isolation.
// Now the assembly is expected to contain two instances of ``sub(shl(0x48, 0x01), 0xbe)``,
// one in the creation code of ``C`` directly, one in a subassembly of ``D``.
// The constant 0xFFFFFFFFFFFFFFFF42 should not occur in the assembly output at all.
if (x == 0xFFFFFFFFFFFFFFFF42)
revert();
}
}
contract D {
function f() public pure returns (bytes memory) {
return type(C).creationCode;
}
}

View File

@ -0,0 +1,379 @@
======= viair_subobject_optimization/input.sol:C =======
EVM assembly:
/* "viair_subobject_optimization/input.sol":61:668 contract C {... */
0x80
dup1
0x40
mstore
jumpi(tag_6, callvalue)
0x1f
bytecodeSize
codesize
dup2
swap1
sub
swap2
dup3
add
not(0x1f)
and
dup4
add
swap2
sub(shl(0x40, 0x01), 0x01)
dup4
gt
dup5
dup5
lt
or
tag_4
jumpi
dup1
dup5
swap3
0x20
swap5
0x40
mstore
dup4
codecopy
dup2
add
sub
slt
tag_6
jumpi
tag_8
swap1
mload
tag_1
jump // in
tag_8:
mload(0x40)
dataSize(sub_0)
swap1
dup2
dataOffset(sub_0)
dup3
codecopy
return
tag_6:
pop
0x00
dup1
revert
tag_4:
pop
pop
pop
pop
mstore(0x00, shl(0xe0, 0x4e487b71))
mstore(0x04, 0x41)
revert(0x00, 0x24)
/* "viair_subobject_optimization/input.sol":76:666 constructor(uint x) {... */
tag_1:
sub(shl(0x48, 0x01), 0xbe)
/* "viair_subobject_optimization/input.sol":620:645 x == 0xFFFFFFFFFFFFFFFF42 */
eq
/* "viair_subobject_optimization/input.sol":616:661 if (x == 0xFFFFFFFFFFFFFFFF42)... */
tag_6
jumpi
/* "viair_subobject_optimization/input.sol":76:666 constructor(uint x) {... */
jump // out
stop
sub_0: assembly {
/* "viair_subobject_optimization/input.sol":61:668 contract C {... */
mstore(0x40, 0x80)
0x00
dup1
revert
auxdata: <AUXDATA REMOVED>
}
======= viair_subobject_optimization/input.sol:D =======
EVM assembly:
/* "viair_subobject_optimization/input.sol":669:772 contract D {... */
0x80
dup1
0x40
mstore
jumpi(tag_1, callvalue)
dataSize(sub_0)
swap1
dup2
dataOffset(sub_0)
dup3
codecopy
return
tag_1:
pop
0x00
dup1
revert
stop
sub_0: assembly {
/* "viair_subobject_optimization/input.sol":669:772 contract D {... */
0x80
dup1
0x40
mstore
jumpi(tag_2, iszero(lt(calldatasize, 0x04)))
tag_3:
pop
0x00
dup1
revert
tag_2:
0x00
swap1
dup2
calldataload
0xe0
shr
0x26121ff0
eq
tag_4
jumpi
pop
jump(tag_3)
tag_4:
jumpi(tag_8, callvalue)
dup2
add(calldatasize, not(0x03))
slt
tag_8
jumpi
/* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */
dataSize(sub_0)
/* "viair_subobject_optimization/input.sol":669:772 contract D {... */
0x3f
dup2
add
not(0x1f)
and
dup3
add
0xffffffffffffffff
dup2
gt
dup4
dup3
lt
or
tag_10
jumpi
tag_12
swap4
pop
0x40
mstore
/* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */
dup1
dup3
mstore
dataOffset(sub_0)
0x20
dup4
add
codecopy
/* "viair_subobject_optimization/input.sol":669:772 contract D {... */
mload(0x40)
swap2
dup3
swap2
dup3
tag_1
jump // in
tag_12:
sub
swap1
return
tag_10:
pop
pop
shl(0xe0, 0x4e487b71)
dup3
mstore
pop
mstore(0x04, 0x41)
0x24
swap1
revert
tag_8:
pop
dup1
revert
tag_1:
swap2
swap1
swap2
0x20
dup1
dup3
mstore
dup4
mload
swap1
dup2
dup2
dup5
add
mstore
0x00
swap5
tag_13:
dup3
dup7
lt
tag_14
jumpi
pop
pop
dup1
0x40
swap4
swap5
gt
tag_16
jumpi
tag_17:
0x1f
add
not(0x1f)
and
add
add
swap1
jump // out
tag_16:
0x00
dup4
dup3
dup5
add
add
mstore
jump(tag_17)
tag_14:
dup6
dup2
add
dup3
add
mload
dup5
dup8
add
0x40
add
mstore
swap5
dup2
add
swap5
jump(tag_13)
stop
sub_0: assembly {
/* "viair_subobject_optimization/input.sol":61:668 contract C {... */
0x80
dup1
0x40
mstore
jumpi(tag_6, callvalue)
0x1f
bytecodeSize
codesize
dup2
swap1
sub
swap2
dup3
add
not(0x1f)
and
dup4
add
swap2
sub(shl(0x40, 0x01), 0x01)
dup4
gt
dup5
dup5
lt
or
tag_4
jumpi
dup1
dup5
swap3
0x20
swap5
0x40
mstore
dup4
codecopy
dup2
add
sub
slt
tag_6
jumpi
tag_8
swap1
mload
tag_1
jump // in
tag_8:
mload(0x40)
dataSize(sub_0)
swap1
dup2
dataOffset(sub_0)
dup3
codecopy
return
tag_6:
pop
0x00
dup1
revert
tag_4:
pop
pop
pop
pop
mstore(0x00, shl(0xe0, 0x4e487b71))
mstore(0x04, 0x41)
revert(0x00, 0x24)
/* "viair_subobject_optimization/input.sol":76:666 constructor(uint x) {... */
tag_1:
sub(shl(0x48, 0x01), 0xbe)
/* "viair_subobject_optimization/input.sol":620:645 x == 0xFFFFFFFFFFFFFFFF42 */
eq
/* "viair_subobject_optimization/input.sol":616:661 if (x == 0xFFFFFFFFFFFFFFFF42)... */
tag_6
jumpi
/* "viair_subobject_optimization/input.sol":76:666 constructor(uint x) {... */
jump // out
stop
sub_0: assembly {
/* "viair_subobject_optimization/input.sol":61:668 contract C {... */
mstore(0x40, 0x80)
0x00
dup1
revert
auxdata: <AUXDATA REMOVED>
}
}
auxdata: <AUXDATA REMOVED>
}

View File

@ -1,10 +1,10 @@
object "RunsTest1" { object "RunsTest1" {
code { code {
// Deploy the contract // Deploy the contract
datacopy(0, dataoffset("Runtime"), datasize("Runtime")) datacopy(0, dataoffset("Runtime_deployed"), datasize("Runtime_deployed"))
return(0, datasize("Runtime")) return(0, datasize("Runtime_deployed"))
} }
object "Runtime" { object "Runtime_deployed" {
code { code {
let funcSel := shl(224, 0xabc12345) let funcSel := shl(224, 0xabc12345)
sstore(0, funcSel) sstore(0, funcSel)

View File

@ -5,12 +5,12 @@ Pretty printed source:
object "RunsTest1" { object "RunsTest1" {
code { code {
{ {
let _1 := datasize("Runtime") let _1 := datasize("Runtime_deployed")
datacopy(0, dataoffset("Runtime"), _1) datacopy(0, dataoffset("Runtime_deployed"), _1)
return(0, _1) return(0, _1)
} }
} }
object "Runtime" { object "Runtime_deployed" {
code { code {
{ {
sstore(0, 0xabc1234500000000000000000000000000000000000000000000000000000000) sstore(0, 0xabc1234500000000000000000000000000000000000000000000000000000000)
@ -24,28 +24,28 @@ Binary representation:
602580600c6000396000f3fe7fabc123450000000000000000000000000000000000000000000000000000000060005500 602580600c6000396000f3fe7fabc123450000000000000000000000000000000000000000000000000000000060005500
Text representation: Text representation:
/* "yul_optimize_runs/input.yul":106:125 */ /* "yul_optimize_runs/input.yul":115:143 */
dataSize(sub_0) dataSize(sub_0)
/* "yul_optimize_runs/input.yul":83:104 */ /* "yul_optimize_runs/input.yul":83:113 */
dup1 dup1
dataOffset(sub_0) dataOffset(sub_0)
/* "yul_optimize_runs/input.yul":80:81 */ /* "yul_optimize_runs/input.yul":80:81 */
0x00 0x00
/* "yul_optimize_runs/input.yul":71:126 */ /* "yul_optimize_runs/input.yul":71:144 */
codecopy codecopy
/* "yul_optimize_runs/input.yul":80:81 */ /* "yul_optimize_runs/input.yul":80:81 */
0x00 0x00
/* "yul_optimize_runs/input.yul":135:165 */ /* "yul_optimize_runs/input.yul":153:192 */
return return
stop stop
sub_0: assembly { sub_0: assembly {
/* "yul_optimize_runs/input.yul":237:257 */ /* "yul_optimize_runs/input.yul":273:293 */
0xabc1234500000000000000000000000000000000000000000000000000000000 0xabc1234500000000000000000000000000000000000000000000000000000000
/* "yul_optimize_runs/input.yul":277:278 */ /* "yul_optimize_runs/input.yul":313:314 */
0x00 0x00
/* "yul_optimize_runs/input.yul":270:288 */ /* "yul_optimize_runs/input.yul":306:324 */
sstore sstore
/* "yul_optimize_runs/input.yul":208:298 */ /* "yul_optimize_runs/input.yul":244:334 */
stop stop
} }

View File

@ -58,11 +58,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{ "root.asm", 0 }, { "root.asm", 0 },
{ "sub.asm", 1 } { "sub.asm", 1 }
}; };
Assembly _assembly; Assembly _assembly{false, {}};
auto root_asm = make_shared<string>("root.asm"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm; Assembly _subAsm{false, {}};
auto sub_asm = make_shared<string>("sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
// PushImmutable // PushImmutable
@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
{ *subName, 1 } { *subName, 1 }
}; };
auto subAsm = make_shared<Assembly>(); auto subAsm = make_shared<Assembly>(false, string{});
for (char i = 0; i < numImmutables; ++i) for (char i = 0; i < numImmutables; ++i)
{ {
for (int r = 0; r < numActualRefs; ++r) for (int r = 0; r < numActualRefs; ++r)
@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps)
} }
} }
Assembly assembly; Assembly assembly{true, {}};
for (char i = 1; i <= numImmutables; ++i) for (char i = 1; i <= numImmutables; ++i)
{ {
assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName}); assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName});
@ -256,11 +256,11 @@ BOOST_AUTO_TEST_CASE(immutable)
{ "root.asm", 0 }, { "root.asm", 0 },
{ "sub.asm", 1 } { "sub.asm", 1 }
}; };
Assembly _assembly; Assembly _assembly{true, {}};
auto root_asm = make_shared<string>("root.asm"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm; Assembly _subAsm{false, {}};
auto sub_asm = make_shared<string>("sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
_subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someImmutable");
@ -349,10 +349,10 @@ BOOST_AUTO_TEST_CASE(immutable)
BOOST_AUTO_TEST_CASE(subobject_encode_decode) BOOST_AUTO_TEST_CASE(subobject_encode_decode)
{ {
Assembly assembly; Assembly assembly{true, {}};
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>(); shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>(false, string{});
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(); shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>(false, string{});
assembly.appendSubroutine(subAsmPtr); assembly.appendSubroutine(subAsmPtr);
subAsmPtr->appendSubroutine(subSubAsmPtr); subAsmPtr->appendSubroutine(subSubAsmPtr);

View File

@ -1250,8 +1250,8 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
// tag unifications (due to block deduplication) is also // tag unifications (due to block deduplication) is also
// visible at the super-assembly. // visible at the super-assembly.
Assembly main; Assembly main{false, {}};
AssemblyPointer sub = make_shared<Assembly>(); AssemblyPointer sub = make_shared<Assembly>(true, string{});
sub->append(u256(1)); sub->append(u256(1));
auto t1 = sub->newTag(); auto t1 = sub->newTag();
@ -1278,7 +1278,6 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
main.append(u256(8)); main.append(u256(8));
Assembly::OptimiserSettings settings; Assembly::OptimiserSettings settings;
settings.isCreation = false;
settings.runInliner = false; settings.runInliner = false;
settings.runJumpdestRemover = true; settings.runJumpdestRemover = true;
settings.runPeephole = true; settings.runPeephole = true;
@ -1287,7 +1286,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
settings.runConstantOptimiser = true; settings.runConstantOptimiser = true;
settings.evmVersion = solidity::test::CommonOptions::get().evmVersion(); settings.evmVersion = solidity::test::CommonOptions::get().evmVersion();
settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment; settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment;
;
main.optimise(settings); main.optimise(settings);
AssemblyItems expectationMain{ AssemblyItems expectationMain{

View File

@ -84,6 +84,34 @@ struct SolidityEndToEndTestExecutionFramework: public SolidityExecutionFramework
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework) BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework)
BOOST_AUTO_TEST_CASE(creation_code_optimizer)
{
string codeC = R"(
contract C {
constructor(uint x) {
if (x == 0xFFFFFFFFFFFFFFFF42)
revert();
}
}
)";
string codeD = R"(
contract D {
function f() public pure returns (bytes memory) {
return type(C).creationCode;
}
}
)";
m_metadataHash = CompilerStack::MetadataHash::None;
ALSO_VIA_YUL({
bytes bytecodeC = compileContract(codeC);
reset();
compileAndRun(codeC + codeD);
ABI_CHECK(callContractFunction("f()"), encodeArgs(0x20, bytecodeC.size()) + encode(bytecodeC, false));
m_doEwasmTestrun = false;
})
}
unsigned constexpr roundTo32(unsigned _num) unsigned constexpr roundTo32(unsigned _num)
{ {
return (_num + 31) / 32 * 32; return (_num + 31) / 32 * 32;

View File

@ -62,6 +62,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul); m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul);
m_compiler.enableIRGeneration(m_compileViaYul); m_compiler.enableIRGeneration(m_compileViaYul);
m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setRevertStringBehaviour(m_revertStrings);
m_compiler.setMetadataHash(m_metadataHash);
if (!m_compiler.compile()) if (!m_compiler.compile())
{ {
// The testing framework expects an exception for // The testing framework expects an exception for

View File

@ -75,11 +75,12 @@ public:
/// the latter only if it is forced. /// the latter only if it is forced.
static std::string addPreamble(std::string const& _sourceCode); static std::string addPreamble(std::string const& _sourceCode);
protected: protected:
using CompilerStack = solidity::frontend::CompilerStack;
solidity::frontend::CompilerStack m_compiler; CompilerStack m_compiler;
bool m_compileViaYul = false; bool m_compileViaYul = false;
bool m_compileToEwasm = false; bool m_compileToEwasm = false;
bool m_showMetadata = false; bool m_showMetadata = false;
CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS;
RevertStrings m_revertStrings = RevertStrings::Default; RevertStrings m_revertStrings = RevertStrings::Default;
}; };

View File

@ -26,6 +26,6 @@ contract Main {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1
// gas irOptimized: 113598 // gas irOptimized: 113613
// gas legacy: 126596 // gas legacy: 126596
// gas legacyOptimized: 113823 // gas legacyOptimized: 113823

View File

@ -26,6 +26,6 @@ contract Creator {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
// gas irOptimized: 443960 // gas irOptimized: 443989
// gas legacy: 590683 // gas legacy: 590683
// gas legacyOptimized: 448326 // gas legacyOptimized: 448326

View File

@ -26,6 +26,6 @@ contract Creator {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h"
// gas irOptimized: 300804 // gas irOptimized: 300837
// gas legacy: 428917 // gas legacy: 428917
// gas legacyOptimized: 298128 // gas legacyOptimized: 298128

View File

@ -17,7 +17,7 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// constructor(): 2 -> // constructor(): 2 ->
// gas irOptimized: 203967 // gas irOptimized: 203982
// gas legacy: 245842 // gas legacy: 245842
// gas legacyOptimized: 195676 // gas legacyOptimized: 195676
// f() -> 2 // f() -> 2

View File

@ -18,7 +18,7 @@ contract D {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// constructor(): 2 -> // constructor(): 2 ->
// gas irOptimized: 204130 // gas irOptimized: 204145
// gas legacy: 246202 // gas legacy: 246202
// gas legacyOptimized: 195914 // gas legacyOptimized: 195914
// f() -> 2 // f() -> 2

View File

@ -42,7 +42,7 @@ contract Main {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// constructor(), 22 wei -> // constructor(), 22 wei ->
// gas irOptimized: 284287 // gas irOptimized: 284321
// gas legacy: 402045 // gas legacy: 402045
// gas legacyOptimized: 266772 // gas legacyOptimized: 266772
// getFlag() -> true // getFlag() -> true

View File

@ -22,6 +22,6 @@ contract A {
// compileViaYul: also // compileViaYul: also
// ---- // ----
// f(), 10 ether -> 3007, 3008, 3009 // f(), 10 ether -> 3007, 3008, 3009
// gas irOptimized: 272413 // gas irOptimized: 272467
// gas legacy: 422501 // gas legacy: 422501
// gas legacyOptimized: 287472 // gas legacyOptimized: 287472

View File

@ -65,7 +65,7 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _
return TestResult::FatalError; return TestResult::FatalError;
} }
evmasm::Assembly assembly; evmasm::Assembly assembly{false, {}};
EthAssemblyAdapter adapter(assembly); EthAssemblyAdapter adapter(assembly);
EVMObjectCompiler::compile( EVMObjectCompiler::compile(
*stack.parserResult(), *stack.parserResult(),

View File

@ -185,25 +185,27 @@ void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet)
if (!_quiet) if (!_quiet)
cout << "Got " << numbers.size() << " inputs:" << endl; cout << "Got " << numbers.size() << " inputs:" << endl;
Assembly assembly;
for (u256 const& n: numbers)
{
if (!_quiet)
cout << n << endl;
assembly.append(n);
}
for (bool isCreation: {false, true}) for (bool isCreation: {false, true})
{
Assembly assembly{isCreation, {}};
for (u256 const& n: numbers)
{
if (!_quiet)
cout << n << endl;
assembly.append(n);
}
for (unsigned runs: {1u, 2u, 3u, 20u, 40u, 100u, 200u, 400u, 1000u}) for (unsigned runs: {1u, 2u, 3u, 20u, 40u, 100u, 200u, 400u, 1000u})
{ {
// Make a copy here so that each time we start with the original state. // Make a copy here so that each time we start with the original state.
Assembly tmp = assembly; Assembly tmp = assembly;
ConstantOptimisationMethod::optimiseConstants( ConstantOptimisationMethod::optimiseConstants(
isCreation, isCreation,
runs, runs,
langutil::EVMVersion{}, langutil::EVMVersion{},
tmp tmp
); );
} }
}
} }
void FuzzerUtil::testStandardCompiler(string const& _input, bool _quiet) void FuzzerUtil::testStandardCompiler(string const& _input, bool _quiet)