#include <libyul/optimiser/LoadResolver.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/SideEffects.h>
#include <libyul/AST.h>
#include <libyul/Utilities.h>
#include <libevmasm/GasMeter.h>
#include <libsolutil/Keccak256.h>
#include <limits>
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
using namespace solidity::yul;
void LoadResolver::run(OptimiserStepContext& _context, Block& _ast)
{
bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast);
LoadResolver{
_context.dialect,
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)),
containsMSize,
_context.expectedExecutionsPerDeployment
}(_ast);
}
void LoadResolver::visit(Expression& _e)
{
DataFlowAnalyzer::visit(_e);
if (FunctionCall const* funCall = std::get_if<FunctionCall>(&_e))
{
for (auto location: { StoreLoadLocation::Memory, StoreLoadLocation::Storage })
if (funCall->functionName.name == m_loadFunctionName[static_cast<unsigned>(location)])
{
tryResolve(_e, location, funCall->arguments);
break;
}
if (!m_containsMSize && funCall->functionName.name == m_dialect.hashFunction({}))
tryEvaluateKeccak(_e, funCall->arguments);
}
}
void LoadResolver::tryResolve(
Expression& _e,
StoreLoadLocation _location,
vector<Expression> const& _arguments
)
{
if (_arguments.empty() || !holds_alternative<Identifier>(_arguments.at(0)))
return;
YulString key = std::get<Identifier>(_arguments.at(0)).name;
if (_location == StoreLoadLocation::Storage)
{
if (auto value = storageValue(key))
if (inScope(*value))
_e = Identifier{debugDataOf(_e), *value};
}
else if (!m_containsMSize && _location == StoreLoadLocation::Memory)
if (auto value = memoryValue(key))
if (inScope(*value))
_e = Identifier{debugDataOf(_e), *value};
}
void LoadResolver::tryEvaluateKeccak(
Expression& _e,
std::vector<Expression> const& _arguments
)
{
yulAssert(_arguments.size() == 2, "");
Identifier const* memoryKey = std::get_if<Identifier>(&_arguments.at(0));
Identifier const* length = std::get_if<Identifier>(&_arguments.at(1));
if (!memoryKey || !length)
return;
GasMeter gasMeter{
dynamic_cast<EVMDialect const&>(m_dialect),
!m_expectedExecutionsPerDeployment,
m_expectedExecutionsPerDeployment ? *m_expectedExecutionsPerDeployment : 1
};
bigint costOfKeccak = gasMeter.costs(_e);
bigint costOfLiteral = gasMeter.costs(
Literal{
{},
LiteralKind::Number,
YulString{numeric_limits<u256>::max().str()},
{}
}
);
if (costOfLiteral > costOfKeccak)
return;
optional<YulString> value = memoryValue(memoryKey->name);
if (value && inScope(*value))
{
optional<u256> memoryContent = valueOfIdentifier(*value);
optional<u256> byteLength = valueOfIdentifier(length->name);
if (memoryContent && byteLength && *byteLength <= 32)
{
bytes contentAsBytes = toBigEndian(*memoryContent);
contentAsBytes.resize(static_cast<size_t>(*byteLength));
_e = Literal{
debugDataOf(_e),
LiteralKind::Number,
YulString{u256(keccak256(contentAsBytes)).str()},
m_dialect.defaultType
};
}
}
}