From 60eaa25ec8441627215a47e2fafb5b1da6fd1f9d Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 19 Jun 2023 00:56:04 -0300 Subject: [PATCH] make bytesRequired use PUSH0 --- libevmasm/Assembly.cpp | 4 ++-- libevmasm/AssemblyItem.cpp | 6 ++++-- libevmasm/AssemblyItem.h | 7 ++++--- libevmasm/ConstantOptimiser.cpp | 8 ++++---- libevmasm/ConstantOptimiser.h | 2 +- libevmasm/Inliner.cpp | 16 ++++++++-------- libevmasm/PeepholeOptimiser.cpp | 2 +- libevmasm/PeepholeOptimiser.h | 9 ++++++++- test/libevmasm/Optimiser.cpp | 16 ++++++++-------- 9 files changed, 40 insertions(+), 30 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index f524aad66..067ba7221 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -67,7 +67,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const ret += i.second.size(); for (AssemblyItem const& i: m_items) - ret += i.bytesRequired(tagSize, Precision::Approximate); + ret += i.bytesRequired(tagSize, m_evmVersion, Precision::Approximate); if (numberEncodingSize(ret) <= tagSize) return static_cast(ret); } @@ -383,7 +383,7 @@ std::map const& Assembly::optimiseInternal( if (_settings.runPeephole) { - PeepholeOptimiser peepOpt{m_items}; + PeepholeOptimiser peepOpt{m_items, m_evmVersion}; while (peepOpt.optimise()) { count++; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 6c2b673c6..b83cb2709 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -116,7 +116,7 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) setData(data); } -size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) const +size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _evmVersion, Precision _precision) const { switch (m_type) { @@ -124,7 +124,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) case Tag: // 1 byte for the JUMPDEST return 1; case Push: - return 1 + std::max(1, numberEncodingSize(data())); + return + 1 + + std::max((_evmVersion.hasPush0() ? 0 : 1), numberEncodingSize(data())); case PushSubSize: case PushProgramSize: return 1 + 4; // worst case: a 16MB program diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 077c1912d..34b469033 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -159,10 +159,11 @@ public: /// @returns an upper bound for the number of bytes required by this item, assuming that /// the value of a jump tag takes @a _addressLength bytes. + /// @param _evmVersion the EVM version /// @param _precision Whether to return a precise count (which involves /// counting immutable references which are only set after /// a call to `assemble()`) or an approx. count. - size_t bytesRequired(size_t _addressLength, Precision _precision = Precision::Precise) const; + size_t bytesRequired(size_t _addressLength, langutil::EVMVersion _evmVersion, Precision _precision = Precision::Precise) const; size_t arguments() const; size_t returnValues() const; size_t deposit() const { return returnValues() - arguments(); } @@ -204,11 +205,11 @@ private: mutable std::optional m_immutableOccurrences; }; -inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength, Precision _precision = Precision::Precise) +inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength, langutil::EVMVersion _evmVersion, Precision _precision = Precision::Precise) { size_t size = 0; for (AssemblyItem const& item: _items) - size += item.bytesRequired(_addressLength, _precision); + size += item.bytesRequired(_addressLength, _evmVersion, _precision); return size; } diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index c5ad3c3c5..1ce7ba920 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -103,9 +103,9 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion)); } -size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) +size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items, langutil::EVMVersion _evmVersion) { - return evmasm::bytesRequired(_items, 3, Precision::Approximate); // assume 3 byte addresses + return evmasm::bytesRequired(_items, 3, _evmVersion, Precision::Approximate); // assume 3 byte addresses } void ConstantOptimisationMethod::replaceConstants( @@ -146,7 +146,7 @@ bigint CodeCopyMethod::gasNeeded() const // Run gas: we ignore memory increase costs simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas, // Data gas for copy routines: Some bytes are zero, but we ignore them. - bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), + bytesRequired(copyRoutine(), m_params.evmVersion) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), // Data gas for data itself dataGas(toBigEndian(m_value)) ); @@ -362,7 +362,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const return combineGas( simpleRunGas(_routine, m_params.evmVersion) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), // Data gas for routine: Some bytes are zero, but we ignore them. - bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), + bytesRequired(_routine, m_params.evmVersion) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), 0 ); } diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index 5997931a9..6199c2296 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -79,7 +79,7 @@ protected: static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion); /// @returns the gas needed to store the given data literally bigint dataGas(bytes const& _data) const; - static size_t bytesRequired(AssemblyItems const& _items); + static size_t bytesRequired(AssemblyItems const& _items, langutil::EVMVersion _evmVersion); /// @returns the combined estimated gas usage taking @a m_params into account. bigint combineGas( bigint const& _runGas, diff --git a/libevmasm/Inliner.cpp b/libevmasm/Inliner.cpp index ded5172dd..37d47522d 100644 --- a/libevmasm/Inliner.cpp +++ b/libevmasm/Inliner.cpp @@ -59,10 +59,10 @@ u256 executionCost(RangeType const& _itemRange, langutil::EVMVersion _evmVersion } /// @returns an estimation of the code size in bytes needed for the AssemblyItems in @a _itemRange. template -uint64_t codeSize(RangeType const& _itemRange) +uint64_t codeSize(RangeType const& _itemRange, langutil::EVMVersion _evmVersion) { return ranges::accumulate(_itemRange | ranges::views::transform( - [](auto const& _item) { return _item.bytesRequired(2, Precision::Approximate); } + [&](auto const& _item) { return _item.bytesRequired(2, _evmVersion, Precision::Approximate); } ), 0u); } /// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, std::nullopt otherwise. @@ -139,7 +139,7 @@ std::map Inliner::determineInlinableBlocks(Asse bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span _block, uint64_t _pushTagCount) const { // Accumulate size of the inline candidate block in bytes (without the return jump). - uint64_t functionBodySize = codeSize(ranges::views::drop_last(_block, 1)); + uint64_t functionBodySize = codeSize(ranges::views::drop_last(_block, 1), m_evmVersion); // Use the number of push tags as approximation of the average number of calls to the function per run. uint64_t numberOfCalls = _pushTagCount; @@ -167,8 +167,8 @@ bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span Inliner::shouldInline(size_t _tag, AssemblyItem cons AssemblyItem{Instruction::JUMP}, }; if ( - GasMeter::dataGas(codeSize(_block.items), m_isCreation, m_evmVersion) <= - GasMeter::dataGas(codeSize(jumpPattern), m_isCreation, m_evmVersion) + GasMeter::dataGas(codeSize(_block.items, m_evmVersion), m_isCreation, m_evmVersion) <= + GasMeter::dataGas(codeSize(jumpPattern, m_evmVersion), m_isCreation, m_evmVersion) ) return blockExit; } diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index e9231c0a5..6437c663d 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -520,7 +520,7 @@ bool PeepholeOptimiser::optimise() ); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( - evmasm::bytesRequired(m_optimisedItems, 3, approx) < evmasm::bytesRequired(m_items, 3, approx) || + evmasm::bytesRequired(m_optimisedItems, 3, m_evmVersion, approx) < evmasm::bytesRequired(m_items, 3, m_evmVersion, approx) || numberOfPops(m_optimisedItems) > numberOfPops(m_items) ) )) diff --git a/libevmasm/PeepholeOptimiser.h b/libevmasm/PeepholeOptimiser.h index 64ad5227f..8e3161b21 100644 --- a/libevmasm/PeepholeOptimiser.h +++ b/libevmasm/PeepholeOptimiser.h @@ -25,6 +25,8 @@ #include #include +#include + namespace solidity::evmasm { class AssemblyItem; @@ -41,7 +43,11 @@ public: class PeepholeOptimiser { public: - explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {} + explicit PeepholeOptimiser(AssemblyItems& _items, langutil::EVMVersion const _evmVersion): + m_items(_items), + m_evmVersion(_evmVersion) + { + } virtual ~PeepholeOptimiser() = default; bool optimise(); @@ -49,6 +55,7 @@ public: private: AssemblyItems& m_items; AssemblyItems m_optimisedItems; + langutil::EVMVersion const m_evmVersion; }; } diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 6f1a13759..b341ac26b 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -1001,7 +1001,7 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code) AssemblyItem(PushTag, 1), Instruction::JUMP }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1027,7 +1027,7 @@ BOOST_AUTO_TEST_CASE(peephole_double_push) u256(4), u256(5) }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) Instruction::LT, Instruction::POP }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); for (size_t i = 0; i < 3; i++) BOOST_CHECK(peepOpt.optimise()); BOOST_CHECK(items.empty()); @@ -1076,7 +1076,7 @@ BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) u256(4), u256(5) }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1114,7 +1114,7 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) u256(4), u256(5) }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(!peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1149,7 +1149,7 @@ BOOST_AUTO_TEST_CASE(peephole_swap_comparison) u256(4), u256(5) }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1175,7 +1175,7 @@ BOOST_AUTO_TEST_CASE(peephole_truthy_and) AssemblyItem(PushTag, 1), Instruction::JUMPI }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), @@ -1208,7 +1208,7 @@ BOOST_AUTO_TEST_CASE(peephole_iszero_iszero_jumpi) u256(0x20), Instruction::RETURN }; - PeepholeOptimiser peepOpt(items); + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(),