make bytesRequired use PUSH0

This commit is contained in:
Matheus Aguiar 2023-06-19 00:56:04 -03:00
parent cd611b2292
commit 60eaa25ec8
9 changed files with 40 additions and 30 deletions

View File

@ -67,7 +67,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
ret += i.second.size(); ret += i.second.size();
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
ret += i.bytesRequired(tagSize, Precision::Approximate); ret += i.bytesRequired(tagSize, m_evmVersion, Precision::Approximate);
if (numberEncodingSize(ret) <= tagSize) if (numberEncodingSize(ret) <= tagSize)
return static_cast<unsigned>(ret); return static_cast<unsigned>(ret);
} }
@ -383,7 +383,7 @@ std::map<u256, u256> const& Assembly::optimiseInternal(
if (_settings.runPeephole) if (_settings.runPeephole)
{ {
PeepholeOptimiser peepOpt{m_items}; PeepholeOptimiser peepOpt{m_items, m_evmVersion};
while (peepOpt.optimise()) while (peepOpt.optimise())
{ {
count++; count++;

View File

@ -116,7 +116,7 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
setData(data); 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) switch (m_type)
{ {
@ -124,7 +124,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
case Tag: // 1 byte for the JUMPDEST case Tag: // 1 byte for the JUMPDEST
return 1; return 1;
case Push: case Push:
return 1 + std::max<size_t>(1, numberEncodingSize(data())); return
1 +
std::max<size_t>((_evmVersion.hasPush0() ? 0 : 1), numberEncodingSize(data()));
case PushSubSize: case PushSubSize:
case PushProgramSize: case PushProgramSize:
return 1 + 4; // worst case: a 16MB program return 1 + 4; // worst case: a 16MB program

View File

@ -159,10 +159,11 @@ public:
/// @returns an upper bound for the number of bytes required by this item, assuming that /// @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. /// 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 /// @param _precision Whether to return a precise count (which involves
/// counting immutable references which are only set after /// counting immutable references which are only set after
/// a call to `assemble()`) or an approx. count. /// 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 arguments() const;
size_t returnValues() const; size_t returnValues() const;
size_t deposit() const { return returnValues() - arguments(); } size_t deposit() const { return returnValues() - arguments(); }
@ -204,11 +205,11 @@ private:
mutable std::optional<size_t> m_immutableOccurrences; mutable std::optional<size_t> 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; size_t size = 0;
for (AssemblyItem const& item: _items) for (AssemblyItem const& item: _items)
size += item.bytesRequired(_addressLength, _precision); size += item.bytesRequired(_addressLength, _evmVersion, _precision);
return size; return size;
} }

View File

@ -103,9 +103,9 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion)); 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( void ConstantOptimisationMethod::replaceConstants(
@ -146,7 +146,7 @@ bigint CodeCopyMethod::gasNeeded() const
// Run gas: we ignore memory increase costs // Run gas: we ignore memory increase costs
simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas, simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them. // 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 // Data gas for data itself
dataGas(toBigEndian(m_value)) dataGas(toBigEndian(m_value))
); );
@ -362,7 +362,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
return combineGas( return combineGas(
simpleRunGas(_routine, m_params.evmVersion) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), 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. // 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 0
); );
} }

View File

