2015-06-01 10:32:59 +00:00
|
|
|
/*
|
2016-11-18 23:13:20 +00:00
|
|
|
This file is part of solidity.
|
2015-06-01 10:32:59 +00:00
|
|
|
|
2016-11-18 23:13:20 +00:00
|
|
|
solidity is free software: you can redistribute it and/or modify
|
2015-06-01 10:32:59 +00:00
|
|
|
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.
|
|
|
|
|
2016-11-18 23:13:20 +00:00
|
|
|
solidity is distributed in the hope that it will be useful,
|
2015-06-01 10:32:59 +00:00
|
|
|
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
|
2016-11-18 23:13:20 +00:00
|
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
2015-06-01 10:32:59 +00:00
|
|
|
*/
|
|
|
|
/** @file ConstantOptimiser.cpp
|
|
|
|
* @author Christian <c@ethdev.com>
|
|
|
|
* @date 2015
|
|
|
|
*/
|
|
|
|
|
2016-03-22 12:05:27 +00:00
|
|
|
#include <libevmasm/ConstantOptimiser.h>
|
|
|
|
#include <libevmasm/Assembly.h>
|
|
|
|
#include <libevmasm/GasMeter.h>
|
2015-06-01 10:32:59 +00:00
|
|
|
using namespace std;
|
|
|
|
using namespace dev;
|
|
|
|
using namespace dev::eth;
|
|
|
|
|
|
|
|
unsigned ConstantOptimisationMethod::optimiseConstants(
|
|
|
|
bool _isCreation,
|
|
|
|
size_t _runs,
|
|
|
|
Assembly& _assembly,
|
|
|
|
AssemblyItems& _items
|
|
|
|
)
|
|
|
|
{
|
|
|
|
unsigned optimisations = 0;
|
|
|
|
map<AssemblyItem, size_t> pushes;
|
|
|
|
for (AssemblyItem const& item: _items)
|
|
|
|
if (item.type() == Push)
|
|
|
|
pushes[item]++;
|
2017-01-06 16:05:27 +00:00
|
|
|
map<u256, AssemblyItems> pendingReplacements;
|
2015-06-01 10:32:59 +00:00
|
|
|
for (auto it: pushes)
|
|
|
|
{
|
|
|
|
AssemblyItem const& item = it.first;
|
|
|
|
if (item.data() < 0x100)
|
|
|
|
continue;
|
|
|
|
Params params;
|
|
|
|
params.multiplicity = it.second;
|
|
|
|
params.isCreation = _isCreation;
|
|
|
|
params.runs = _runs;
|
|
|
|
LiteralMethod lit(params, item.data());
|
|
|
|
bigint literalGas = lit.gasNeeded();
|
|
|
|
CodeCopyMethod copy(params, item.data());
|
|
|
|
bigint copyGas = copy.gasNeeded();
|
|
|
|
ComputeMethod compute(params, item.data());
|
|
|
|
bigint computeGas = compute.gasNeeded();
|
2017-01-06 16:05:27 +00:00
|
|
|
AssemblyItems replacement;
|
2015-06-01 10:32:59 +00:00
|
|
|
if (copyGas < literalGas && copyGas < computeGas)
|
|
|
|
{
|
2017-01-06 16:05:27 +00:00
|
|
|
replacement = copy.execute(_assembly);
|
2015-06-01 10:32:59 +00:00
|
|
|
optimisations++;
|
|
|
|
}
|
2017-01-12 16:52:23 +00:00
|
|
|
else if (computeGas < literalGas && computeGas <= copyGas)
|
2015-06-01 10:32:59 +00:00
|
|
|
{
|
2017-01-06 16:05:27 +00:00
|
|
|
replacement = compute.execute(_assembly);
|
2015-06-01 10:32:59 +00:00
|
|
|
optimisations++;
|
|
|
|
}
|
2017-01-06 16:05:27 +00:00
|
|
|
if (!replacement.empty())
|
|
|
|
pendingReplacements[item.data()] = replacement;
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
2017-01-06 16:05:27 +00:00
|
|
|
if (!pendingReplacements.empty())
|
|
|
|
replaceConstants(_items, pendingReplacements);
|
2015-06-01 10:32:59 +00:00
|
|
|
return optimisations;
|
|
|
|
}
|
|
|
|
|
|
|
|
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
|
|
|
|
{
|
|
|
|
bigint gas = 0;
|
|
|
|
for (AssemblyItem const& item: _items)
|
|
|
|
if (item.type() == Push)
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += GasMeter::runGas(Instruction::PUSH1);
|
2015-06-01 10:32:59 +00:00
|
|
|
else if (item.type() == Operation)
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += GasMeter::runGas(item.instruction());
|
2015-06-01 10:32:59 +00:00
|
|
|
return gas;
|
|
|
|
}
|
|
|
|
|
|
|
|
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
|
|
|
|
{
|
|
|
|
if (m_params.isCreation)
|
|
|
|
{
|
|
|
|
bigint gas;
|
|
|
|
for (auto b: _data)
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += b ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas;
|
2015-06-01 10:32:59 +00:00
|
|
|
return gas;
|
|
|
|
}
|
|
|
|
else
|
2016-04-06 18:55:46 +00:00
|
|
|
return GasCosts::createDataGas * dataSize();
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
|
|
|
{
|
|
|
|
size_t size = 0;
|
|
|
|
for (AssemblyItem const& item: _items)
|
|
|
|
size += item.bytesRequired(3); // assume 3 byte addresses
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstantOptimisationMethod::replaceConstants(
|
|
|
|
AssemblyItems& _items,
|
2017-01-06 16:05:27 +00:00
|
|
|
map<u256, AssemblyItems> const& _replacements
|
|
|
|
)
|
2015-06-01 10:32:59 +00:00
|
|
|
{
|
2017-01-06 16:05:27 +00:00
|
|
|
AssemblyItems replaced;
|
|
|
|
for (AssemblyItem const& item: _items)
|
2015-06-01 10:32:59 +00:00
|
|
|
{
|
2017-01-06 16:05:27 +00:00
|
|
|
if (item.type() == Push)
|
|
|
|
{
|
|
|
|
auto it = _replacements.find(item.data());
|
|
|
|
if (it != _replacements.end())
|
|
|
|
{
|
|
|
|
replaced += it->second;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
replaced.push_back(item);
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
2017-01-06 16:05:27 +00:00
|
|
|
_items = std::move(replaced);
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bigint LiteralMethod::gasNeeded()
|
|
|
|
{
|
|
|
|
return combineGas(
|
2015-06-05 15:34:20 +00:00
|
|
|
simpleRunGas({Instruction::PUSH1}),
|
2015-06-01 10:32:59 +00:00
|
|
|
// PUSHX plus data
|
2016-04-06 18:55:46 +00:00
|
|
|
(m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas) + dataGas(),
|
2015-06-01 10:32:59 +00:00
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value):
|
2015-06-04 09:02:34 +00:00
|
|
|
ConstantOptimisationMethod(_params, _value)
|
|
|
|
{
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bigint CodeCopyMethod::gasNeeded()
|
|
|
|
{
|
|
|
|
return combineGas(
|
|
|
|
// Run gas: we ignore memory increase costs
|
2017-01-06 16:05:27 +00:00
|
|
|
simpleRunGas(copyRoutine()) + GasCosts::copyGas,
|
2015-06-01 10:32:59 +00:00
|
|
|
// Data gas for copy routines: Some bytes are zero, but we ignore them.
|
2017-01-06 16:05:27 +00:00
|
|
|
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
|
2015-06-01 10:32:59 +00:00
|
|
|
// Data gas for data itself
|
|
|
|
dataGas(toBigEndian(m_value))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-01-06 16:05:27 +00:00
|
|
|
AssemblyItems CodeCopyMethod::execute(Assembly& _assembly)
|
2015-06-01 10:32:59 +00:00
|
|
|
{
|
|
|
|
bytes data = toBigEndian(m_value);
|
2017-01-06 16:05:27 +00:00
|
|
|
AssemblyItems actualCopyRoutine = copyRoutine();
|
|
|
|
actualCopyRoutine[4] = _assembly.newData(data);
|
|
|
|
return actualCopyRoutine;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssemblyItems const& CodeCopyMethod::copyRoutine() const
|
|
|
|
{
|
|
|
|
AssemblyItems static copyRoutine{
|
|
|
|
u256(0),
|
|
|
|
Instruction::DUP1,
|
|
|
|
Instruction::MLOAD, // back up memory
|
|
|
|
u256(32),
|
|
|
|
AssemblyItem(PushData, u256(1) << 16), // has to be replaced
|
|
|
|
Instruction::DUP4,
|
|
|
|
Instruction::CODECOPY,
|
|
|
|
Instruction::DUP2,
|
|
|
|
Instruction::MLOAD,
|
|
|
|
Instruction::SWAP2,
|
|
|
|
Instruction::MSTORE
|
|
|
|
};
|
|
|
|
return copyRoutine;
|
2015-06-01 10:32:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
|
|
|
|
{
|
|
|
|
if (_value < 0x10000)
|
|
|
|
// Very small value, not worth computing
|
|
|
|
return AssemblyItems{_value};
|
|
|
|
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value))
|
|
|
|
// Negated is shorter to represent
|
|
|
|
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Decompose value into a * 2**k + b where abs(b) << 2**k
|
|
|
|
// Is not always better, try literal and decomposition method.
|
|
|
|
AssemblyItems routine{u256(_value)};
|
|
|
|
bigint bestGas = gasNeeded(routine);
|
2017-03-03 15:17:21 +00:00
|
|
|
for (unsigned bits = 255; bits > 8 && m_maxSteps > 0; --bits)
|
2015-06-01 10:32:59 +00:00
|
|
|
{
|
|
|
|
unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff;
|
|
|
|
if (gapDetector != 0xff && gapDetector != 0x100)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
u256 powerOfTwo = u256(1) << bits;
|
|
|
|
u256 upperPart = _value >> bits;
|
|
|
|
bigint lowerPart = _value & (powerOfTwo - 1);
|
|
|
|
if (abs(powerOfTwo - lowerPart) < lowerPart)
|
|
|
|
lowerPart = lowerPart - powerOfTwo; // make it negative
|
|
|
|
if (abs(lowerPart) >= (powerOfTwo >> 8))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
AssemblyItems newRoutine;
|
|
|
|
if (lowerPart != 0)
|
|
|
|
newRoutine += findRepresentation(u256(abs(lowerPart)));
|
|
|
|
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP};
|
|
|
|
if (upperPart != 1 && upperPart != 0)
|
|
|
|
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
|
|
|
|
if (lowerPart > 0)
|
|
|
|
newRoutine += AssemblyItems{Instruction::ADD};
|
|
|
|
else if (lowerPart < 0)
|
2015-06-05 15:34:20 +00:00
|
|
|
newRoutine.push_back(Instruction::SUB);
|
2015-06-01 10:32:59 +00:00
|
|
|
|
2017-03-03 15:17:21 +00:00
|
|
|
if (m_maxSteps > 0)
|
|
|
|
m_maxSteps--;
|
2015-06-01 10:32:59 +00:00
|
|
|
bigint newGas = gasNeeded(newRoutine);
|
|
|
|
if (newGas < bestGas)
|
|
|
|
{
|
|
|
|
bestGas = move(newGas);
|
|
|
|
routine = move(newRoutine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return routine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine)
|
|
|
|
{
|
2015-06-05 15:34:20 +00:00
|
|
|
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP);
|
2015-06-01 10:32:59 +00:00
|
|
|
return combineGas(
|
2016-04-06 18:55:46 +00:00
|
|
|
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas),
|
2015-06-01 10:32:59 +00:00
|
|
|
// Data gas for routine: Some bytes are zero, but we ignore them.
|
2016-04-06 18:55:46 +00:00
|
|
|
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas),
|
2015-06-01 10:32:59 +00:00
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|