mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
make bytesRequired use PUSH0
This commit is contained in:
parent
cd611b2292
commit
60eaa25ec8
@ -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++;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
Loading…
Reference in New Issue
Block a user