Store whether an evmasm Assembly is creation code.

This commit is contained in:
Daniel Kirchner 2022-03-07 12:49:23 +01:00
parent c6fcb6c395
commit ce0a3e93f2
23 changed files with 62 additions and 47 deletions

View File

@ -8,6 +8,7 @@ Compiler Features:
* JSON-AST: Added selector field for errors and events.
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)

View File

@ -411,13 +411,15 @@ map<u256, u256> const& Assembly::optimiseInternal(
if (m_tagReplacements)
return *m_tagReplacements;
assertThrow(_settings.isCreation == m_creation, OptimizerException, "Mismatching creation settings.");
// Run optimisation for sub-assemblies.
for (size_t subId = 0; subId < m_subs.size(); ++subId)
{
OptimiserSettings settings = _settings;
// Disable creation mode for sub-assemblies.
settings.isCreation = false;
map<u256, u256> const& subTagReplacements = m_subs[subId]->optimiseInternal(
Assembly& sub = *m_subs[subId];
settings.isCreation = sub.isCreation();
map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
settings,
JumpdestRemover::referencedTags(m_items, subId)
);

View File

@ -48,7 +48,7 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly
{
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 newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
@ -157,6 +157,8 @@ public:
std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
size_t encodeSubPath(std::vector<size_t> const& _subPath);
bool isCreation() const { return m_creation; }
protected:
/// 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
@ -214,6 +216,8 @@ protected:
mutable std::vector<size_t> m_tagPositionsInBytecode;
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
/// currently
std::string m_name;

View File

@ -65,7 +65,7 @@ public:
RevertStrings _revertStrings,
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_revertStrings(_revertStrings),
m_reservedMemory{0},

View File

@ -38,6 +38,7 @@
#include <libevmasm/Assembly.h>
#include <liblangutil/Scanner.h>
#include <boost/algorithm/string.hpp>
#include <optional>
using namespace std;
@ -194,7 +195,10 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation)
yulAssert(_object.analysisInfo, "");
for (auto& subNode: _object.subObjects)
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);
unique_ptr<GasMeter> meter;
@ -281,7 +285,7 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
yulAssert(m_parserResult->code, "");
yulAssert(m_parserResult->analysisInfo, "");
evmasm::Assembly assembly;
evmasm::Assembly assembly(true, {});
EthAssemblyAdapter adapter(assembly);
compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation);

View File

@ -98,7 +98,7 @@ public:
/// Append the assembled size as a constant.
virtual void appendAssemblySize() = 0;
/// 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.
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
/// Appends the size of the given sub-assembly or data.

View File

@ -30,6 +30,8 @@
#include <libyul/Object.h>
#include <libyul/Exceptions.h>
#include <boost/algorithm/string.hpp>
using namespace solidity::yul;
using namespace std;
@ -48,7 +50,8 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
for (auto const& subNode: _object.subObjects)
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;
subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize);

View File

@ -122,9 +122,9 @@ void EthAssemblyAdapter::appendAssemblySize()
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);
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 appendJumpToIf(LabelID _labelId, JumpType _jumpType) 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 appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

View File

@ -98,7 +98,7 @@ void NoOutputAssembly::appendAssemblySize()
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.");
return {};

View File

@ -65,7 +65,7 @@ public:
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) 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 appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

View File

@ -21,7 +21,7 @@ object "RunsTest1" {
Binary representation:
602580600c6000396000f3fe7fabc123450000000000000000000000000000000000000000000000000000000060005500
600c80600c6000396000f3fe63abc1234560e01b60005500
Text representation:
/* "yul_optimize_runs/input.yul":106:125 */
@ -40,8 +40,7 @@ Text representation:
stop
sub_0: assembly {
/* "yul_optimize_runs/input.yul":237:257 */
0xabc1234500000000000000000000000000000000000000000000000000000000
shl(0xe0, 0xabc12345)
/* "yul_optimize_runs/input.yul":277:278 */
0x00
/* "yul_optimize_runs/input.yul":270:288 */

View File

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

View File

@ -1250,8 +1250,8 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
// tag unifications (due to block deduplication) is also
// visible at the super-assembly.
Assembly main;
AssemblyPointer sub = make_shared<Assembly>();
Assembly main{false, {}};
AssemblyPointer sub = make_shared<Assembly>(true, string{});
sub->append(u256(1));
auto t1 = sub->newTag();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -185,25 +185,27 @@ void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet)
if (!_quiet)
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})
{
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})
{
// Make a copy here so that each time we start with the original state.
Assembly tmp = assembly;
ConstantOptimisationMethod::optimiseConstants(
isCreation,
runs,
langutil::EVMVersion{},
tmp
isCreation,
runs,
langutil::EVMVersion{},
tmp
);
}
}
}
void FuzzerUtil::testStandardCompiler(string const& _input, bool _quiet)