mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add inlining for old optimizer.
This commit is contained in:
parent
e777cad78a
commit
cb74a45fd6
@ -5,6 +5,7 @@ Language Features:
|
|||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* AST: Export NatSpec comments above each statement as their documentation.
|
* AST: Export NatSpec comments above each statement as their documentation.
|
||||||
|
* Optimizer: Simple inlining when jumping to small blocks that jump again after a few side-effect free opcodes.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -80,6 +80,8 @@ explanatory purposes.
|
|||||||
details: {
|
details: {
|
||||||
// peephole defaults to "true"
|
// peephole defaults to "true"
|
||||||
peephole: true,
|
peephole: true,
|
||||||
|
// inliner defaults to "true"
|
||||||
|
inliner: true,
|
||||||
// jumpdestRemover defaults to "true"
|
// jumpdestRemover defaults to "true"
|
||||||
jumpdestRemover: true,
|
jumpdestRemover: true,
|
||||||
orderLiterals: false,
|
orderLiterals: false,
|
||||||
|
@ -246,6 +246,9 @@ Input Description
|
|||||||
// The peephole optimizer is always on if no details are given,
|
// The peephole optimizer is always on if no details are given,
|
||||||
// use details to switch it off.
|
// use details to switch it off.
|
||||||
"peephole": true,
|
"peephole": true,
|
||||||
|
// The inliner is always on if no details are given,
|
||||||
|
// use details to switch it off.
|
||||||
|
"inliner": true,
|
||||||
// The unused jumpdest remover is always on if no details are given,
|
// The unused jumpdest remover is always on if no details are given,
|
||||||
// use details to switch it off.
|
// use details to switch it off.
|
||||||
"jumpdestRemover": true,
|
"jumpdestRemover": true,
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/ControlFlowGraph.h>
|
#include <libevmasm/ControlFlowGraph.h>
|
||||||
#include <libevmasm/PeepholeOptimiser.h>
|
#include <libevmasm/PeepholeOptimiser.h>
|
||||||
|
#include <libevmasm/Inliner.h>
|
||||||
#include <libevmasm/JumpdestRemover.h>
|
#include <libevmasm/JumpdestRemover.h>
|
||||||
#include <libevmasm/BlockDeduplicator.h>
|
#include <libevmasm/BlockDeduplicator.h>
|
||||||
#include <libevmasm/ConstantOptimiser.h>
|
#include <libevmasm/ConstantOptimiser.h>
|
||||||
@ -375,6 +376,7 @@ Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreat
|
|||||||
{
|
{
|
||||||
OptimiserSettings settings;
|
OptimiserSettings settings;
|
||||||
settings.isCreation = _isCreation;
|
settings.isCreation = _isCreation;
|
||||||
|
settings.runInliner = true;
|
||||||
settings.runJumpdestRemover = true;
|
settings.runJumpdestRemover = true;
|
||||||
settings.runPeephole = true;
|
settings.runPeephole = true;
|
||||||
if (_enable)
|
if (_enable)
|
||||||
@ -421,6 +423,15 @@ map<u256, u256> Assembly::optimiseInternal(
|
|||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
if (_settings.runInliner)
|
||||||
|
Inliner{
|
||||||
|
m_items,
|
||||||
|
_tagsReferencedFromOutside,
|
||||||
|
_settings.expectedExecutionsPerDeployment,
|
||||||
|
_settings.isCreation,
|
||||||
|
_settings.evmVersion
|
||||||
|
}.optimise();
|
||||||
|
|
||||||
if (_settings.runJumpdestRemover)
|
if (_settings.runJumpdestRemover)
|
||||||
{
|
{
|
||||||
JumpdestRemover jumpdestOpt{m_items};
|
JumpdestRemover jumpdestOpt{m_items};
|
||||||
|
@ -106,6 +106,7 @@ public:
|
|||||||
struct OptimiserSettings
|
struct OptimiserSettings
|
||||||
{
|
{
|
||||||
bool isCreation = false;
|
bool isCreation = false;
|
||||||
|
bool runInliner = false;
|
||||||
bool runJumpdestRemover = false;
|
bool runJumpdestRemover = false;
|
||||||
bool runPeephole = false;
|
bool runPeephole = false;
|
||||||
bool runDeduplicate = false;
|
bool runDeduplicate = false;
|
||||||
|
@ -16,6 +16,8 @@ set(sources
|
|||||||
ExpressionClasses.h
|
ExpressionClasses.h
|
||||||
GasMeter.cpp
|
GasMeter.cpp
|
||||||
GasMeter.h
|
GasMeter.h
|
||||||
|
Inliner.cpp
|
||||||
|
Inliner.h
|
||||||
Instruction.cpp
|
Instruction.cpp
|
||||||
Instruction.h
|
Instruction.h
|
||||||
JumpdestRemover.cpp
|
JumpdestRemover.cpp
|
||||||
|
@ -286,3 +286,11 @@ u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersio
|
|||||||
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
|
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
|
||||||
return u256(gas);
|
return u256(gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u256 GasMeter::dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
bigint gas = bigint(_length) * (_inCreation ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::createDataGas);
|
||||||
|
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
|
||||||
|
return u256(gas);
|
||||||
|
}
|
||||||
|
@ -125,6 +125,12 @@ public:
|
|||||||
static GasConsumption infinite() { return GasConsumption(0, true); }
|
static GasConsumption infinite() { return GasConsumption(0, true); }
|
||||||
|
|
||||||
GasConsumption& operator+=(GasConsumption const& _other);
|
GasConsumption& operator+=(GasConsumption const& _other);
|
||||||
|
GasConsumption operator+(GasConsumption const& _other) const
|
||||||
|
{
|
||||||
|
GasConsumption result = *this;
|
||||||
|
result += _other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
bool operator<(GasConsumption const& _other) const
|
bool operator<(GasConsumption const& _other) const
|
||||||
{
|
{
|
||||||
return std::make_pair(isInfinite, value) < std::make_pair(_other.isInfinite, _other.value);
|
return std::make_pair(isInfinite, value) < std::make_pair(_other.isInfinite, _other.value);
|
||||||
@ -154,6 +160,11 @@ public:
|
|||||||
/// otherwise code will be stored and have to pay "createDataGas" cost.
|
/// otherwise code will be stored and have to pay "createDataGas" cost.
|
||||||
static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion);
|
static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion);
|
||||||
|
|
||||||
|
/// @returns the gas cost of non-zero data of the supplied length, depending whether it is in creation code, or not.
|
||||||
|
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
|
||||||
|
/// otherwise code will be stored and have to pay "createDataGas" cost.
|
||||||
|
static u256 dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
|
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
|
||||||
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
|
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
|
||||||
|
259
libevmasm/Inliner.cpp
Normal file
259
libevmasm/Inliner.cpp
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
/**
|
||||||
|
* @file Inliner.cpp
|
||||||
|
* Inlines small code snippets by replacing JUMP with a copy of the code jumped to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libevmasm/Inliner.h>
|
||||||
|
|
||||||
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
#include <libevmasm/GasMeter.h>
|
||||||
|
#include <libevmasm/KnownState.h>
|
||||||
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
#include <range/v3/numeric/accumulate.hpp>
|
||||||
|
#include <range/v3/view/drop_last.hpp>
|
||||||
|
#include <range/v3/view/enumerate.hpp>
|
||||||
|
#include <range/v3/view/slice.hpp>
|
||||||
|
#include <range/v3/view/transform.hpp>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/// @returns an estimation of the runtime gas cost of the AsssemblyItems in @a _itemRange.
|
||||||
|
template<typename RangeType>
|
||||||
|
u256 executionCost(RangeType const& _itemRange, langutil::EVMVersion _evmVersion)
|
||||||
|
{
|
||||||
|
GasMeter gasMeter{std::make_shared<KnownState>(), _evmVersion};
|
||||||
|
auto gasConsumption = ranges::accumulate(_itemRange | ranges::views::transform(
|
||||||
|
[&gasMeter](auto const& _item) { return gasMeter.estimateMax(_item, false); }
|
||||||
|
), GasMeter::GasConsumption());
|
||||||
|
if (gasConsumption.isInfinite)
|
||||||
|
return numeric_limits<u256>::max();
|
||||||
|
else
|
||||||
|
return gasConsumption.value;
|
||||||
|
}
|
||||||
|
/// @returns an estimation of the code size in bytes needed for the AssemblyItems in @a _itemRange.
|
||||||
|
template<typename RangeType>
|
||||||
|
uint64_t codeSize(RangeType const& _itemRange)
|
||||||
|
{
|
||||||
|
return ranges::accumulate(_itemRange | ranges::views::transform(
|
||||||
|
[](auto const& _item) { return _item.bytesRequired(2); }
|
||||||
|
), 0u);
|
||||||
|
}
|
||||||
|
/// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.
|
||||||
|
optional<size_t> getLocalTag(AssemblyItem const& _item)
|
||||||
|
{
|
||||||
|
if (_item.type() != PushTag && _item.type() != Tag)
|
||||||
|
return nullopt;
|
||||||
|
auto [subId, tag] = _item.splitForeignPushTag();
|
||||||
|
if (subId != numeric_limits<size_t>::max())
|
||||||
|
return nullopt;
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inliner::isInlineCandidate(size_t _tag, ranges::span<AssemblyItem const> _items) const
|
||||||
|
{
|
||||||
|
assertThrow(_items.size() > 0, OptimizerException, "");
|
||||||
|
|
||||||
|
// Only consider blocks that end in a JUMP for now. This can e.g. be extended to include transaction terminating
|
||||||
|
// instructions as well in the future.
|
||||||
|
if (_items.back() != Instruction::JUMP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Never inline tags that reference themselves.
|
||||||
|
for (AssemblyItem const& item: _items)
|
||||||
|
if (item.type() == PushTag)
|
||||||
|
if (getLocalTag(item) == _tag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<size_t, Inliner::InlinableBlock> Inliner::determineInlinableBlocks(AssemblyItems const& _items) const
|
||||||
|
{
|
||||||
|
std::map<size_t, ranges::span<AssemblyItem const>> inlinableBlockItems;
|
||||||
|
std::map<size_t, uint64_t> numPushTags;
|
||||||
|
std::optional<size_t> lastTag;
|
||||||
|
for (auto&& [index, item]: _items | ranges::views::enumerate)
|
||||||
|
{
|
||||||
|
// The number of PushTags approximates the number of calls to a block.
|
||||||
|
if (item.type() == PushTag)
|
||||||
|
if (optional<size_t> tag = getLocalTag(item))
|
||||||
|
++numPushTags[*tag];
|
||||||
|
|
||||||
|
// We can only inline blocks with straight control flow that end in a jump.
|
||||||
|
// Using breaksCSEAnalysisBlock will hopefully allow the return jump to be optimized after inlining.
|
||||||
|
if (lastTag && SemanticInformation::breaksCSEAnalysisBlock(item, false))
|
||||||
|
{
|
||||||
|
ranges::span<AssemblyItem const> block = _items | ranges::views::slice(*lastTag + 1, index + 1);
|
||||||
|
if (optional<size_t> tag = getLocalTag(_items[*lastTag]))
|
||||||
|
if (isInlineCandidate(*tag, block))
|
||||||
|
inlinableBlockItems[*tag] = block;
|
||||||
|
lastTag.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type() == Tag)
|
||||||
|
{
|
||||||
|
assertThrow(getLocalTag(item), OptimizerException, "");
|
||||||
|
lastTag = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the number of PushTags alongside the assembly items and discard tags that are never pushed.
|
||||||
|
map<size_t, InlinableBlock> result;
|
||||||
|
for (auto&& [tag, items]: inlinableBlockItems)
|
||||||
|
if (uint64_t const* numPushes = util::valueOrNullptr(numPushTags, tag))
|
||||||
|
result.emplace(tag, InlinableBlock{items, *numPushes});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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).
|
||||||
|
uint64_t functionBodySize = codeSize(ranges::views::drop_last(_block, 1));
|
||||||
|
|
||||||
|
// Use the number of push tags as approximation of the average number of calls to the function per run.
|
||||||
|
uint64_t numberOfCalls = _pushTagCount;
|
||||||
|
// Also use the number of push tags as approximation of the number of call sites to the function.
|
||||||
|
uint64_t numberOfCallSites = _pushTagCount;
|
||||||
|
|
||||||
|
static AssemblyItems const uninlinedCallSitePattern = {
|
||||||
|
AssemblyItem{PushTag},
|
||||||
|
AssemblyItem{PushTag},
|
||||||
|
AssemblyItem{Instruction::JUMP},
|
||||||
|
AssemblyItem{Tag}
|
||||||
|
};
|
||||||
|
static AssemblyItems const uninlinedFunctionPattern = {
|
||||||
|
AssemblyItem{Tag},
|
||||||
|
// Actual function body of size functionBodySize. Handled separately below.
|
||||||
|
AssemblyItem{Instruction::JUMP}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Both the call site and jump site pattern is executed for each call.
|
||||||
|
// Since the function body has to be executed equally often both with and without inlining,
|
||||||
|
// it can be ignored.
|
||||||
|
bigint uninlinedExecutionCost = numberOfCalls * (
|
||||||
|
executionCost(uninlinedCallSitePattern, m_evmVersion) +
|
||||||
|
executionCost(uninlinedFunctionPattern, m_evmVersion)
|
||||||
|
);
|
||||||
|
// Each call site deposits the call site pattern, whereas the jump site pattern and the function itself are deposited once.
|
||||||
|
bigint uninlinedDepositCost = GasMeter::dataGas(
|
||||||
|
numberOfCallSites * codeSize(uninlinedCallSitePattern) +
|
||||||
|
codeSize(uninlinedFunctionPattern) +
|
||||||
|
functionBodySize,
|
||||||
|
m_isCreation,
|
||||||
|
m_evmVersion
|
||||||
|
);
|
||||||
|
// When inlining the execution cost beyond the actual function execution is zero,
|
||||||
|
// but for each call site a copy of the function is deposited.
|
||||||
|
bigint inlinedDepositCost = GasMeter::dataGas(
|
||||||
|
numberOfCallSites * functionBodySize,
|
||||||
|
m_isCreation,
|
||||||
|
m_evmVersion
|
||||||
|
);
|
||||||
|
// If the block is referenced from outside the current subassembly, the original function cannot be removed.
|
||||||
|
// Note that the function also cannot always be removed, if it is not referenced from outside, but in that case
|
||||||
|
// the heuristics is optimistic.
|
||||||
|
if (m_tagsReferencedFromOutside.count(_tag))
|
||||||
|
inlinedDepositCost += GasMeter::dataGas(
|
||||||
|
uninlinedFunctionPattern.size() + functionBodySize,
|
||||||
|
m_isCreation,
|
||||||
|
m_evmVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the estimated runtime cost over the lifetime of the contract plus the deposit cost in the uninlined case
|
||||||
|
// exceed the inlined deposit costs, it is beneficial to inline.
|
||||||
|
if (bigint(m_runs) * uninlinedExecutionCost + uninlinedDepositCost > inlinedDepositCost)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
optional<AssemblyItem::JumpType> Inliner::shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const
|
||||||
|
{
|
||||||
|
AssemblyItem exitJump = _block.items.back();
|
||||||
|
assertThrow(_jump == Instruction::JUMP && exitJump == Instruction::JUMP, OptimizerException, "");
|
||||||
|
|
||||||
|
if (
|
||||||
|
_jump.getJumpType() == AssemblyItem::JumpType::IntoFunction &&
|
||||||
|
exitJump.getJumpType() == AssemblyItem::JumpType::OutOfFunction
|
||||||
|
)
|
||||||
|
return
|
||||||
|
shouldInlineFullFunctionBody(_tag, _block.items, _block.pushTagCount) ?
|
||||||
|
make_optional(AssemblyItem::JumpType::Ordinary) : nullopt;
|
||||||
|
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Inliner::optimise()
|
||||||
|
{
|
||||||
|
std::map<size_t, InlinableBlock> inlinableBlocks = determineInlinableBlocks(m_items);
|
||||||
|
|
||||||
|
if (inlinableBlocks.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssemblyItems newItems;
|
||||||
|
for (auto it = m_items.begin(); it != m_items.end(); ++it)
|
||||||
|
{
|
||||||
|
AssemblyItem const& item = *it;
|
||||||
|
if (next(it) != m_items.end())
|
||||||
|
{
|
||||||
|
AssemblyItem const& nextItem = *next(it);
|
||||||
|
if (item.type() == PushTag && nextItem == Instruction::JUMP)
|
||||||
|
{
|
||||||
|
if (optional<size_t> tag = getLocalTag(item))
|
||||||
|
if (auto* inlinableBlock = util::valueOrNullptr(inlinableBlocks, *tag))
|
||||||
|
if (auto exitJumpType = shouldInline(*tag, nextItem, *inlinableBlock))
|
||||||
|
{
|
||||||
|
newItems += inlinableBlock->items;
|
||||||
|
newItems.back().setJumpType(*exitJumpType);
|
||||||
|
|
||||||
|
// We are removing one push tag to the block we inline.
|
||||||
|
--inlinableBlock->pushTagCount;
|
||||||
|
// We might increase the number of push tags to other blocks.
|
||||||
|
for (AssemblyItem const& inlinedItem: inlinableBlock->items)
|
||||||
|
if (inlinedItem.type() == PushTag)
|
||||||
|
if (optional<size_t> duplicatedTag = getLocalTag(inlinedItem))
|
||||||
|
if (auto* block = util::valueOrNullptr(inlinableBlocks, *duplicatedTag))
|
||||||
|
++block->pushTagCount;
|
||||||
|
|
||||||
|
// Skip the original jump to the inlined tag and continue.
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newItems.emplace_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_items = move(newItems);
|
||||||
|
}
|
83
libevmasm/Inliner.h
Normal file
83
libevmasm/Inliner.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
/**
|
||||||
|
* @file Inliner.h
|
||||||
|
* Inlines small code snippets by replacing JUMP with a copy of the code jumped to.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolutil/Common.h>
|
||||||
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/span.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::evmasm
|
||||||
|
{
|
||||||
|
|
||||||
|
class Inliner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Inliner(
|
||||||
|
AssemblyItems& _items,
|
||||||
|
std::set<size_t> const& _tagsReferencedFromOutside,
|
||||||
|
size_t _runs,
|
||||||
|
bool _isCreation,
|
||||||
|
langutil::EVMVersion _evmVersion
|
||||||
|
):
|
||||||
|
m_items(_items),
|
||||||
|
m_tagsReferencedFromOutside(_tagsReferencedFromOutside),
|
||||||
|
m_runs(_runs),
|
||||||
|
m_isCreation(_isCreation),
|
||||||
|
m_evmVersion(_evmVersion)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~Inliner() = default;
|
||||||
|
|
||||||
|
void optimise();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct InlinableBlock
|
||||||
|
{
|
||||||
|
ranges::span<AssemblyItem const> items;
|
||||||
|
uint64_t pushTagCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns the exit jump type for the block to be inlined, if a particular jump to it should be inlined, otherwise nullopt.
|
||||||
|
std::optional<AssemblyItem::JumpType> shouldInline(size_t _tag, AssemblyItem const& _jump, InlinableBlock const& _block) const;
|
||||||
|
/// @returns true, if the full function at tag @a _tag with body @a _block that is referenced @a _pushTagCount times
|
||||||
|
/// should be inlined, false otherwise. @a _block should start at the first instruction after the function entry tag
|
||||||
|
/// up to and including the return jump.
|
||||||
|
bool shouldInlineFullFunctionBody(size_t _tag, ranges::span<AssemblyItem const> _block, uint64_t _pushTagCount) const;
|
||||||
|
/// @returns true, if the @a _items at @a _tag are a potential candidate for inlining.
|
||||||
|
bool isInlineCandidate(size_t _tag, ranges::span<AssemblyItem const> _items) const;
|
||||||
|
/// @returns a map from tags that can potentially be inlined to the inlinable item range behind that tag and the
|
||||||
|
/// number of times the tag in question was referenced.
|
||||||
|
std::map<size_t, InlinableBlock> determineInlinableBlocks(AssemblyItems const& _items) const;
|
||||||
|
|
||||||
|
AssemblyItems& m_items;
|
||||||
|
std::set<size_t> const& m_tagsReferencedFromOutside;
|
||||||
|
size_t const m_runs = 200;
|
||||||
|
bool const m_isCreation = false;
|
||||||
|
langutil::EVMVersion const m_evmVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -570,8 +570,9 @@ 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, m_evmVersion, 0};
|
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, m_evmVersion, 0};
|
||||||
asmSettings.isCreation = true;
|
asmSettings.isCreation = true;
|
||||||
|
asmSettings.runInliner = _settings.runInliner;
|
||||||
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
|
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
|
||||||
asmSettings.runPeephole = _settings.runPeephole;
|
asmSettings.runPeephole = _settings.runPeephole;
|
||||||
asmSettings.runDeduplicate = _settings.runDeduplicate;
|
asmSettings.runDeduplicate = _settings.runDeduplicate;
|
||||||
|
@ -1445,6 +1445,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
|||||||
Json::Value details{Json::objectValue};
|
Json::Value details{Json::objectValue};
|
||||||
|
|
||||||
details["orderLiterals"] = m_optimiserSettings.runOrderLiterals;
|
details["orderLiterals"] = m_optimiserSettings.runOrderLiterals;
|
||||||
|
details["inliner"] = m_optimiserSettings.runInliner;
|
||||||
details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover;
|
details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover;
|
||||||
details["peephole"] = m_optimiserSettings.runPeephole;
|
details["peephole"] = m_optimiserSettings.runPeephole;
|
||||||
details["deduplicate"] = m_optimiserSettings.runDeduplicate;
|
details["deduplicate"] = m_optimiserSettings.runDeduplicate;
|
||||||
|
@ -67,6 +67,7 @@ struct OptimiserSettings
|
|||||||
{
|
{
|
||||||
OptimiserSettings s;
|
OptimiserSettings s;
|
||||||
s.runOrderLiterals = true;
|
s.runOrderLiterals = true;
|
||||||
|
s.runInliner = true;
|
||||||
s.runJumpdestRemover = true;
|
s.runJumpdestRemover = true;
|
||||||
s.runPeephole = true;
|
s.runPeephole = true;
|
||||||
s.runDeduplicate = true;
|
s.runDeduplicate = true;
|
||||||
@ -87,6 +88,7 @@ struct OptimiserSettings
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
runOrderLiterals == _other.runOrderLiterals &&
|
runOrderLiterals == _other.runOrderLiterals &&
|
||||||
|
runInliner == _other.runInliner &&
|
||||||
runJumpdestRemover == _other.runJumpdestRemover &&
|
runJumpdestRemover == _other.runJumpdestRemover &&
|
||||||
runPeephole == _other.runPeephole &&
|
runPeephole == _other.runPeephole &&
|
||||||
runDeduplicate == _other.runDeduplicate &&
|
runDeduplicate == _other.runDeduplicate &&
|
||||||
@ -101,6 +103,8 @@ struct OptimiserSettings
|
|||||||
/// Move literals to the right of commutative binary operators during code generation.
|
/// Move literals to the right of commutative binary operators during code generation.
|
||||||
/// This helps exploiting associativity.
|
/// This helps exploiting associativity.
|
||||||
bool runOrderLiterals = false;
|
bool runOrderLiterals = false;
|
||||||
|
/// Inliner
|
||||||
|
bool runInliner = false;
|
||||||
/// Non-referenced jump destination remover.
|
/// Non-referenced jump destination remover.
|
||||||
bool runJumpdestRemover = false;
|
bool runJumpdestRemover = false;
|
||||||
/// Peephole optimizer
|
/// Peephole optimizer
|
||||||
|
@ -580,6 +580,8 @@ std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value
|
|||||||
|
|
||||||
if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
|
if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
|
||||||
return *error;
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "inliner", settings.runInliner))
|
||||||
|
return *error;
|
||||||
if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
|
if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
|
||||||
return *error;
|
return *error;
|
||||||
if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
|
if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
|
||||||
|
@ -66,12 +66,12 @@ sub_0: assembly {
|
|||||||
dup1
|
dup1
|
||||||
0x2e1fb2bc
|
0x2e1fb2bc
|
||||||
eq
|
eq
|
||||||
tag_3
|
tag_4
|
||||||
jumpi
|
jumpi
|
||||||
dup1
|
dup1
|
||||||
0x4753a67d
|
0x4753a67d
|
||||||
eq
|
eq
|
||||||
tag_3
|
tag_4
|
||||||
jumpi
|
jumpi
|
||||||
tag_2:
|
tag_2:
|
||||||
0x00
|
0x00
|
||||||
@ -79,11 +79,11 @@ sub_0: assembly {
|
|||||||
revert
|
revert
|
||||||
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||||
tag_3:
|
tag_3:
|
||||||
tag_6
|
|
||||||
tag_7
|
|
||||||
jump // in
|
|
||||||
tag_6:
|
|
||||||
stop
|
stop
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":108:133 function fun_() public {} */
|
||||||
|
tag_4:
|
||||||
|
jump(tag_3)
|
||||||
|
/* "optimizer_BlockDeDuplicator/input.sol":138:174 function f() public { true ? 1 : 3;} */
|
||||||
tag_7:
|
tag_7:
|
||||||
jump // out
|
jump // out
|
||||||
|
|
||||||
|
1
test/cmdlineTests/optimizer_inliner_add/args
Normal file
1
test/cmdlineTests/optimizer_inliner_add/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
17
test/cmdlineTests/optimizer_inliner_add/input.sol
Normal file
17
test/cmdlineTests/optimizer_inliner_add/input.sol
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
function unsafe_add(uint x, uint y) pure returns (uint)
|
||||||
|
{
|
||||||
|
unchecked {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
for(uint x = 0; x < 10; x = unsafe_add(x, unsafe_add(x, 1)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
test/cmdlineTests/optimizer_inliner_add/output
Normal file
86
test/cmdlineTests/optimizer_inliner_add/output
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_add/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_add/input.sol":165:305 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_add/input.sol":165:305 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x26121ff0
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_add/input.sol":182:303 function f() public pure {... */
|
||||||
|
tag_3:
|
||||||
|
tag_4
|
||||||
|
tag_5
|
||||||
|
jump // in
|
||||||
|
tag_4:
|
||||||
|
stop
|
||||||
|
tag_5:
|
||||||
|
/* "optimizer_inliner_add/input.sol":221:227 uint x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_add/input.sol":217:297 for(uint x = 0; x < 10; x = unsafe_add(x, unsafe_add(x, 1)))... */
|
||||||
|
tag_7:
|
||||||
|
/* "optimizer_inliner_add/input.sol":237:239 10 */
|
||||||
|
0x0a
|
||||||
|
/* "optimizer_inliner_add/input.sol":233:234 x */
|
||||||
|
dup2
|
||||||
|
/* "optimizer_inliner_add/input.sol":233:239 x < 10 */
|
||||||
|
lt
|
||||||
|
/* "optimizer_inliner_add/input.sol":217:297 for(uint x = 0; x < 10; x = unsafe_add(x, unsafe_add(x, 1)))... */
|
||||||
|
iszero
|
||||||
|
tag_8
|
||||||
|
jumpi
|
||||||
|
/* "optimizer_inliner_add/input.sol":149:154 x + y */
|
||||||
|
dup1
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_add/input.sol":273:274 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_add/input.sol":149:154 x + y */
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_add/input.sol":217:297 for(uint x = 0; x < 10; x = unsafe_add(x, unsafe_add(x, 1)))... */
|
||||||
|
jump(tag_7)
|
||||||
|
tag_8:
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_add/input.sol":182:303 function f() public pure {... */
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint x;
|
||||||
|
constructor() { x = a(); }
|
||||||
|
function a() public pure returns (uint) { return f(); } // this should be inlined
|
||||||
|
function f() internal pure returns (uint) { return 6; }
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_call_from_constructor/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":60:263 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":89:115 constructor() { x = a(); } */
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":257:258 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":105:106 x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":105:112 x = a() */
|
||||||
|
sstore
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":60:263 contract C {... */
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":60:263 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x0dbe671f
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":120:175 function a() public pure returns (uint) { return f(); } */
|
||||||
|
tag_3:
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":257:258 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":120:175 function a() public pure returns (uint) { return f(); } */
|
||||||
|
mload(0x40)
|
||||||
|
/* "#utility.yul":160:185 */
|
||||||
|
swap1
|
||||||
|
dup2
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":148:150 */
|
||||||
|
0x20
|
||||||
|
/* "#utility.yul":133:151 */
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_call_from_constructor/input.sol":120:175 function a() public pure returns (uint) { return f(); } */
|
||||||
|
mload(0x40)
|
||||||
|
dup1
|
||||||
|
swap2
|
||||||
|
sub
|
||||||
|
swap1
|
||||||
|
return
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
@ -0,0 +1,11 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function() internal view returns (uint) x;
|
||||||
|
|
||||||
|
function g() public { x = f; }
|
||||||
|
function a() public pure returns (uint) { return f(); } // this should be inlined
|
||||||
|
function h() public view returns (uint) { return x() + 1; }
|
||||||
|
function f() internal pure returns (uint) { return 6; }
|
||||||
|
}
|
198
test/cmdlineTests/optimizer_inliner_dynamic_reference/output
Normal file
198
test/cmdlineTests/optimizer_inliner_dynamic_reference/output
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_dynamic_reference/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":60:367 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":60:367 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x0dbe671f
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
dup1
|
||||||
|
0xb8c9d365
|
||||||
|
eq
|
||||||
|
tag_4
|
||||||
|
jumpi
|
||||||
|
dup1
|
||||||
|
0xe2179b8e
|
||||||
|
eq
|
||||||
|
tag_5
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||||
|
tag_3:
|
||||||
|
tag_6
|
||||||
|
tag_7
|
||||||
|
jump // in
|
||||||
|
tag_6:
|
||||||
|
mload(0x40)
|
||||||
|
/* "#utility.yul":160:185 */
|
||||||
|
swap1
|
||||||
|
dup2
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":148:150 */
|
||||||
|
0x20
|
||||||
|
/* "#utility.yul":133:151 */
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||||
|
mload(0x40)
|
||||||
|
dup1
|
||||||
|
swap2
|
||||||
|
sub
|
||||||
|
swap1
|
||||||
|
return
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":246:305 function h() public view returns (uint) { return x() + 1; } */
|
||||||
|
tag_4:
|
||||||
|
tag_6
|
||||||
|
tag_11
|
||||||
|
jump // in
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":125:155 function g() public { x = f; } */
|
||||||
|
tag_5:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":147:148 x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":147:152 x = f */
|
||||||
|
dup1
|
||||||
|
sload
|
||||||
|
not(0xffffffffffffffff)
|
||||||
|
and
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":151:152 f */
|
||||||
|
tag_17
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":147:152 x = f */
|
||||||
|
or
|
||||||
|
swap1
|
||||||
|
sstore
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":125:155 function g() public { x = f; } */
|
||||||
|
stop
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||||
|
tag_7:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":194:198 uint */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":361:362 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":209:212 f() */
|
||||||
|
tag_16:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":202:212 return f() */
|
||||||
|
swap1
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":160:215 function a() public pure returns (uint) { return f(); } */
|
||||||
|
swap1
|
||||||
|
jump // out
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":246:305 function h() public view returns (uint) { return x() + 1; } */
|
||||||
|
tag_11:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":280:284 uint */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:296 x */
|
||||||
|
dup1
|
||||||
|
sload
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:298 x() */
|
||||||
|
tag_19
|
||||||
|
swap1
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:296 x */
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_20
|
||||||
|
mul
|
||||||
|
or
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:298 x() */
|
||||||
|
0xffffffff
|
||||||
|
and
|
||||||
|
jump // in
|
||||||
|
tag_19:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:302 x() + 1 */
|
||||||
|
tag_16
|
||||||
|
swap1
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":301:302 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":295:302 x() + 1 */
|
||||||
|
tag_22
|
||||||
|
jump // in
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":310:365 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
tag_17:
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":361:362 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_dynamic_reference/input.sol":310:365 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
swap1
|
||||||
|
jump // out
|
||||||
|
tag_20:
|
||||||
|
mstore(0x00, shl(0xe0, 0x4e487b71))
|
||||||
|
mstore(0x04, 0x51)
|
||||||
|
revert(0x00, 0x24)
|
||||||
|
/* "#utility.yul":196:425 */
|
||||||
|
tag_22:
|
||||||
|
0x00
|
||||||
|
/* "#utility.yul":267:268 */
|
||||||
|
dup3
|
||||||
|
/* "#utility.yul":263:269 */
|
||||||
|
not
|
||||||
|
/* "#utility.yul":260:261 */
|
||||||
|
dup3
|
||||||
|
/* "#utility.yul":257:270 */
|
||||||
|
gt
|
||||||
|
/* "#utility.yul":254:256 */
|
||||||
|
iszero
|
||||||
|
tag_28
|
||||||
|
jumpi
|
||||||
|
shl(0xe0, 0x4e487b71)
|
||||||
|
/* "#utility.yul":293:326 */
|
||||||
|
dup2
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":349:353 */
|
||||||
|
0x11
|
||||||
|
/* "#utility.yul":346:347 */
|
||||||
|
0x04
|
||||||
|
/* "#utility.yul":339:354 */
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":379:383 */
|
||||||
|
0x24
|
||||||
|
/* "#utility.yul":300:303 */
|
||||||
|
dup2
|
||||||
|
/* "#utility.yul":367:384 */
|
||||||
|
revert
|
||||||
|
/* "#utility.yul":254:256 */
|
||||||
|
tag_28:
|
||||||
|
pop
|
||||||
|
/* "#utility.yul":410:419 */
|
||||||
|
add
|
||||||
|
swap1
|
||||||
|
/* "#utility.yul":244:425 */
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
@ -0,0 +1,11 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
constructor() { x = f; }
|
||||||
|
function() internal view returns (uint) x;
|
||||||
|
|
||||||
|
function a() public pure returns (uint) { return f(); } // this should be inlined
|
||||||
|
function h() public view returns (uint) { return x() + 1; }
|
||||||
|
function f() internal pure returns (uint) { return 6; }
|
||||||
|
}
|
@ -0,0 +1,203 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_dynamic_reference_constructor/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":60:361 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":77:101 constructor() { x = f; } */
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":93:94 x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":93:98 x = f */
|
||||||
|
dup1
|
||||||
|
sload
|
||||||
|
not(sub(shl(0x40, 0x01), 0x01))
|
||||||
|
and
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":97:98 f */
|
||||||
|
or(tag_0_12, shl(0x20, tag_4))
|
||||||
|
sub(shl(0x40, 0x01), 0x01)
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":93:98 x = f */
|
||||||
|
and
|
||||||
|
or
|
||||||
|
swap1
|
||||||
|
sstore
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":60:361 contract C {... */
|
||||||
|
jump(tag_5)
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
tag_4:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
swap1
|
||||||
|
jump // out
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":60:361 contract C {... */
|
||||||
|
tag_5:
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":60:361 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x0dbe671f
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
dup1
|
||||||
|
0xb8c9d365
|
||||||
|
eq
|
||||||
|
tag_4
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||||
|
tag_3:
|
||||||
|
tag_5
|
||||||
|
tag_6
|
||||||
|
jump // in
|
||||||
|
tag_5:
|
||||||
|
mload(0x40)
|
||||||
|
/* "#utility.yul":160:185 */
|
||||||
|
swap1
|
||||||
|
dup2
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":148:150 */
|
||||||
|
0x20
|
||||||
|
/* "#utility.yul":133:151 */
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||||
|
mload(0x40)
|
||||||
|
dup1
|
||||||
|
swap2
|
||||||
|
sub
|
||||||
|
swap1
|
||||||
|
return
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":240:299 function h() public view returns (uint) { return x() + 1; } */
|
||||||
|
tag_4:
|
||||||
|
tag_5
|
||||||
|
tag_10
|
||||||
|
jump // in
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||||
|
tag_6:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":188:192 uint */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":203:206 f() */
|
||||||
|
tag_14:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":196:206 return f() */
|
||||||
|
swap1
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":154:209 function a() public pure returns (uint) { return f(); } */
|
||||||
|
swap1
|
||||||
|
jump // out
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":240:299 function h() public view returns (uint) { return x() + 1; } */
|
||||||
|
tag_10:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":274:278 uint */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:290 x */
|
||||||
|
dup1
|
||||||
|
sload
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:292 x() */
|
||||||
|
tag_16
|
||||||
|
swap1
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:290 x */
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_17
|
||||||
|
mul
|
||||||
|
or
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:292 x() */
|
||||||
|
0xffffffff
|
||||||
|
and
|
||||||
|
jump // in
|
||||||
|
tag_16:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:296 x() + 1 */
|
||||||
|
tag_14
|
||||||
|
swap1
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":295:296 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":289:296 x() + 1 */
|
||||||
|
tag_19
|
||||||
|
jump // in
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
tag_12:
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":355:356 6 */
|
||||||
|
0x06
|
||||||
|
/* "optimizer_inliner_dynamic_reference_constructor/input.sol":304:359 function f() internal pure returns (uint) { return 6; } */
|
||||||
|
swap1
|
||||||
|
jump // out
|
||||||
|
tag_17:
|
||||||
|
mstore(0x00, shl(0xe0, 0x4e487b71))
|
||||||
|
mstore(0x04, 0x51)
|
||||||
|
revert(0x00, 0x24)
|
||||||
|
/* "#utility.yul":196:425 */
|
||||||
|
tag_19:
|
||||||
|
0x00
|
||||||
|
/* "#utility.yul":267:268 */
|
||||||
|
dup3
|
||||||
|
/* "#utility.yul":263:269 */
|
||||||
|
not
|
||||||
|
/* "#utility.yul":260:261 */
|
||||||
|
dup3
|
||||||
|
/* "#utility.yul":257:270 */
|
||||||
|
gt
|
||||||
|
/* "#utility.yul":254:256 */
|
||||||
|
iszero
|
||||||
|
tag_24
|
||||||
|
jumpi
|
||||||
|
shl(0xe0, 0x4e487b71)
|
||||||
|
/* "#utility.yul":293:326 */
|
||||||
|
dup2
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":349:353 */
|
||||||
|
0x11
|
||||||
|
/* "#utility.yul":346:347 */
|
||||||
|
0x04
|
||||||
|
/* "#utility.yul":339:354 */
|
||||||
|
mstore
|
||||||
|
/* "#utility.yul":379:383 */
|
||||||
|
0x24
|
||||||
|
/* "#utility.yul":300:303 */
|
||||||
|
dup2
|
||||||
|
/* "#utility.yul":367:384 */
|
||||||
|
revert
|
||||||
|
/* "#utility.yul":254:256 */
|
||||||
|
tag_24:
|
||||||
|
pop
|
||||||
|
/* "#utility.yul":410:419 */
|
||||||
|
add
|
||||||
|
swap1
|
||||||
|
/* "#utility.yul":244:425 */
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
1
test/cmdlineTests/optimizer_inliner_inc/args
Normal file
1
test/cmdlineTests/optimizer_inliner_inc/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
17
test/cmdlineTests/optimizer_inliner_inc/input.sol
Normal file
17
test/cmdlineTests/optimizer_inliner_inc/input.sol
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
function unsafe_inc(uint x) pure returns (uint)
|
||||||
|
{
|
||||||
|
unchecked {
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
for(uint x = 0; x < 10; x = unsafe_inc(x))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
test/cmdlineTests/optimizer_inliner_inc/output
Normal file
83
test/cmdlineTests/optimizer_inliner_inc/output
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_inc/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_inc/input.sol":157:279 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_inc/input.sol":157:279 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x26121ff0
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_inc/input.sol":174:277 function f() public pure {... */
|
||||||
|
tag_3:
|
||||||
|
tag_4
|
||||||
|
tag_5
|
||||||
|
jump // in
|
||||||
|
tag_4:
|
||||||
|
stop
|
||||||
|
tag_5:
|
||||||
|
/* "optimizer_inliner_inc/input.sol":213:219 uint x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_inc/input.sol":209:271 for(uint x = 0; x < 10; x = unsafe_inc(x))... */
|
||||||
|
tag_7:
|
||||||
|
/* "optimizer_inliner_inc/input.sol":229:231 10 */
|
||||||
|
0x0a
|
||||||
|
/* "optimizer_inliner_inc/input.sol":225:226 x */
|
||||||
|
dup2
|
||||||
|
/* "optimizer_inliner_inc/input.sol":225:231 x < 10 */
|
||||||
|
lt
|
||||||
|
/* "optimizer_inliner_inc/input.sol":209:271 for(uint x = 0; x < 10; x = unsafe_inc(x))... */
|
||||||
|
iszero
|
||||||
|
tag_8
|
||||||
|
jumpi
|
||||||
|
/* "optimizer_inliner_inc/input.sol":145:146 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_inc/input.sol":141:146 x + 1 */
|
||||||
|
add
|
||||||
|
/* "optimizer_inliner_inc/input.sol":209:271 for(uint x = 0; x < 10; x = unsafe_inc(x))... */
|
||||||
|
jump(tag_7)
|
||||||
|
tag_8:
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_inc/input.sol":174:277 function f() public pure {... */
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
1
test/cmdlineTests/optimizer_inliner_multireturn/args
Normal file
1
test/cmdlineTests/optimizer_inliner_multireturn/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--optimize --asm --metadata-hash none
|
17
test/cmdlineTests/optimizer_inliner_multireturn/input.sol
Normal file
17
test/cmdlineTests/optimizer_inliner_multireturn/input.sol
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
|
||||||
|
function test(uint x) pure returns (uint, uint)
|
||||||
|
{
|
||||||
|
unchecked {
|
||||||
|
return (x + 1, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
for((uint x, uint y) = (0, 1); x < 10; (x, y) = test(x))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
89
test/cmdlineTests/optimizer_inliner_multireturn/output
Normal file
89
test/cmdlineTests/optimizer_inliner_multireturn/output
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
======= optimizer_inliner_multireturn/input.sol:C =======
|
||||||
|
EVM assembly:
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":162:298 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
dataSize(sub_0)
|
||||||
|
dup1
|
||||||
|
dataOffset(sub_0)
|
||||||
|
0x00
|
||||||
|
codecopy
|
||||||
|
0x00
|
||||||
|
return
|
||||||
|
stop
|
||||||
|
|
||||||
|
sub_0: assembly {
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":162:298 contract C {... */
|
||||||
|
mstore(0x40, 0x80)
|
||||||
|
callvalue
|
||||||
|
dup1
|
||||||
|
iszero
|
||||||
|
tag_1
|
||||||
|
jumpi
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
tag_1:
|
||||||
|
pop
|
||||||
|
jumpi(tag_2, lt(calldatasize, 0x04))
|
||||||
|
shr(0xe0, calldataload(0x00))
|
||||||
|
dup1
|
||||||
|
0x26121ff0
|
||||||
|
eq
|
||||||
|
tag_3
|
||||||
|
jumpi
|
||||||
|
tag_2:
|
||||||
|
0x00
|
||||||
|
dup1
|
||||||
|
revert
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":179:296 function f() public pure {... */
|
||||||
|
tag_3:
|
||||||
|
tag_4
|
||||||
|
tag_5
|
||||||
|
jump // in
|
||||||
|
tag_4:
|
||||||
|
stop
|
||||||
|
tag_5:
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":219:225 uint x */
|
||||||
|
0x00
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":241:242 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":214:290 for((uint x, uint y) = (0, 1); x < 10; (x, y) = test(x))... */
|
||||||
|
tag_7:
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":249:251 10 */
|
||||||
|
0x0a
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":245:246 x */
|
||||||
|
dup3
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":245:251 x < 10 */
|
||||||
|
lt
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":214:290 for((uint x, uint y) = (0, 1); x < 10; (x, y) = test(x))... */
|
||||||
|
iszero
|
||||||
|
tag_8
|
||||||
|
jumpi
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":146:147 1 */
|
||||||
|
0x01
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":142:147 x + 1 */
|
||||||
|
dup2
|
||||||
|
add
|
||||||
|
swap1
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":214:290 for((uint x, uint y) = (0, 1); x < 10; (x, y) = test(x))... */
|
||||||
|
jump(tag_7)
|
||||||
|
tag_8:
|
||||||
|
pop
|
||||||
|
pop
|
||||||
|
/* "optimizer_inliner_multireturn/input.sol":179:296 function f() public pure {... */
|
||||||
|
jump // out
|
||||||
|
|
||||||
|
auxdata: <AUXDATA REMOVED>
|
||||||
|
}
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/PeepholeOptimiser.h>
|
#include <libevmasm/PeepholeOptimiser.h>
|
||||||
|
#include <libevmasm/Inliner.h>
|
||||||
#include <libevmasm/JumpdestRemover.h>
|
#include <libevmasm/JumpdestRemover.h>
|
||||||
#include <libevmasm/ControlFlowGraph.h>
|
#include <libevmasm/ControlFlowGraph.h>
|
||||||
#include <libevmasm/BlockDeduplicator.h>
|
#include <libevmasm/BlockDeduplicator.h>
|
||||||
@ -1233,7 +1234,17 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
|
|||||||
main.append(t1.toSubAssemblyTag(subId));
|
main.append(t1.toSubAssemblyTag(subId));
|
||||||
main.append(u256(8));
|
main.append(u256(8));
|
||||||
|
|
||||||
main.optimise(true, solidity::test::CommonOptions::get().evmVersion(), false, 200);
|
Assembly::OptimiserSettings settings;
|
||||||
|
settings.isCreation = false;
|
||||||
|
settings.runInliner = false;
|
||||||
|
settings.runJumpdestRemover = true;
|
||||||
|
settings.runPeephole = true;
|
||||||
|
settings.runDeduplicate = true;
|
||||||
|
settings.runCSE = true;
|
||||||
|
settings.runConstantOptimiser = true;
|
||||||
|
settings.evmVersion = solidity::test::CommonOptions::get().evmVersion();
|
||||||
|
settings.expectedExecutionsPerDeployment = 200;
|
||||||
|
main.optimise(settings);
|
||||||
|
|
||||||
AssemblyItems expectationMain{
|
AssemblyItems expectationMain{
|
||||||
AssemblyItem(PushSubSize, 0),
|
AssemblyItem(PushSubSize, 0),
|
||||||
@ -1467,6 +1478,165 @@ BOOST_AUTO_TEST_CASE(cse_replace_too_large_shift)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner)
|
||||||
|
{
|
||||||
|
AssemblyItem jumpInto{Instruction::JUMP};
|
||||||
|
jumpInto.setJumpType(AssemblyItem::JumpType::IntoFunction);
|
||||||
|
AssemblyItem jumpOutOf{Instruction::JUMP};
|
||||||
|
jumpOutOf.setJumpType(AssemblyItem::JumpType::OutOfFunction);
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
AssemblyItem(PushTag, 2),
|
||||||
|
jumpInto,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::SWAP1,
|
||||||
|
jumpOutOf,
|
||||||
|
};
|
||||||
|
AssemblyItems expectation{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::SWAP1,
|
||||||
|
Instruction::JUMP,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::SWAP1,
|
||||||
|
jumpOutOf,
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
expectation.begin(), expectation.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner_no_inline_type)
|
||||||
|
{
|
||||||
|
// Will not inline due to jump types.
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
AssemblyItem(PushTag, 2),
|
||||||
|
Instruction::JUMP,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::SWAP1,
|
||||||
|
Instruction::JUMP,
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
items.begin(), items.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner_no_inline)
|
||||||
|
{
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
Instruction::JUMP,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::JUMPI,
|
||||||
|
Instruction::JUMP,
|
||||||
|
};
|
||||||
|
AssemblyItems expectation{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
Instruction::JUMP,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::CALLVALUE,
|
||||||
|
Instruction::JUMPI,
|
||||||
|
Instruction::JUMP,
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
expectation.begin(), expectation.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner_single_jump)
|
||||||
|
{
|
||||||
|
AssemblyItem jumpInto{Instruction::JUMP};
|
||||||
|
jumpInto.setJumpType(AssemblyItem::JumpType::IntoFunction);
|
||||||
|
AssemblyItem jumpOutOf{Instruction::JUMP};
|
||||||
|
jumpOutOf.setJumpType(AssemblyItem::JumpType::OutOfFunction);
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
AssemblyItem(PushTag, 2),
|
||||||
|
jumpInto,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
jumpOutOf,
|
||||||
|
};
|
||||||
|
AssemblyItems expectation{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
Instruction::JUMP,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
jumpOutOf,
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
expectation.begin(), expectation.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner_end_of_bytecode)
|
||||||
|
{
|
||||||
|
AssemblyItem jumpInto{Instruction::JUMP};
|
||||||
|
jumpInto.setJumpType(AssemblyItem::JumpType::IntoFunction);
|
||||||
|
// Cannot inline, since the block at Tag_2 does not end in a jump.
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
AssemblyItem(PushTag, 2),
|
||||||
|
jumpInto,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
items.begin(), items.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inliner_cse_break)
|
||||||
|
{
|
||||||
|
AssemblyItem jumpInto{Instruction::JUMP};
|
||||||
|
jumpInto.setJumpType(AssemblyItem::JumpType::IntoFunction);
|
||||||
|
AssemblyItem jumpOutOf{Instruction::JUMP};
|
||||||
|
jumpOutOf.setJumpType(AssemblyItem::JumpType::OutOfFunction);
|
||||||
|
// Could be inlined, but we only consider non-CSE-breaking blocks ending in JUMP so far.
|
||||||
|
AssemblyItems items{
|
||||||
|
AssemblyItem(PushTag, 1),
|
||||||
|
AssemblyItem(PushTag, 2),
|
||||||
|
jumpInto,
|
||||||
|
AssemblyItem(Tag, 1),
|
||||||
|
Instruction::STOP,
|
||||||
|
AssemblyItem(Tag, 2),
|
||||||
|
Instruction::STOP, // CSE breaking instruction
|
||||||
|
jumpOutOf
|
||||||
|
};
|
||||||
|
Inliner{items, {}, 200, false, {}}.optimise();
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
items.begin(), items.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
} // end namespaces
|
} // end namespaces
|
||||||
|
@ -174,9 +174,9 @@ BOOST_AUTO_TEST_CASE(location_test)
|
|||||||
if (solidity::test::CommonOptions::get().optimize)
|
if (solidity::test::CommonOptions::get().optimize)
|
||||||
locations =
|
locations =
|
||||||
vector<SourceLocation>(31, SourceLocation{23, 103, sourceCode}) +
|
vector<SourceLocation>(31, SourceLocation{23, 103, sourceCode}) +
|
||||||
vector<SourceLocation>(21, SourceLocation{41, 100, sourceCode}) +
|
vector<SourceLocation>(1, SourceLocation{41, 100, sourceCode}) +
|
||||||
vector<SourceLocation>(1, SourceLocation{93, 95, sourceCode}) +
|
vector<SourceLocation>(1, SourceLocation{93, 95, sourceCode}) +
|
||||||
vector<SourceLocation>(2, SourceLocation{41, 100, sourceCode});
|
vector<SourceLocation>(15, SourceLocation{41, 100, sourceCode});
|
||||||
else
|
else
|
||||||
locations =
|
locations =
|
||||||
vector<SourceLocation>(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) +
|
vector<SourceLocation>(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) +
|
||||||
@ -209,7 +209,10 @@ BOOST_AUTO_TEST_CASE(jump_type)
|
|||||||
if (item.getJumpType() != AssemblyItem::JumpType::Ordinary)
|
if (item.getJumpType() != AssemblyItem::JumpType::Ordinary)
|
||||||
jumpTypes += item.getJumpTypeAsString() + "\n";
|
jumpTypes += item.getJumpTypeAsString() + "\n";
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n");
|
if (solidity::test::CommonOptions::get().optimize)
|
||||||
|
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n");
|
||||||
|
else
|
||||||
|
BOOST_CHECK_EQUAL(jumpTypes, "[in]\n[out]\n[in]\n[out]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(string_storage)
|
|||||||
if (CommonOptions::get().useABIEncoderV1)
|
if (CommonOptions::get().useABIEncoderV1)
|
||||||
CHECK_DEPLOY_GAS(133045, 129731, evmVersion);
|
CHECK_DEPLOY_GAS(133045, 129731, evmVersion);
|
||||||
else
|
else
|
||||||
CHECK_DEPLOY_GAS(155553, 135201, evmVersion);
|
CHECK_DEPLOY_GAS(155553, 132103, evmVersion);
|
||||||
}
|
}
|
||||||
// This is only correct on >=Constantinople.
|
// This is only correct on >=Constantinople.
|
||||||
else if (!CommonOptions::get().useABIEncoderV1)
|
else if (!CommonOptions::get().useABIEncoderV1)
|
||||||
@ -110,9 +110,9 @@ BOOST_AUTO_TEST_CASE(string_storage)
|
|||||||
{
|
{
|
||||||
// Costs with 0 are cases which cannot be triggered in tests.
|
// Costs with 0 are cases which cannot be triggered in tests.
|
||||||
if (evmVersion < EVMVersion::istanbul())
|
if (evmVersion < EVMVersion::istanbul())
|
||||||
CHECK_DEPLOY_GAS(0, 122869, evmVersion);
|
CHECK_DEPLOY_GAS(0, 120189, evmVersion);
|
||||||
else
|
else
|
||||||
CHECK_DEPLOY_GAS(0, 110701, evmVersion);
|
CHECK_DEPLOY_GAS(0, 108541, evmVersion);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -131,16 +131,16 @@ BOOST_AUTO_TEST_CASE(string_storage)
|
|||||||
{
|
{
|
||||||
callContractFunction("f()");
|
callContractFunction("f()");
|
||||||
if (evmVersion == EVMVersion::byzantium())
|
if (evmVersion == EVMVersion::byzantium())
|
||||||
CHECK_GAS(21741, 21555, 20);
|
CHECK_GAS(21741, 21522, 20);
|
||||||
// This is only correct on >=Constantinople.
|
// This is only correct on >=Constantinople.
|
||||||
else if (!CommonOptions::get().useABIEncoderV1)
|
else if (!CommonOptions::get().useABIEncoderV1)
|
||||||
{
|
{
|
||||||
if (CommonOptions::get().optimize)
|
if (CommonOptions::get().optimize)
|
||||||
{
|
{
|
||||||
if (evmVersion < EVMVersion::istanbul())
|
if (evmVersion < EVMVersion::istanbul())
|
||||||
CHECK_GAS(0, 21567, 20);
|
CHECK_GAS(0, 21526, 20);
|
||||||
else
|
else
|
||||||
CHECK_GAS(0, 21351, 20);
|
CHECK_GAS(0, 21318, 20);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -170,8 +170,8 @@ BOOST_AUTO_TEST_CASE(branches)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
testCreationTimeGas(sourceCode);
|
testCreationTimeGas(sourceCode, 1);
|
||||||
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
|
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_calls)
|
BOOST_AUTO_TEST_CASE(function_calls)
|
||||||
|
@ -1230,7 +1230,7 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
|
|||||||
);
|
);
|
||||||
BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
|
BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
|
||||||
BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps);
|
BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps);
|
||||||
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
|
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 9);
|
||||||
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,13 +17,13 @@ contract C {
|
|||||||
// optimize-yul: true
|
// optimize-yul: true
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 588800
|
// codeDepositCost: 583400
|
||||||
// executionCost: 619
|
// executionCost: 619
|
||||||
// totalCost: 589419
|
// totalCost: 584019
|
||||||
// external:
|
// external:
|
||||||
// a(): 1029
|
// a(): 985
|
||||||
// b(uint256): 2084
|
// b(uint256): 2052
|
||||||
// f1(uint256): 351
|
// f1(uint256): 307
|
||||||
// f2(uint256[],string[],uint16,address): infinite
|
// f2(uint256[],string[],uint16,address): infinite
|
||||||
// f3(uint16[],string[],uint16,address): infinite
|
// f3(uint16[],string[],uint16,address): infinite
|
||||||
// f4(uint32[],string[12],bytes[2][],address): infinite
|
// f4(uint32[],string[12],bytes[2][],address): infinite
|
||||||
|
@ -27,29 +27,29 @@ contract Large {
|
|||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 270600
|
// codeDepositCost: 267000
|
||||||
// executionCost: 312
|
// executionCost: 306
|
||||||
// totalCost: 270912
|
// totalCost: 267306
|
||||||
// external:
|
// external:
|
||||||
// a(): 1028
|
// a(): 983
|
||||||
// b(uint256): 2370
|
// b(uint256): 2337
|
||||||
// f0(uint256): 399
|
// f0(uint256): 366
|
||||||
// f1(uint256): 41539
|
// f1(uint256): 41506
|
||||||
// f2(uint256): 21605
|
// f2(uint256): 21572
|
||||||
// f3(uint256): 21693
|
// f3(uint256): 21660
|
||||||
// f4(uint256): 21671
|
// f4(uint256): 21638
|
||||||
// f5(uint256): 21649
|
// f5(uint256): 21616
|
||||||
// f6(uint256): 21561
|
// f6(uint256): 21528
|
||||||
// f7(uint256): 21341
|
// f7(uint256): 21308
|
||||||
// f8(uint256): 21473
|
// f8(uint256): 21440
|
||||||
// f9(uint256): 21495
|
// f9(uint256): 21462
|
||||||
// g0(uint256): 639
|
// g0(uint256): 606
|
||||||
// g1(uint256): 41251
|
// g1(uint256): 41218
|
||||||
// g2(uint256): 21339
|
// g2(uint256): 21306
|
||||||
// g3(uint256): 21427
|
// g3(uint256): 21394
|
||||||
// g4(uint256): 21405
|
// g4(uint256): 21372
|
||||||
// g5(uint256): 21493
|
// g5(uint256): 21460
|
||||||
// g6(uint256): 21273
|
// g6(uint256): 21240
|
||||||
// g7(uint256): 21383
|
// g7(uint256): 21350
|
||||||
// g8(uint256): 21361
|
// g8(uint256): 21328
|
||||||
// g9(uint256): 21207
|
// g9(uint256): 21174
|
||||||
|
@ -14,16 +14,16 @@ contract Medium {
|
|||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 161000
|
// codeDepositCost: 157400
|
||||||
// executionCost: 208
|
// executionCost: 202
|
||||||
// totalCost: 161208
|
// totalCost: 157602
|
||||||
// external:
|
// external:
|
||||||
// a(): 1028
|
// a(): 983
|
||||||
// b(uint256): 2128
|
// b(uint256): 2095
|
||||||
// f1(uint256): 41319
|
// f1(uint256): 41286
|
||||||
// f2(uint256): 21363
|
// f2(uint256): 21330
|
||||||
// f3(uint256): 21407
|
// f3(uint256): 21374
|
||||||
// g0(uint256): 397
|
// g0(uint256): 364
|
||||||
// g7(uint256): 21273
|
// g7(uint256): 21240
|
||||||
// g8(uint256): 21251
|
// g8(uint256): 21218
|
||||||
// g9(uint256): 21207
|
// g9(uint256): 21174
|
||||||
|
@ -9,11 +9,11 @@ contract Small {
|
|||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 76200
|
// codeDepositCost: 72600
|
||||||
// executionCost: 123
|
// executionCost: 123
|
||||||
// totalCost: 76323
|
// totalCost: 72723
|
||||||
// external:
|
// external:
|
||||||
// fallback: 118
|
// fallback: 118
|
||||||
// a(): 1006
|
// a(): 961
|
||||||
// b(uint256): 2018
|
// b(uint256): 1985
|
||||||
// f1(uint256): 41253
|
// f1(uint256): 41220
|
||||||
|
@ -19,11 +19,11 @@ contract C {
|
|||||||
// optimize-yul: true
|
// optimize-yul: true
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 53200
|
// codeDepositCost: 47800
|
||||||
// executionCost: 105
|
// executionCost: 99
|
||||||
// totalCost: 53305
|
// totalCost: 47899
|
||||||
// external:
|
// external:
|
||||||
// exp_neg_one(uint256): 1962
|
// exp_neg_one(uint256): 1917
|
||||||
// exp_one(uint256): 1915
|
// exp_one(uint256): 1870
|
||||||
// exp_two(uint256): 1893
|
// exp_two(uint256): 1848
|
||||||
// exp_zero(uint256): 1937
|
// exp_zero(uint256): 1892
|
||||||
|
Loading…
Reference in New Issue
Block a user