@ -79,7 +79,7 @@ protected:
static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion); static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion);
/// @returns the gas needed to store the given data literally /// @returns the gas needed to store the given data literally
bigint dataGas(bytes const& _data) const; 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. /// @returns the combined estimated gas usage taking @a m_params into account.
bigint combineGas( bigint combineGas(
bigint const& _runGas, bigint const& _runGas,

View File

@ -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. /// @returns an estimation of the code size in bytes needed for the AssemblyItems in @a _itemRange.
template<typename RangeType> template<typename RangeType>
uint64_t codeSize(RangeType const& _itemRange) uint64_t codeSize(RangeType const& _itemRange, langutil::EVMVersion _evmVersion)
{ {
return ranges::accumulate(_itemRange | ranges::views::transform( 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); ), 0u);
} }
/// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, std::nullopt otherwise. /// @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<size_t, Inliner::InlinableBlock> Inliner::determineInlinableBlocks(Asse
bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyItem const> _block, uint64_t _pushTagCount) const bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyItem const> _block, uint64_t _pushTagCount) const
{ {
// Accumulate size of the inline candidate block in bytes (without the return jump). // 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. // Use the number of push tags as approximation of the average number of calls to the function per run.
uint64_t numberOfCalls = _pushTagCount; uint64_t numberOfCalls = _pushTagCount;
@ -167,8 +167,8 @@ bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyIte
); );
// Each call site deposits the call site pattern, whereas the jump site pattern and the function itself are deposited once. // Each call site deposits the call site pattern, whereas the jump site pattern and the function itself are deposited once.
bigint uninlinedDepositCost = GasMeter::dataGas( bigint uninlinedDepositCost = GasMeter::dataGas(
numberOfCallSites * codeSize(uninlinedCallSitePattern) + numberOfCallSites * codeSize(uninlinedCallSitePattern, m_evmVersion) +
codeSize(uninlinedFunctionPattern) + codeSize(uninlinedFunctionPattern, m_evmVersion) +
functionBodySize, functionBodySize,
m_isCreation, m_isCreation,
m_evmVersion m_evmVersion
@ -185,7 +185,7 @@ bool Inliner::shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyIte
// the heuristics is optimistic. // the heuristics is optimistic.
if (m_tagsReferencedFromOutside.count(_tag)) if (m_tagsReferencedFromOutside.count(_tag))
inlinedDepositCost += GasMeter::dataGas( inlinedDepositCost += GasMeter::dataGas(
codeSize(uninlinedFunctionPattern) + functionBodySize, codeSize(uninlinedFunctionPattern, m_evmVersion) + functionBodySize,
m_isCreation, m_isCreation,
m_evmVersion m_evmVersion
); );
@ -225,8 +225,8 @@ std::optional<AssemblyItem> Inliner::shouldInline(size_t _tag, AssemblyItem cons
AssemblyItem{Instruction::JUMP}, AssemblyItem{Instruction::JUMP},
}; };
if ( if (
GasMeter::dataGas(codeSize(_block.items), m_isCreation, m_evmVersion) <= GasMeter::dataGas(codeSize(_block.items, m_evmVersion), m_isCreation, m_evmVersion) <=
GasMeter::dataGas(codeSize(jumpPattern), m_isCreation, m_evmVersion) GasMeter::dataGas(codeSize(jumpPattern, m_evmVersion), m_isCreation, m_evmVersion)
) )
return blockExit; return blockExit;
} }

View File

@ -520,7 +520,7 @@ bool PeepholeOptimiser::optimise()
); );
if (m_optimisedItems.size() < m_items.size() || ( if (m_optimisedItems.size() < m_items.size() || (
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) numberOfPops(m_optimisedItems) > numberOfPops(m_items)
) )
)) ))

View File

@ -25,6 +25,8 @@
#include <cstddef> #include <cstddef>
#include <iterator> #include <iterator>
#include <liblangutil/EVMVersion.h>
namespace solidity::evmasm namespace solidity::evmasm
{ {
class AssemblyItem; class AssemblyItem;
@ -41,7 +43,11 @@ public:
class PeepholeOptimiser class PeepholeOptimiser
{ {
public: 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; virtual ~PeepholeOptimiser() = default;
bool optimise(); bool optimise();
@ -49,6 +55,7 @@ public:
private: private:
AssemblyItems& m_items; AssemblyItems& m_items;
AssemblyItems m_optimisedItems; AssemblyItems m_optimisedItems;
langutil::EVMVersion const m_evmVersion;
}; };
} }

View File

@ -1001,7 +1001,7 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code)
AssemblyItem(PushTag, 1), AssemblyItem(PushTag, 1),
Instruction::JUMP Instruction::JUMP
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1027,7 +1027,7 @@ BOOST_AUTO_TEST_CASE(peephole_double_push)
u256(4), u256(4),
u256(5) u256(5)
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1043,7 +1043,7 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize)
Instruction::LT, Instruction::LT,
Instruction::POP Instruction::POP
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
for (size_t i = 0; i < 3; i++) for (size_t i = 0; i < 3; i++)
BOOST_CHECK(peepOpt.optimise()); BOOST_CHECK(peepOpt.optimise());
BOOST_CHECK(items.empty()); BOOST_CHECK(items.empty());
@ -1076,7 +1076,7 @@ BOOST_AUTO_TEST_CASE(peephole_commutative_swap1)
u256(4), u256(4),
u256(5) u256(5)
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1114,7 +1114,7 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1)
u256(4), u256(4),
u256(5) u256(5)
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(!peepOpt.optimise()); BOOST_REQUIRE(!peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1149,7 +1149,7 @@ BOOST_AUTO_TEST_CASE(peephole_swap_comparison)
u256(4), u256(4),
u256(5) u256(5)
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1175,7 +1175,7 @@ BOOST_AUTO_TEST_CASE(peephole_truthy_and)
AssemblyItem(PushTag, 1), AssemblyItem(PushTag, 1),
Instruction::JUMPI Instruction::JUMPI
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),
@ -1208,7 +1208,7 @@ BOOST_AUTO_TEST_CASE(peephole_iszero_iszero_jumpi)
u256(0x20), u256(0x20),
Instruction::RETURN Instruction::RETURN
}; };
PeepholeOptimiser peepOpt(items); PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion());
BOOST_REQUIRE(peepOpt.optimise()); BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS( BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(), items.begin(), items.end(),