mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Initial still broken version.
This commit is contained in:
parent
71a860ecf5
commit
1d5dfcdaf0
@ -52,11 +52,11 @@ AssemblyItem const& Assembly::append(AssemblyItem _i)
|
|||||||
{
|
{
|
||||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||||
m_deposit += static_cast<int>(_i.deposit());
|
m_deposit += static_cast<int>(_i.deposit());
|
||||||
m_items.emplace_back(std::move(_i));
|
items().emplace_back(std::move(_i));
|
||||||
if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid())
|
if (!items().back().location().isValid() && m_currentSourceLocation.isValid())
|
||||||
m_items.back().setLocation(m_currentSourceLocation);
|
items().back().setLocation(m_currentSourceLocation);
|
||||||
m_items.back().m_modifierDepth = m_currentModifierDepth;
|
items().back().m_modifierDepth = m_currentModifierDepth;
|
||||||
return m_items.back();
|
return items().back();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Assembly::codeSize(unsigned subTagSize) const
|
unsigned Assembly::codeSize(unsigned subTagSize) const
|
||||||
@ -67,7 +67,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
|
|||||||
for (auto const& i: m_data)
|
for (auto const& i: m_data)
|
||||||
ret += i.second.size();
|
ret += i.second.size();
|
||||||
|
|
||||||
for (AssemblyItem const& i: m_items)
|
for (AssemblyItem const& i: items())
|
||||||
ret += i.bytesRequired(tagSize, Precision::Approximate);
|
ret += i.bytesRequired(tagSize, Precision::Approximate);
|
||||||
if (numberEncodingSize(ret) <= tagSize)
|
if (numberEncodingSize(ret) <= tagSize)
|
||||||
return static_cast<unsigned>(ret);
|
return static_cast<unsigned>(ret);
|
||||||
@ -189,7 +189,7 @@ void Assembly::assemblyStream(
|
|||||||
{
|
{
|
||||||
Functionalizer f(_out, _prefix, _sourceCodes, *this);
|
Functionalizer f(_out, _prefix, _sourceCodes, *this);
|
||||||
|
|
||||||
for (auto const& i: m_items)
|
for (auto const& i: items())
|
||||||
f.feed(i, _debugInfoSelection);
|
f.feed(i, _debugInfoSelection);
|
||||||
f.flush();
|
f.flush();
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
|
|||||||
Json::Value root;
|
Json::Value root;
|
||||||
root[".code"] = Json::arrayValue;
|
root[".code"] = Json::arrayValue;
|
||||||
Json::Value& code = root[".code"];
|
Json::Value& code = root[".code"];
|
||||||
for (AssemblyItem const& item: m_items)
|
for (AssemblyItem const& item: items())
|
||||||
{
|
{
|
||||||
int sourceIndex = -1;
|
int sourceIndex = -1;
|
||||||
if (item.location().sourceName)
|
if (item.location().sourceName)
|
||||||
@ -344,6 +344,10 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
std::set<size_t> _tagsReferencedFromOutside
|
std::set<size_t> _tagsReferencedFromOutside
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (m_eofVersion.has_value())
|
||||||
|
// TODO
|
||||||
|
return *m_tagReplacements;
|
||||||
|
|
||||||
if (m_tagReplacements)
|
if (m_tagReplacements)
|
||||||
return *m_tagReplacements;
|
return *m_tagReplacements;
|
||||||
|
|
||||||
@ -354,10 +358,10 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
Assembly& sub = *m_subs[subId];
|
Assembly& sub = *m_subs[subId];
|
||||||
map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
|
map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
|
||||||
settings,
|
settings,
|
||||||
JumpdestRemover::referencedTags(m_items, subId)
|
JumpdestRemover::referencedTags(items(), subId)
|
||||||
);
|
);
|
||||||
// Apply the replacements (can be empty).
|
// Apply the replacements (can be empty).
|
||||||
BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId);
|
BlockDeduplicator::applyTagReplacement(items(), subTagReplacements, subId);
|
||||||
}
|
}
|
||||||
|
|
||||||
map<u256, u256> tagReplacements;
|
map<u256, u256> tagReplacements;
|
||||||
@ -368,7 +372,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
|
|
||||||
if (_settings.runInliner)
|
if (_settings.runInliner)
|
||||||
Inliner{
|
Inliner{
|
||||||
m_items,
|
items(),
|
||||||
_tagsReferencedFromOutside,
|
_tagsReferencedFromOutside,
|
||||||
_settings.expectedExecutionsPerDeployment,
|
_settings.expectedExecutionsPerDeployment,
|
||||||
isCreation(),
|
isCreation(),
|
||||||
@ -377,14 +381,14 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
|
|
||||||
if (_settings.runJumpdestRemover)
|
if (_settings.runJumpdestRemover)
|
||||||
{
|
{
|
||||||
JumpdestRemover jumpdestOpt{m_items};
|
JumpdestRemover jumpdestOpt{items()};
|
||||||
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
|
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_settings.runPeephole)
|
if (_settings.runPeephole)
|
||||||
{
|
{
|
||||||
PeepholeOptimiser peepOpt{m_items};
|
PeepholeOptimiser peepOpt{items()};
|
||||||
while (peepOpt.optimise())
|
while (peepOpt.optimise())
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
@ -395,7 +399,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
// This only modifies PushTags, we have to run again to actually remove code.
|
// This only modifies PushTags, we have to run again to actually remove code.
|
||||||
if (_settings.runDeduplicate)
|
if (_settings.runDeduplicate)
|
||||||
{
|
{
|
||||||
BlockDeduplicator deduplicator{m_items};
|
BlockDeduplicator deduplicator{items()};
|
||||||
if (deduplicator.deduplicate())
|
if (deduplicator.deduplicate())
|
||||||
{
|
{
|
||||||
for (auto const& replacement: deduplicator.replacedTags())
|
for (auto const& replacement: deduplicator.replacedTags())
|
||||||
@ -425,17 +429,17 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
// function types that can be stored in storage.
|
// function types that can be stored in storage.
|
||||||
AssemblyItems optimisedItems;
|
AssemblyItems optimisedItems;
|
||||||
|
|
||||||
bool usesMSize = ranges::any_of(m_items, [](AssemblyItem const& _i) {
|
bool usesMSize = ranges::any_of(items(), [](AssemblyItem const& _i) {
|
||||||
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto iter = m_items.begin();
|
auto iter = items().begin();
|
||||||
while (iter != m_items.end())
|
while (iter != items().end())
|
||||||
{
|
{
|
||||||
KnownState emptyState;
|
KnownState emptyState;
|
||||||
CommonSubexpressionEliminator eliminator{emptyState};
|
CommonSubexpressionEliminator eliminator{emptyState};
|
||||||
auto orig = iter;
|
auto orig = iter;
|
||||||
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
|
iter = eliminator.feedItems(iter, items().end(), usesMSize);
|
||||||
bool shouldReplace = false;
|
bool shouldReplace = false;
|
||||||
AssemblyItems optimisedChunk;
|
AssemblyItems optimisedChunk;
|
||||||
try
|
try
|
||||||
@ -462,9 +466,9 @@ map<u256, u256> const& Assembly::optimiseInternal(
|
|||||||
else
|
else
|
||||||
copy(orig, iter, back_inserter(optimisedItems));
|
copy(orig, iter, back_inserter(optimisedItems));
|
||||||
}
|
}
|
||||||
if (optimisedItems.size() < m_items.size())
|
if (optimisedItems.size() < items().size())
|
||||||
{
|
{
|
||||||
m_items = std::move(optimisedItems);
|
items() = std::move(optimisedItems);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,6 +497,8 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
|
|
||||||
LinkerObject& ret = m_assembledObject;
|
LinkerObject& ret = m_assembledObject;
|
||||||
|
|
||||||
|
bool const needsEOFContainer = m_eofVersion.has_value();
|
||||||
|
|
||||||
size_t subTagSize = 1;
|
size_t subTagSize = 1;
|
||||||
map<u256, pair<string, vector<size_t>>> immutableReferencesBySub;
|
map<u256, pair<string, vector<size_t>>> immutableReferencesBySub;
|
||||||
for (auto const& sub: m_subs)
|
for (auto const& sub: m_subs)
|
||||||
@ -515,7 +521,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
bool setsImmutables = false;
|
bool setsImmutables = false;
|
||||||
bool pushesImmutables = false;
|
bool pushesImmutables = false;
|
||||||
|
|
||||||
for (auto const& i: m_items)
|
for (auto const& i: items())
|
||||||
if (i.type() == AssignImmutable)
|
if (i.type() == AssignImmutable)
|
||||||
{
|
{
|
||||||
i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size());
|
i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size());
|
||||||
@ -539,6 +545,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode);
|
unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode);
|
||||||
uint8_t tagPush = static_cast<uint8_t>(pushInstruction(bytesPerTag));
|
uint8_t tagPush = static_cast<uint8_t>(pushInstruction(bytesPerTag));
|
||||||
|
|
||||||
|
// TODO: all of this is a bit off
|
||||||
unsigned bytesRequiredIncludingData = (m_eofVersion.has_value() ? 10 : 0) + bytesRequiredForCode + 1 + static_cast<unsigned>(m_auxiliaryData.size());
|
unsigned bytesRequiredIncludingData = (m_eofVersion.has_value() ? 10 : 0) + bytesRequiredForCode + 1 + static_cast<unsigned>(m_auxiliaryData.size());
|
||||||
for (auto const& sub: m_subs)
|
for (auto const& sub: m_subs)
|
||||||
bytesRequiredIncludingData += static_cast<unsigned>(sub->assemble().bytecode.size());
|
bytesRequiredIncludingData += static_cast<unsigned>(sub->assemble().bytecode.size());
|
||||||
@ -549,27 +556,55 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
|
|
||||||
// Insert EOF1 header.
|
// Insert EOF1 header.
|
||||||
bytesRef eofCodeLength(&ret.bytecode.back(), 0);
|
bytesRef eofCodeLength(&ret.bytecode.back(), 0);
|
||||||
|
vector<bytesRef> eofFunctionLengths;
|
||||||
bytesRef eofDataLength(&ret.bytecode.back(), 0);
|
bytesRef eofDataLength(&ret.bytecode.back(), 0);
|
||||||
if (m_eofVersion.has_value())
|
if (needsEOFContainer)
|
||||||
{
|
{
|
||||||
// TODO: empty data is disallowed
|
// TODO: empty data is disallowed
|
||||||
ret.bytecode.push_back(0xef);
|
ret.bytecode.push_back(0xef);
|
||||||
ret.bytecode.push_back(0x00);
|
ret.bytecode.push_back(0x00);
|
||||||
ret.bytecode.push_back(0x01); // version 1
|
ret.bytecode.push_back(0x01); // version 1
|
||||||
|
if (!m_functions.empty())
|
||||||
|
{
|
||||||
|
ret.bytecode.push_back(0x03); // kind=type
|
||||||
|
ret.bytecode.push_back(0x00); // length of type section
|
||||||
|
ret.bytecode.push_back(0x00);
|
||||||
|
bytesRef length(&ret.bytecode.back() + 1 - 2, 2);
|
||||||
|
toBigEndian((m_functions.size() + 1) * 2, length);
|
||||||
|
}
|
||||||
ret.bytecode.push_back(0x01); // kind=code
|
ret.bytecode.push_back(0x01); // kind=code
|
||||||
ret.bytecode.push_back(0x00); // length of code
|
ret.bytecode.push_back(0x00); // length of code
|
||||||
ret.bytecode.push_back(0x00);
|
ret.bytecode.push_back(0x00);
|
||||||
eofCodeLength = bytesRef(&ret.bytecode.back() + 1 - 2, 2);
|
eofCodeLength = bytesRef(&ret.bytecode.back() + 1 - 2, 2);
|
||||||
|
for (size_t i = 0; i < m_functions.size(); ++i)
|
||||||
|
{
|
||||||
|
ret.bytecode.push_back(0x01); // kind=code
|
||||||
|
ret.bytecode.push_back(0x00); // length of code
|
||||||
|
ret.bytecode.push_back(0x00);
|
||||||
|
eofFunctionLengths.emplace_back(&ret.bytecode.back() + 1 - 2, 2);
|
||||||
|
}
|
||||||
ret.bytecode.push_back(0x02); // kind=data
|
ret.bytecode.push_back(0x02); // kind=data
|
||||||
ret.bytecode.push_back(0x00); // length of data
|
ret.bytecode.push_back(0x00); // length of data
|
||||||
ret.bytecode.push_back(0x00);
|
ret.bytecode.push_back(0x00);
|
||||||
eofDataLength = bytesRef(&ret.bytecode.back() + 1 - 2, 2);
|
eofDataLength = bytesRef(&ret.bytecode.back() + 1 - 2, 2);
|
||||||
ret.bytecode.push_back(0x00); // terminator
|
ret.bytecode.push_back(0x00); // terminator
|
||||||
|
if (!m_functions.empty())
|
||||||
|
{
|
||||||
|
ret.bytecode.push_back(0);
|
||||||
|
ret.bytecode.push_back(0);
|
||||||
|
for (auto const& [args, rets, functionAssembly]: m_functions)
|
||||||
|
{
|
||||||
|
(void)functionAssembly;
|
||||||
|
ret.bytecode.push_back(args);
|
||||||
|
ret.bytecode.push_back(rets);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const codeStart = ret.bytecode.size();
|
auto const codeStart = ret.bytecode.size();
|
||||||
|
|
||||||
for (AssemblyItem const& i: m_items)
|
auto assembleItems = [&](AssemblyItems const& _items) {
|
||||||
|
for (AssemblyItem const& i: _items)
|
||||||
{
|
{
|
||||||
// store position of the invalid jump destination
|
// store position of the invalid jump destination
|
||||||
if (i.type() != Tag && m_tagPositionsInBytecode[0] == numeric_limits<size_t>::max())
|
if (i.type() != Tag && m_tagPositionsInBytecode[0] == numeric_limits<size_t>::max())
|
||||||
@ -684,10 +719,25 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::JUMPDEST));
|
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::JUMPDEST));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CallF:
|
||||||
|
{
|
||||||
|
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::CALLF));
|
||||||
|
ret.bytecode.resize(ret.bytecode.size() + 2);
|
||||||
|
bytesRef byr(&ret.bytecode.back() + 1 - 2, 2);
|
||||||
|
toBigEndian(i.data(), byr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RetF:
|
||||||
|
{
|
||||||
|
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETF));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling.");
|
assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
assembleItems(items());
|
||||||
|
|
||||||
if (!immutableReferencesBySub.empty())
|
if (!immutableReferencesBySub.empty())
|
||||||
throw
|
throw
|
||||||
@ -697,17 +747,26 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
"Some immutables were read from but never assigned, possibly because of optimization."
|
"Some immutables were read from but never assigned, possibly because of optimization."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty())
|
if (!needsEOFContainer && (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()))
|
||||||
// Append an INVALID here to help tests find miscompilation.
|
// Append an INVALID here to help tests find miscompilation.
|
||||||
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::INVALID));
|
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::INVALID));
|
||||||
|
|
||||||
auto const codeLength = ret.bytecode.size() - codeStart;
|
auto const codeLength = ret.bytecode.size() - codeStart;
|
||||||
if (m_eofVersion.has_value())
|
if (needsEOFContainer)
|
||||||
{
|
{
|
||||||
assertThrow(codeLength > 0 && codeLength <= 0xffff, AssemblyException, "Invalid code section size.");
|
assertThrow(codeLength > 0 && codeLength <= 0xffff, AssemblyException, "Invalid code section size.");
|
||||||
toBigEndian(codeLength, eofCodeLength);
|
toBigEndian(codeLength, eofCodeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_functions.size(); ++i)
|
||||||
|
{
|
||||||
|
size_t start = ret.bytecode.size();
|
||||||
|
assembleItems(std::get<2>(m_functions[i]));
|
||||||
|
size_t size = ret.bytecode.size() - start;
|
||||||
|
toBigEndian(size, eofFunctionLengths.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
auto const dataStart = ret.bytecode.size();
|
auto const dataStart = ret.bytecode.size();
|
||||||
|
|
||||||
for (auto const& [subIdPath, bytecodeOffset]: subRef)
|
for (auto const& [subIdPath, bytecodeOffset]: subRef)
|
||||||
@ -738,7 +797,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
{
|
{
|
||||||
size_t position = m_tagPositionsInBytecode.at(tagInfo.id);
|
size_t position = m_tagPositionsInBytecode.at(tagInfo.id);
|
||||||
optional<size_t> tagIndex;
|
optional<size_t> tagIndex;
|
||||||
for (auto&& [index, item]: m_items | ranges::views::enumerate)
|
for (auto&& [index, item]: items() | ranges::views::enumerate)
|
||||||
if (item.type() == Tag && static_cast<size_t>(item.data()) == tagInfo.id)
|
if (item.type() == Tag && static_cast<size_t>(item.data()) == tagInfo.id)
|
||||||
{
|
{
|
||||||
tagIndex = index;
|
tagIndex = index;
|
||||||
@ -775,7 +834,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto dataLength = ret.bytecode.size() - dataStart;
|
auto dataLength = ret.bytecode.size() - dataStart;
|
||||||
if (m_eofVersion.has_value())
|
if (needsEOFContainer)
|
||||||
{
|
{
|
||||||
// Note: Temporary solution to current evmone requirement of non-empty data section.
|
// Note: Temporary solution to current evmone requirement of non-empty data section.
|
||||||
if (dataLength == 0)
|
if (dataLength == 0)
|
||||||
|
@ -51,13 +51,46 @@ class Assembly
|
|||||||
public:
|
public:
|
||||||
Assembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name): m_creation(_creation), m_eofVersion(_eofVersion), m_name(std::move(_name)) { }
|
Assembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name): m_creation(_creation), m_eofVersion(_eofVersion), m_name(std::move(_name)) { }
|
||||||
|
|
||||||
|
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
|
||||||
|
bool supportsFunctions() const { return m_eofVersion.has_value(); }
|
||||||
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
|
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
|
||||||
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
||||||
|
AssemblyItem newFunctionCall(uint16_t _functionID)
|
||||||
|
{
|
||||||
|
auto&& [args, rets, functionItems] = m_functions.at(_functionID);
|
||||||
|
(void)functionItems;
|
||||||
|
return AssemblyItem::functionCall(_functionID, args, rets);
|
||||||
|
}
|
||||||
|
AssemblyItem newFunctionReturn()
|
||||||
|
{
|
||||||
|
assertThrow(m_currentFunctionID.has_value(), AssemblyException, "");
|
||||||
|
return AssemblyItem::functionReturn(std::get<1>(m_functions.at(*m_currentFunctionID)));
|
||||||
|
}
|
||||||
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
||||||
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
|
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
|
||||||
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
||||||
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
|
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
|
||||||
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
||||||
|
uint16_t createFunction(uint8_t _args, uint8_t _rets)
|
||||||
|
{
|
||||||
|
size_t functionID = m_functions.size();
|
||||||
|
assertThrow(functionID <= 0xFFFF, AssemblyException, "Too many functions");
|
||||||
|
assertThrow(!m_currentFunctionID.has_value(), AssemblyException, "Nested createFunction");
|
||||||
|
m_functions.emplace_back(_args, _rets, AssemblyItems{});
|
||||||
|
return static_cast<uint16_t>(functionID);
|
||||||
|
}
|
||||||
|
void beginFunction(uint16_t _functionID)
|
||||||
|
{
|
||||||
|
auto& function = m_functions.at(_functionID);
|
||||||
|
assertThrow(!m_currentFunctionID.has_value(), AssemblyException, "Nested beginFunction");
|
||||||
|
assertThrow(std::get<2>(function).empty(), AssemblyException, "Function already defined.");
|
||||||
|
m_currentFunctionID = _functionID;
|
||||||
|
}
|
||||||
|
void endFunction()
|
||||||
|
{
|
||||||
|
assertThrow(m_currentFunctionID.has_value(), AssemblyException, "");
|
||||||
|
m_currentFunctionID.reset();
|
||||||
|
}
|
||||||
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||||
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||||
size_t numSubs() const { return m_subs.size(); }
|
size_t numSubs() const { return m_subs.size(); }
|
||||||
@ -83,6 +116,15 @@ public:
|
|||||||
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
|
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AssemblyItem appendFunctionCall(uint16_t _functionID)
|
||||||
|
{
|
||||||
|
return append(newFunctionCall(_functionID));
|
||||||
|
}
|
||||||
|
AssemblyItem appendFunctionReturn()
|
||||||
|
{
|
||||||
|
return append(newFunctionReturn());
|
||||||
|
}
|
||||||
|
|
||||||
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
||||||
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
||||||
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
|
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
|
||||||
@ -99,10 +141,22 @@ public:
|
|||||||
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
|
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
|
||||||
|
|
||||||
/// Returns the assembly items.
|
/// Returns the assembly items.
|
||||||
AssemblyItems const& items() const { return m_items; }
|
AssemblyItems const& items() const
|
||||||
|
{
|
||||||
|
if (m_currentFunctionID.has_value())
|
||||||
|
return std::get<2>(m_functions.at(*m_currentFunctionID));
|
||||||
|
else
|
||||||
|
return m_mainItems;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the mutable assembly items. Use with care!
|
/// Returns the mutable assembly items. Use with care!
|
||||||
AssemblyItems& items() { return m_items; }
|
AssemblyItems& items()
|
||||||
|
{
|
||||||
|
if (m_currentFunctionID.has_value())
|
||||||
|
return std::get<2>(m_functions.at(*m_currentFunctionID));
|
||||||
|
else
|
||||||
|
return m_mainItems;
|
||||||
|
}
|
||||||
|
|
||||||
int deposit() const { return m_deposit; }
|
int deposit() const { return m_deposit; }
|
||||||
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
|
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
|
||||||
@ -189,11 +243,12 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string, NamedTagInfo> m_namedTags;
|
std::map<std::string, NamedTagInfo> m_namedTags;
|
||||||
AssemblyItems m_items;
|
AssemblyItems m_mainItems;
|
||||||
std::map<util::h256, bytes> m_data;
|
std::map<util::h256, bytes> m_data;
|
||||||
/// Data that is appended to the very end of the contract.
|
/// Data that is appended to the very end of the contract.
|
||||||
bytes m_auxiliaryData;
|
bytes m_auxiliaryData;
|
||||||
std::vector<std::shared_ptr<Assembly>> m_subs;
|
std::vector<std::shared_ptr<Assembly>> m_subs;
|
||||||
|
std::vector<std::tuple<uint8_t, uint8_t, AssemblyItems>> m_functions;
|
||||||
std::map<util::h256, std::string> m_strings;
|
std::map<util::h256, std::string> m_strings;
|
||||||
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||||
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
|
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
|
||||||
@ -213,6 +268,7 @@ protected:
|
|||||||
/// True, if the assembly contains contract creation code.
|
/// True, if the assembly contains contract creation code.
|
||||||
bool const m_creation = false;
|
bool const m_creation = false;
|
||||||
std::optional<uint8_t> m_eofVersion;
|
std::optional<uint8_t> m_eofVersion;
|
||||||
|
std::optional<uint16_t> m_currentFunctionID;
|
||||||
/// Internal name of the assembly object, only used with the Yul backend
|
/// Internal name of the assembly object, only used with the Yul backend
|
||||||
/// currently
|
/// currently
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
@ -159,6 +159,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
|
|||||||
}
|
}
|
||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
return std::get<2>(*m_verbatimBytecode).size();
|
return std::get<2>(*m_verbatimBytecode).size();
|
||||||
|
case CallF:
|
||||||
|
return 3;
|
||||||
|
case RetF:
|
||||||
|
return 1;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -173,6 +177,15 @@ size_t AssemblyItem::arguments() const
|
|||||||
return get<0>(*m_verbatimBytecode);
|
return get<0>(*m_verbatimBytecode);
|
||||||
else if (type() == AssignImmutable)
|
else if (type() == AssignImmutable)
|
||||||
return 2;
|
return 2;
|
||||||
|
else if (type() == CallF)
|
||||||
|
{
|
||||||
|
assertThrow(m_functionSignature.has_value(), AssemblyException, "");
|
||||||
|
return std::get<0>(*m_functionSignature);
|
||||||
|
}
|
||||||
|
else if (type() == RetF)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(data());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -197,6 +210,11 @@ size_t AssemblyItem::returnValues() const
|
|||||||
return 0;
|
return 0;
|
||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
return get<1>(*m_verbatimBytecode);
|
return get<1>(*m_verbatimBytecode);
|
||||||
|
case CallF:
|
||||||
|
assertThrow(m_functionSignature.has_value(), AssemblyException, "");
|
||||||
|
return std::get<1>(*m_functionSignature);
|
||||||
|
case RetF:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -309,6 +327,12 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
|
|||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
text = string("verbatimbytecode_") + util::toHex(get<2>(*m_verbatimBytecode));
|
text = string("verbatimbytecode_") + util::toHex(get<2>(*m_verbatimBytecode));
|
||||||
break;
|
break;
|
||||||
|
case CallF:
|
||||||
|
text = "callf(" + to_string(static_cast<size_t>(data())) + ")";
|
||||||
|
break;
|
||||||
|
case RetF:
|
||||||
|
text = "retf";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertThrow(false, InvalidOpcode, "");
|
assertThrow(false, InvalidOpcode, "");
|
||||||
}
|
}
|
||||||
@ -335,6 +359,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
case Push:
|
case Push:
|
||||||
_out << " PUSH " << hex << _item.data() << dec;
|
_out << " PUSH " << hex << _item.data() << dec;
|
||||||
break;
|
break;
|
||||||
|
case CallF:
|
||||||
|
_out << " CALLF " << dec << _item.data();
|
||||||
|
break;
|
||||||
|
case RetF:
|
||||||
|
_out << " RETF";
|
||||||
|
break;
|
||||||
case PushTag:
|
case PushTag:
|
||||||
{
|
{
|
||||||
size_t subId = _item.splitForeignPushTag().first;
|
size_t subId = _item.splitForeignPushTag().first;
|
||||||
|
@ -50,7 +50,9 @@ enum AssemblyItemType
|
|||||||
PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer.
|
PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer.
|
||||||
PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor.
|
PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor.
|
||||||
AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code.
|
AssignImmutable, ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code.
|
||||||
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
|
VerbatimBytecode, ///< Contains data that is inserted into the bytecode code section without modification.
|
||||||
|
CallF,
|
||||||
|
RetF
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Precision { Precise , Approximate };
|
enum class Precision { Precise , Approximate };
|
||||||
@ -85,6 +87,16 @@ public:
|
|||||||
m_instruction{},
|
m_instruction{},
|
||||||
m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}}
|
m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}}
|
||||||
{}
|
{}
|
||||||
|
static AssemblyItem functionCall(uint16_t _functionID, uint8_t _args, uint8_t _rets, langutil::SourceLocation _location = langutil::SourceLocation())
|
||||||
|
{
|
||||||
|
AssemblyItem result(CallF, _functionID, _location);
|
||||||
|
result.m_functionSignature = std::make_tuple(_args, _rets);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static AssemblyItem functionReturn(uint8_t _rets, langutil::SourceLocation _location = langutil::SourceLocation())
|
||||||
|
{
|
||||||
|
return AssemblyItem(RetF, _rets, _location);
|
||||||
|
}
|
||||||
|
|
||||||
AssemblyItem(AssemblyItem const&) = default;
|
AssemblyItem(AssemblyItem const&) = default;
|
||||||
AssemblyItem(AssemblyItem&&) = default;
|
AssemblyItem(AssemblyItem&&) = default;
|
||||||
@ -191,6 +203,7 @@ private:
|
|||||||
AssemblyItemType m_type;
|
AssemblyItemType m_type;
|
||||||
Instruction m_instruction; ///< Only valid if m_type == Operation
|
Instruction m_instruction; ///< Only valid if m_type == Operation
|
||||||
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
|
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
|
||||||
|
std::optional<std::tuple<uint8_t, uint8_t>> m_functionSignature;
|
||||||
/// If m_type == VerbatimBytecode, this holds number of arguments, number of
|
/// If m_type == VerbatimBytecode, this holds number of arguments, number of
|
||||||
/// return variables and verbatim bytecode.
|
/// return variables and verbatim bytecode.
|
||||||
std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode;
|
std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode;
|
||||||
|
@ -175,6 +175,10 @@ enum class Instruction: uint8_t
|
|||||||
LOG3, ///< Makes a log entry; 3 topics.
|
LOG3, ///< Makes a log entry; 3 topics.
|
||||||
LOG4, ///< Makes a log entry; 4 topics.
|
LOG4, ///< Makes a log entry; 4 topics.
|
||||||
|
|
||||||
|
CALLF = 0xb0,
|
||||||
|
RETF,
|
||||||
|
JUMPF,
|
||||||
|
|
||||||
CREATE = 0xf0, ///< create a new account with associated code
|
CREATE = 0xf0, ///< create a new account with associated code
|
||||||
CALL, ///< message-call into an account
|
CALL, ///< message-call into an account
|
||||||
CALLCODE, ///< message-call with another account's code only
|
CALLCODE, ///< message-call with another account's code only
|
||||||
|
@ -160,6 +160,8 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
|
|||||||
case PushDeployTimeAddress:
|
case PushDeployTimeAddress:
|
||||||
case AssignImmutable:
|
case AssignImmutable:
|
||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
|
case CallF:
|
||||||
|
case RetF:
|
||||||
return true;
|
return true;
|
||||||
case Push:
|
case Push:
|
||||||
case PushTag:
|
case PushTag:
|
||||||
|
@ -543,6 +543,7 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
|
|||||||
yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
|
yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
|
||||||
yul::OptimiserSuite::run(
|
yul::OptimiserSuite::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
|
assembly().eofVersion(),
|
||||||
&meter,
|
&meter,
|
||||||
_object,
|
_object,
|
||||||
_optimiserSettings.optimizeStackAllocation,
|
_optimiserSettings.optimizeStackAllocation,
|
||||||
|
@ -33,6 +33,7 @@ using namespace solidity::util;
|
|||||||
|
|
||||||
CompilabilityChecker::CompilabilityChecker(
|
CompilabilityChecker::CompilabilityChecker(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
Object const& _object,
|
Object const& _object,
|
||||||
bool _optimizeStackAllocation
|
bool _optimizeStackAllocation
|
||||||
)
|
)
|
||||||
@ -50,7 +51,7 @@ CompilabilityChecker::CompilabilityChecker(
|
|||||||
builtinContext.subIDs[_object.name] = 1;
|
builtinContext.subIDs[_object.name] = 1;
|
||||||
for (auto const& subNode: _object.subObjects)
|
for (auto const& subNode: _object.subObjects)
|
||||||
builtinContext.subIDs[subNode->name] = 1;
|
builtinContext.subIDs[subNode->name] = 1;
|
||||||
NoOutputAssembly assembly;
|
NoOutputAssembly assembly(_eofVersion.has_value());
|
||||||
CodeTransform transform(
|
CodeTransform transform(
|
||||||
assembly,
|
assembly,
|
||||||
analysisInfo,
|
analysisInfo,
|
||||||
|
@ -44,7 +44,12 @@ namespace solidity::yul
|
|||||||
*/
|
*/
|
||||||
struct CompilabilityChecker
|
struct CompilabilityChecker
|
||||||
{
|
{
|
||||||
CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation);
|
CompilabilityChecker(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
|
Object const& _object,
|
||||||
|
bool _optimizeStackAllocation
|
||||||
|
);
|
||||||
std::map<YulString, std::set<YulString>> unreachableVariables;
|
std::map<YulString, std::set<YulString>> unreachableVariables;
|
||||||
std::map<YulString, int> stackDeficit;
|
std::map<YulString, int> stackDeficit;
|
||||||
};
|
};
|
||||||
|
@ -184,6 +184,7 @@ void YulStack::optimize(Object& _object, bool _isCreation)
|
|||||||
meter = make_unique<GasMeter>(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
|
meter = make_unique<GasMeter>(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||||
OptimiserSuite::run(
|
OptimiserSuite::run(
|
||||||
dialect,
|
dialect,
|
||||||
|
m_eofVersion,
|
||||||
meter.get(),
|
meter.get(),
|
||||||
_object,
|
_object,
|
||||||
m_optimiserSettings.optimizeStackAllocation,
|
m_optimiserSettings.optimizeStackAllocation,
|
||||||
|
@ -55,10 +55,12 @@ class AbstractAssembly
|
|||||||
public:
|
public:
|
||||||
using LabelID = size_t;
|
using LabelID = size_t;
|
||||||
using SubID = size_t;
|
using SubID = size_t;
|
||||||
|
using FunctionID = uint16_t;
|
||||||
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
||||||
|
|
||||||
virtual ~AbstractAssembly() = default;
|
virtual ~AbstractAssembly() = default;
|
||||||
|
|
||||||
|
virtual bool supportsFunctions() const = 0;
|
||||||
/// Set a new source location valid starting from the next instruction.
|
/// Set a new source location valid starting from the next instruction.
|
||||||
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
|
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
|
||||||
/// Retrieve the current height of the stack. This does not have to be zero
|
/// Retrieve the current height of the stack. This does not have to be zero
|
||||||
@ -99,6 +101,14 @@ public:
|
|||||||
virtual void appendAssemblySize() = 0;
|
virtual void appendAssemblySize() = 0;
|
||||||
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
|
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
|
||||||
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") = 0;
|
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") = 0;
|
||||||
|
|
||||||
|
virtual FunctionID createFunction(uint8_t _args, uint8_t _rets) = 0;
|
||||||
|
virtual void beginFunction(FunctionID _functionID) = 0;
|
||||||
|
virtual void endFunction() = 0;
|
||||||
|
|
||||||
|
virtual void appendFunctionCall(FunctionID _functionID) = 0;
|
||||||
|
virtual void appendFunctionReturn() = 0;
|
||||||
|
|
||||||
/// Appends the offset of the given sub-assembly or data.
|
/// Appends the offset of the given sub-assembly or data.
|
||||||
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
|
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
|
||||||
/// Appends the size of the given sub-assembly or data.
|
/// Appends the size of the given sub-assembly or data.
|
||||||
|
@ -123,7 +123,7 @@ inline bool canBeFreelyGenerated(StackSlot const& _slot)
|
|||||||
/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow.
|
/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow.
|
||||||
struct CFG
|
struct CFG
|
||||||
{
|
{
|
||||||
explicit CFG() {}
|
explicit CFG(bool _useFunctions): useFunctions(_useFunctions) {}
|
||||||
CFG(CFG const&) = delete;
|
CFG(CFG const&) = delete;
|
||||||
CFG(CFG&&) = delete;
|
CFG(CFG&&) = delete;
|
||||||
CFG& operator=(CFG const&) = delete;
|
CFG& operator=(CFG const&) = delete;
|
||||||
@ -220,6 +220,7 @@ struct CFG
|
|||||||
bool canContinue = true;
|
bool canContinue = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool useFunctions = false;
|
||||||
/// The main entry point, i.e. the start of the outermost Yul block.
|
/// The main entry point, i.e. the start of the outermost Yul block.
|
||||||
BasicBlock* entry = nullptr;
|
BasicBlock* entry = nullptr;
|
||||||
/// Subgraphs for functions.
|
/// Subgraphs for functions.
|
||||||
|
@ -209,10 +209,11 @@ void markNeedsCleanStack(CFG& _cfg)
|
|||||||
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
||||||
AsmAnalysisInfo const& _analysisInfo,
|
AsmAnalysisInfo const& _analysisInfo,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
Block const& _block
|
Block const& _block
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto result = std::make_unique<CFG>();
|
auto result = std::make_unique<CFG>(_eofVersion.has_value());
|
||||||
result->entry = &result->makeBlock(debugDataOf(_block));
|
result->entry = &result->makeBlock(debugDataOf(_block));
|
||||||
|
|
||||||
ControlFlowSideEffectsCollector sideEffects(_dialect, _block);
|
ControlFlowSideEffectsCollector sideEffects(_dialect, _block);
|
||||||
@ -541,7 +542,7 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
|
|||||||
Scope::Function const& function = lookupFunction(_call.functionName.name);
|
Scope::Function const& function = lookupFunction(_call.functionName.name);
|
||||||
canContinue = m_graph.functionInfo.at(&function).canContinue;
|
canContinue = m_graph.functionInfo.at(&function).canContinue;
|
||||||
Stack inputs;
|
Stack inputs;
|
||||||
if (canContinue)
|
if (!m_graph.useFunctions && canContinue)
|
||||||
inputs.emplace_back(FunctionCallReturnLabelSlot{_call});
|
inputs.emplace_back(FunctionCallReturnLabelSlot{_call});
|
||||||
for (auto const& arg: _call.arguments | ranges::views::reverse)
|
for (auto const& arg: _call.arguments | ranges::views::reverse)
|
||||||
inputs.emplace_back(std::visit(*this, arg));
|
inputs.emplace_back(std::visit(*this, arg));
|
||||||
|
@ -31,7 +31,12 @@ class ControlFlowGraphBuilder
|
|||||||
public:
|
public:
|
||||||
ControlFlowGraphBuilder(ControlFlowGraphBuilder const&) = delete;
|
ControlFlowGraphBuilder(ControlFlowGraphBuilder const&) = delete;
|
||||||
ControlFlowGraphBuilder& operator=(ControlFlowGraphBuilder const&) = delete;
|
ControlFlowGraphBuilder& operator=(ControlFlowGraphBuilder const&) = delete;
|
||||||
static std::unique_ptr<CFG> build(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block);
|
static std::unique_ptr<CFG> build(
|
||||||
|
AsmAnalysisInfo const& _analysisInfo,
|
||||||
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
|
Block const& _block
|
||||||
|
);
|
||||||
|
|
||||||
StackSlot operator()(Expression const& _literal);
|
StackSlot operator()(Expression const& _literal);
|
||||||
StackSlot operator()(Literal const& _literal);
|
StackSlot operator()(Literal const& _literal);
|
||||||
@ -81,6 +86,7 @@ private:
|
|||||||
AsmAnalysisInfo const& m_info;
|
AsmAnalysisInfo const& m_info;
|
||||||
std::map<FunctionDefinition const*, ControlFlowSideEffects> const& m_functionSideEffects;
|
std::map<FunctionDefinition const*, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
std::optional<uint8_t> m_eofVersion;
|
||||||
CFG::BasicBlock* m_currentBlock = nullptr;
|
CFG::BasicBlock* m_currentBlock = nullptr;
|
||||||
Scope* m_scope = nullptr;
|
Scope* m_scope = nullptr;
|
||||||
struct ForLoopInfo
|
struct ForLoopInfo
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
|
#include <libyul/Scope.h>
|
||||||
|
|
||||||
#include <libyul/backends/evm/AbstractAssembly.h>
|
#include <libyul/backends/evm/AbstractAssembly.h>
|
||||||
#include <libyul/ASTForward.h>
|
#include <libyul/ASTForward.h>
|
||||||
@ -46,6 +47,8 @@ struct BuiltinContext
|
|||||||
Object const* currentObject = nullptr;
|
Object const* currentObject = nullptr;
|
||||||
/// Mapping from named objects to abstract assembly sub IDs.
|
/// Mapping from named objects to abstract assembly sub IDs.
|
||||||
std::map<YulString, AbstractAssembly::SubID> subIDs;
|
std::map<YulString, AbstractAssembly::SubID> subIDs;
|
||||||
|
|
||||||
|
std::map<Scope::Function const*, AbstractAssembly::FunctionID> functionIDs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFunctionForEVM: public BuiltinFunction
|
struct BuiltinFunctionForEVM: public BuiltinFunction
|
||||||
|
@ -86,6 +86,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
|
|||||||
*_object.analysisInfo,
|
*_object.analysisInfo,
|
||||||
*_object.code,
|
*_object.code,
|
||||||
m_dialect,
|
m_dialect,
|
||||||
|
m_eofVersion,
|
||||||
context,
|
context,
|
||||||
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||||
);
|
);
|
||||||
|
@ -44,6 +44,11 @@ EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly):
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EthAssemblyAdapter::supportsFunctions() const
|
||||||
|
{
|
||||||
|
return m_assembly.supportsFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
|
void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
m_assembly.setSourceLocation(_location);
|
m_assembly.setSourceLocation(_location);
|
||||||
@ -129,6 +134,31 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::
|
|||||||
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
|
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets)
|
||||||
|
{
|
||||||
|
return m_assembly.createFunction(_args, _rets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthAssemblyAdapter::beginFunction(AbstractAssembly::FunctionID _functionID)
|
||||||
|
{
|
||||||
|
m_assembly.beginFunction(_functionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthAssemblyAdapter::endFunction()
|
||||||
|
{
|
||||||
|
m_assembly.endFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthAssemblyAdapter::appendFunctionReturn()
|
||||||
|
{
|
||||||
|
m_assembly.appendFunctionReturn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthAssemblyAdapter::appendFunctionCall(FunctionID _functionID)
|
||||||
|
{
|
||||||
|
m_assembly.appendFunctionCall(_functionID);
|
||||||
|
}
|
||||||
|
|
||||||
void EthAssemblyAdapter::appendDataOffset(vector<AbstractAssembly::SubID> const& _subPath)
|
void EthAssemblyAdapter::appendDataOffset(vector<AbstractAssembly::SubID> const& _subPath)
|
||||||
{
|
{
|
||||||
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
|
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
|
||||||
|
@ -40,6 +40,7 @@ class EthAssemblyAdapter: public AbstractAssembly
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit EthAssemblyAdapter(evmasm::Assembly& _assembly);
|
explicit EthAssemblyAdapter(evmasm::Assembly& _assembly);
|
||||||
|
bool supportsFunctions() const override;
|
||||||
void setSourceLocation(langutil::SourceLocation const& _location) override;
|
void setSourceLocation(langutil::SourceLocation const& _location) override;
|
||||||
int stackHeight() const override;
|
int stackHeight() const override;
|
||||||
void setStackHeight(int height) override;
|
void setStackHeight(int height) override;
|
||||||
@ -56,6 +57,11 @@ public:
|
|||||||
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
|
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
|
||||||
void appendAssemblySize() override;
|
void appendAssemblySize() override;
|
||||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = {}) override;
|
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = {}) override;
|
||||||
|
AbstractAssembly::FunctionID createFunction(uint8_t _args, uint8_t _rets) override;
|
||||||
|
void beginFunction(AbstractAssembly::FunctionID _functionID) override;
|
||||||
|
void endFunction() override;
|
||||||
|
void appendFunctionCall(FunctionID _functionID) override;
|
||||||
|
void appendFunctionReturn() override;
|
||||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||||
SubID appendData(bytes const& _data) override;
|
SubID appendData(bytes const& _data) override;
|
||||||
|
@ -104,6 +104,33 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::cr
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbstractAssembly::FunctionID NoOutputAssembly::createFunction(uint8_t _args, uint8_t _rets)
|
||||||
|
{
|
||||||
|
yulAssert(m_context->numFunctions <= std::numeric_limits<AbstractAssembly::FunctionID>::max());
|
||||||
|
AbstractAssembly::FunctionID id = static_cast<AbstractAssembly::FunctionID>(m_context->numFunctions++);
|
||||||
|
m_context->functionSignatures[id] = std::make_pair(_args, _rets);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoOutputAssembly::beginFunction(FunctionID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoOutputAssembly::endFunction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoOutputAssembly::appendFunctionCall(FunctionID _functionID)
|
||||||
|
{
|
||||||
|
auto [args, rets] = m_context->functionSignatures.at(_functionID);
|
||||||
|
m_stackHeight += static_cast<int>(rets) - static_cast<int>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoOutputAssembly::appendFunctionReturn()
|
||||||
|
{
|
||||||
|
m_stackHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void NoOutputAssembly::appendDataOffset(std::vector<AbstractAssembly::SubID> const&)
|
void NoOutputAssembly::appendDataOffset(std::vector<AbstractAssembly::SubID> const&)
|
||||||
{
|
{
|
||||||
appendInstruction(evmasm::Instruction::PUSH1);
|
appendInstruction(evmasm::Instruction::PUSH1);
|
||||||
|
@ -37,6 +37,13 @@ struct SourceLocation;
|
|||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class NoOutputAssembly;
|
||||||
|
|
||||||
|
struct NoOutputAssemblyContext
|
||||||
|
{
|
||||||
|
size_t numFunctions = 0;
|
||||||
|
std::map<uint16_t, std::pair<uint8_t, uint8_t>> functionSignatures;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assembly class that just ignores everything and only performs stack counting.
|
* Assembly class that just ignores everything and only performs stack counting.
|
||||||
@ -45,9 +52,13 @@ namespace solidity::yul
|
|||||||
class NoOutputAssembly: public AbstractAssembly
|
class NoOutputAssembly: public AbstractAssembly
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NoOutputAssembly() { }
|
explicit NoOutputAssembly(bool _hasFunctions): m_hasFunctions(_hasFunctions), m_context(std::make_shared<NoOutputAssemblyContext>()) { }
|
||||||
|
NoOutputAssembly(bool _hasFunctions, std::shared_ptr<NoOutputAssemblyContext> _context): m_hasFunctions(_hasFunctions), m_context(_context) {}
|
||||||
|
|
||||||
~NoOutputAssembly() override = default;
|
~NoOutputAssembly() override = default;
|
||||||
|
|
||||||
|
bool supportsFunctions() const override { return m_hasFunctions; }
|
||||||
|
|
||||||
void setSourceLocation(langutil::SourceLocation const&) override {}
|
void setSourceLocation(langutil::SourceLocation const&) override {}
|
||||||
int stackHeight() const override { return m_stackHeight; }
|
int stackHeight() const override { return m_stackHeight; }
|
||||||
void setStackHeight(int height) override { m_stackHeight = height; }
|
void setStackHeight(int height) override { m_stackHeight = height; }
|
||||||
@ -66,6 +77,11 @@ public:
|
|||||||
|
|
||||||
void appendAssemblySize() override;
|
void appendAssemblySize() override;
|
||||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") override;
|
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") override;
|
||||||
|
FunctionID createFunction(uint8_t _args, uint8_t rets) override;
|
||||||
|
void beginFunction(FunctionID) override;
|
||||||
|
void endFunction() override;
|
||||||
|
void appendFunctionCall(FunctionID _functionID) override;
|
||||||
|
void appendFunctionReturn() override;
|
||||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||||
SubID appendData(bytes const& _data) override;
|
SubID appendData(bytes const& _data) override;
|
||||||
@ -78,6 +94,8 @@ public:
|
|||||||
void markAsInvalid() override {}
|
void markAsInvalid() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_hasFunctions = false;
|
||||||
|
std::shared_ptr<NoOutputAssemblyContext> m_context;
|
||||||
int m_stackHeight = 0;
|
int m_stackHeight = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,12 +45,28 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
|||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
Block const& _block,
|
Block const& _block,
|
||||||
EVMDialect const& _dialect,
|
EVMDialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
BuiltinContext& _builtinContext,
|
BuiltinContext& _builtinContext,
|
||||||
UseNamedLabels _useNamedLabelsForFunctions
|
UseNamedLabels _useNamedLabelsForFunctions
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
|
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _eofVersion, _block);
|
||||||
|
yulAssert(_eofVersion.has_value() == dfg->useFunctions);
|
||||||
StackLayout stackLayout = StackLayoutGenerator::run(*dfg);
|
StackLayout stackLayout = StackLayoutGenerator::run(*dfg);
|
||||||
|
|
||||||
|
if (dfg->useFunctions)
|
||||||
|
for (Scope::Function const* function: dfg->functions)
|
||||||
|
{
|
||||||
|
auto const& info = dfg->functionInfo.at(function);
|
||||||
|
yulAssert(info.parameters.size() <= 0xFF);
|
||||||
|
yulAssert(info.returnVariables.size() <= 0xFF);
|
||||||
|
auto functionID = _assembly.createFunction(
|
||||||
|
static_cast<uint8_t>(info.parameters.size()),
|
||||||
|
static_cast<uint8_t>(info.returnVariables.size())
|
||||||
|
);
|
||||||
|
_builtinContext.functionIDs[function] = functionID;
|
||||||
|
}
|
||||||
|
|
||||||
OptimizedEVMCodeTransform optimizedCodeTransform(
|
OptimizedEVMCodeTransform optimizedCodeTransform(
|
||||||
_assembly,
|
_assembly,
|
||||||
_builtinContext,
|
_builtinContext,
|
||||||
@ -68,10 +84,11 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
|||||||
|
|
||||||
void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
||||||
{
|
{
|
||||||
|
bool useReturnLabel = !m_dfg.useFunctions && _call.canContinue;
|
||||||
// Validate stack.
|
// Validate stack.
|
||||||
{
|
{
|
||||||
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
|
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
|
||||||
yulAssert(m_stack.size() >= _call.function.get().arguments.size() + (_call.canContinue ? 1 : 0), "");
|
yulAssert(m_stack.size() >= _call.function.get().arguments.size() + (useReturnLabel ? 1 : 0), "");
|
||||||
// Assert that we got the correct arguments on stack for the call.
|
// Assert that we got the correct arguments on stack for the call.
|
||||||
for (auto&& [arg, slot]: ranges::zip_view(
|
for (auto&& [arg, slot]: ranges::zip_view(
|
||||||
_call.functionCall.get().arguments | ranges::views::reverse,
|
_call.functionCall.get().arguments | ranges::views::reverse,
|
||||||
@ -79,7 +96,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
|||||||
))
|
))
|
||||||
validateSlot(slot, arg);
|
validateSlot(slot, arg);
|
||||||
// Assert that we got the correct return label on stack.
|
// Assert that we got the correct return label on stack.
|
||||||
if (_call.canContinue)
|
if (useReturnLabel)
|
||||||
{
|
{
|
||||||
auto const* returnLabelSlot = get_if<FunctionCallReturnLabelSlot>(
|
auto const* returnLabelSlot = get_if<FunctionCallReturnLabelSlot>(
|
||||||
&m_stack.at(m_stack.size() - _call.functionCall.get().arguments.size() - 1)
|
&m_stack.at(m_stack.size() - _call.functionCall.get().arguments.size() - 1)
|
||||||
@ -91,19 +108,22 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
|||||||
// Emit code.
|
// Emit code.
|
||||||
{
|
{
|
||||||
m_assembly.setSourceLocation(originLocationOf(_call));
|
m_assembly.setSourceLocation(originLocationOf(_call));
|
||||||
m_assembly.appendJumpTo(
|
if (m_dfg.useFunctions)
|
||||||
getFunctionLabel(_call.function),
|
m_assembly.appendFunctionCall(m_builtinContext.functionIDs.at(&_call.function.get()));
|
||||||
static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - (_call.canContinue ? 1 : 0),
|
else
|
||||||
AbstractAssembly::JumpType::IntoFunction
|
m_assembly.appendJumpTo(
|
||||||
);
|
getFunctionLabel(_call.function),
|
||||||
if (_call.canContinue)
|
static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - (_call.canContinue ? 1 : 0),
|
||||||
|
AbstractAssembly::JumpType::IntoFunction
|
||||||
|
);
|
||||||
|
if (useReturnLabel)
|
||||||
m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get()));
|
m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update stack.
|
// Update stack.
|
||||||
{
|
{
|
||||||
// Remove arguments and return label from m_stack.
|
// Remove arguments and return label from m_stack.
|
||||||
for (size_t i = 0; i < _call.function.get().arguments.size() + (_call.canContinue ? 1 : 0); ++i)
|
for (size_t i = 0; i < _call.function.get().arguments.size() + (useReturnLabel ? 1 : 0); ++i)
|
||||||
m_stack.pop_back();
|
m_stack.pop_back();
|
||||||
// Push return values to m_stack.
|
// Push return values to m_stack.
|
||||||
for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size()))
|
for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size()))
|
||||||
@ -184,7 +204,7 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
|
|||||||
m_builtinContext(_builtinContext),
|
m_builtinContext(_builtinContext),
|
||||||
m_dfg(_dfg),
|
m_dfg(_dfg),
|
||||||
m_stackLayout(_stackLayout),
|
m_stackLayout(_stackLayout),
|
||||||
m_functionLabels([&](){
|
m_functionLabels(_dfg.useFunctions ? decltype(m_functionLabels)() : [&](){
|
||||||
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
|
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
|
||||||
set<YulString> assignedFunctionNames;
|
set<YulString> assignedFunctionNames;
|
||||||
for (Scope::Function const* function: m_dfg.functions)
|
for (Scope::Function const* function: m_dfg.functions)
|
||||||
@ -217,6 +237,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS
|
|||||||
|
|
||||||
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
|
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
|
||||||
{
|
{
|
||||||
|
yulAssert(!m_dfg.useFunctions);
|
||||||
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
|
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,11 +512,15 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
|
|||||||
Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
||||||
return StackSlot{_varSlot};
|
return StackSlot{_varSlot};
|
||||||
}) | ranges::to<Stack>;
|
}) | ranges::to<Stack>;
|
||||||
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
if (!m_dfg.useFunctions)
|
||||||
|
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||||
|
|
||||||
// Create the function return layout and jump.
|
// Create the function return layout and jump.
|
||||||
createStackLayout(debugDataOf(_functionReturn), exitStack);
|
createStackLayout(debugDataOf(_functionReturn), exitStack);
|
||||||
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
|
if (m_dfg.useFunctions)
|
||||||
|
m_assembly.appendFunctionReturn();
|
||||||
|
else
|
||||||
|
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
|
||||||
},
|
},
|
||||||
[&](CFG::BasicBlock::Terminated const&)
|
[&](CFG::BasicBlock::Terminated const&)
|
||||||
{
|
{
|
||||||
@ -516,25 +541,31 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
|
|||||||
|
|
||||||
void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
|
void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
|
||||||
{
|
{
|
||||||
|
bool useReturnLabel = !m_dfg.useFunctions && _functionInfo.canContinue;
|
||||||
yulAssert(!m_currentFunctionInfo, "");
|
yulAssert(!m_currentFunctionInfo, "");
|
||||||
ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);
|
ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);
|
||||||
|
|
||||||
yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, "");
|
yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, "");
|
||||||
|
|
||||||
// Create function entry layout in m_stack.
|
// Create function entry layout in m_stack.
|
||||||
if (_functionInfo.canContinue)
|
if (useReturnLabel)
|
||||||
m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
|
m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
|
||||||
for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
|
for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
|
||||||
m_stack.emplace_back(param);
|
m_stack.emplace_back(param);
|
||||||
|
if (m_dfg.useFunctions)
|
||||||
|
m_assembly.beginFunction(m_builtinContext.functionIDs[&_functionInfo.function]);
|
||||||
m_assembly.setStackHeight(static_cast<int>(m_stack.size()));
|
m_assembly.setStackHeight(static_cast<int>(m_stack.size()));
|
||||||
|
|
||||||
m_assembly.setSourceLocation(originLocationOf(_functionInfo));
|
m_assembly.setSourceLocation(originLocationOf(_functionInfo));
|
||||||
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
|
if (!m_dfg.useFunctions)
|
||||||
|
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
|
||||||
|
|
||||||
// Create the entry layout of the function body block and visit.
|
// Create the entry layout of the function body block and visit.
|
||||||
createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout);
|
createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout);
|
||||||
(*this)(*_functionInfo.entry);
|
(*this)(*_functionInfo.entry);
|
||||||
|
|
||||||
m_stack.clear();
|
m_stack.clear();
|
||||||
|
if (m_dfg.useFunctions)
|
||||||
|
m_assembly.endFunction();
|
||||||
m_assembly.setStackHeight(0);
|
m_assembly.setStackHeight(0);
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ public:
|
|||||||
AsmAnalysisInfo& _analysisInfo,
|
AsmAnalysisInfo& _analysisInfo,
|
||||||
Block const& _block,
|
Block const& _block,
|
||||||
EVMDialect const& _dialect,
|
EVMDialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
BuiltinContext& _builtinContext,
|
BuiltinContext& _builtinContext,
|
||||||
UseNamedLabels _useNamedLabelsForFunctions
|
UseNamedLabels _useNamedLabelsForFunctions
|
||||||
);
|
);
|
||||||
|
@ -50,7 +50,7 @@ using namespace std;
|
|||||||
|
|
||||||
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
|
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
|
||||||
{
|
{
|
||||||
StackLayout stackLayout;
|
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
|
||||||
StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry);
|
StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry);
|
||||||
|
|
||||||
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||||
@ -71,7 +71,7 @@ map<YulString, vector<StackLayoutGenerator::StackTooDeep>> StackLayoutGenerator:
|
|||||||
|
|
||||||
vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulString _functionName)
|
vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulString _functionName)
|
||||||
{
|
{
|
||||||
StackLayout stackLayout;
|
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
|
||||||
CFG::FunctionInfo const* functionInfo = nullptr;
|
CFG::FunctionInfo const* functionInfo = nullptr;
|
||||||
if (!_functionName.empty())
|
if (!_functionName.empty())
|
||||||
{
|
{
|
||||||
@ -463,7 +463,9 @@ optional<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
|
|||||||
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
||||||
return StackSlot{_varSlot};
|
return StackSlot{_varSlot};
|
||||||
}) | ranges::to<Stack>;
|
}) | ranges::to<Stack>;
|
||||||
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
|
||||||
|
if (!m_layout.useFunctions)
|
||||||
|
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||||
return stack;
|
return stack;
|
||||||
},
|
},
|
||||||
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
|
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
|
||||||
|
@ -37,6 +37,7 @@ struct StackLayout
|
|||||||
/// The resulting stack layout after executing the block.
|
/// The resulting stack layout after executing the block.
|
||||||
Stack exitLayout;
|
Stack exitLayout;
|
||||||
};
|
};
|
||||||
|
bool useFunctions = false;
|
||||||
std::map<CFG::BasicBlock const*, BlockInfo> blockInfos;
|
std::map<CFG::BasicBlock const*, BlockInfo> blockInfos;
|
||||||
/// For each operation the complete stack layout that:
|
/// For each operation the complete stack layout that:
|
||||||
/// - has the slots required for the operation at the stack top.
|
/// - has the slots required for the operation at the stack top.
|
||||||
|
@ -74,6 +74,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
|||||||
// expectedExecutionsPerDeployment is currently unused.
|
// expectedExecutionsPerDeployment is currently unused.
|
||||||
OptimiserStepContext context{
|
OptimiserStepContext context{
|
||||||
m_dialect,
|
m_dialect,
|
||||||
|
nullopt,
|
||||||
nameDispenser,
|
nameDispenser,
|
||||||
reservedIdentifiers,
|
reservedIdentifiers,
|
||||||
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
||||||
|
@ -35,6 +35,7 @@ class NameDispenser;
|
|||||||
struct OptimiserStepContext
|
struct OptimiserStepContext
|
||||||
{
|
{
|
||||||
Dialect const& dialect;
|
Dialect const& dialect;
|
||||||
|
std::optional<uint8_t> eofVersion;
|
||||||
NameDispenser& dispenser;
|
NameDispenser& dispenser;
|
||||||
std::set<YulString> const& reservedIdentifiers;
|
std::set<YulString> const& reservedIdentifiers;
|
||||||
/// The value nullopt represents creation code
|
/// The value nullopt represents creation code
|
||||||
|
@ -238,6 +238,7 @@ void eliminateVariablesOptimizedCodegen(
|
|||||||
|
|
||||||
bool StackCompressor::run(
|
bool StackCompressor::run(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
size_t _maxIterations
|
size_t _maxIterations
|
||||||
@ -258,7 +259,7 @@ bool StackCompressor::run(
|
|||||||
if (usesOptimizedCodeGenerator)
|
if (usesOptimizedCodeGenerator)
|
||||||
{
|
{
|
||||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
||||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code);
|
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, _eofVersion, *_object.code);
|
||||||
eliminateVariablesOptimizedCodegen(
|
eliminateVariablesOptimizedCodegen(
|
||||||
_dialect,
|
_dialect,
|
||||||
*_object.code,
|
*_object.code,
|
||||||
@ -269,7 +270,7 @@ bool StackCompressor::run(
|
|||||||
else
|
else
|
||||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||||
{
|
{
|
||||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _eofVersion, _object, _optimizeStackAllocation).stackDeficit;
|
||||||
if (stackSurplus.empty())
|
if (stackSurplus.empty())
|
||||||
return true;
|
return true;
|
||||||
eliminateVariables(
|
eliminateVariables(
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
/// @returns true if it was successful.
|
/// @returns true if it was successful.
|
||||||
static bool run(
|
static bool run(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
size_t _maxIterations
|
size_t _maxIterations
|
||||||
|
@ -131,12 +131,13 @@ void StackLimitEvader::run(
|
|||||||
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
|
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
|
||||||
{
|
{
|
||||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
||||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, *_object.code);
|
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, _context.eofVersion, *_object.code);
|
||||||
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
|
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
run(_context, _object, CompilabilityChecker{
|
run(_context, _object, CompilabilityChecker{
|
||||||
_context.dialect,
|
_context.dialect,
|
||||||
|
_context.eofVersion,
|
||||||
_object,
|
_object,
|
||||||
true
|
true
|
||||||
}.unreachableVariables);
|
}.unreachableVariables);
|
||||||
|
@ -135,6 +135,7 @@ void outputPerformanceMetrics(map<string, int64_t> const& _metrics)
|
|||||||
|
|
||||||
void OptimiserSuite::run(
|
void OptimiserSuite::run(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
GasMeter const* _meter,
|
GasMeter const* _meter,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
@ -161,7 +162,7 @@ void OptimiserSuite::run(
|
|||||||
Block& ast = *_object.code;
|
Block& ast = *_object.code;
|
||||||
|
|
||||||
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
|
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
|
||||||
OptimiserStepContext context{_dialect, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
|
OptimiserStepContext context{_dialect, _eofVersion, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
|
||||||
|
|
||||||
OptimiserSuite suite(context, Debug::None);
|
OptimiserSuite suite(context, Debug::None);
|
||||||
|
|
||||||
@ -182,6 +183,7 @@ void OptimiserSuite::run(
|
|||||||
if (!usesOptimizedCodeGenerator)
|
if (!usesOptimizedCodeGenerator)
|
||||||
StackCompressor::run(
|
StackCompressor::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
|
_eofVersion,
|
||||||
_object,
|
_object,
|
||||||
_optimizeStackAllocation,
|
_optimizeStackAllocation,
|
||||||
stackCompressorMaxIterations
|
stackCompressorMaxIterations
|
||||||
@ -202,6 +204,7 @@ void OptimiserSuite::run(
|
|||||||
{
|
{
|
||||||
StackCompressor::run(
|
StackCompressor::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
|
_eofVersion,
|
||||||
_object,
|
_object,
|
||||||
_optimizeStackAllocation,
|
_optimizeStackAllocation,
|
||||||
stackCompressorMaxIterations
|
stackCompressorMaxIterations
|
||||||
|
@ -64,6 +64,7 @@ public:
|
|||||||
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
|
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
|
||||||
static void run(
|
static void run(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
|
std::optional<uint8_t> _eofVersion,
|
||||||
GasMeter const* _meter,
|
GasMeter const* _meter,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
|
@ -39,7 +39,12 @@ string check(string const& _input)
|
|||||||
Object obj;
|
Object obj;
|
||||||
std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false);
|
std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false);
|
||||||
BOOST_REQUIRE(obj.code);
|
BOOST_REQUIRE(obj.code);
|
||||||
auto functions = CompilabilityChecker(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true).stackDeficit;
|
auto functions = CompilabilityChecker(
|
||||||
|
EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()),
|
||||||
|
solidity::test::CommonOptions::get().eofVersion(),
|
||||||
|
obj,
|
||||||
|
true
|
||||||
|
).stackDeficit;
|
||||||
string out;
|
string out;
|
||||||
for (auto const& function: functions)
|
for (auto const& function: functions)
|
||||||
out += function.first.str() + ": " + to_string(function.second) + " ";
|
out += function.first.str() + ": " + to_string(function.second) + " ";
|
||||||
|
@ -208,7 +208,7 @@ TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _
|
|||||||
|
|
||||||
std::ostringstream output;
|
std::ostringstream output;
|
||||||
|
|
||||||
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, *object->code);
|
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, nullopt /* TODO */, *object->code);
|
||||||
|
|
||||||
output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
|
output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
|
||||||
ControlFlowGraphPrinter printer{output};
|
ControlFlowGraphPrinter printer{output};
|
||||||
|
@ -51,7 +51,7 @@ protected:
|
|||||||
|
|
||||||
NameDispenser dispenser(m_dialect, *m_object->code);
|
NameDispenser dispenser(m_dialect, *m_object->code);
|
||||||
std::set<YulString> reserved;
|
std::set<YulString> reserved;
|
||||||
OptimiserStepContext context{m_dialect, dispenser, reserved, 0};
|
OptimiserStepContext context{m_dialect, nullopt /* TODO */, dispenser, reserved, 0};
|
||||||
CommonSubexpressionEliminator::run(context, *m_object->code);
|
CommonSubexpressionEliminator::run(context, *m_object->code);
|
||||||
|
|
||||||
m_ssaValues(*m_object->code);
|
m_ssaValues(*m_object->code);
|
||||||
|
@ -225,7 +225,7 @@ TestCase::TestResult StackLayoutGeneratorTest::run(ostream& _stream, string cons
|
|||||||
|
|
||||||
std::ostringstream output;
|
std::ostringstream output;
|
||||||
|
|
||||||
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, *object->code);
|
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, nullopt /* TODO */, *object->code);
|
||||||
StackLayout stackLayout = StackLayoutGenerator::run(*cfg);
|
StackLayout stackLayout = StackLayoutGenerator::run(*cfg);
|
||||||
|
|
||||||
output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
|
output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
|
||||||
|
@ -323,7 +323,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
|
|||||||
FunctionHoister::run(*m_context, *m_ast);
|
FunctionHoister::run(*m_context, *m_ast);
|
||||||
FunctionGrouper::run(*m_context, *m_ast);
|
FunctionGrouper::run(*m_context, *m_ast);
|
||||||
size_t maxIterations = 16;
|
size_t maxIterations = 16;
|
||||||
StackCompressor::run(*m_dialect, *m_object, true, maxIterations);
|
StackCompressor::run(*m_dialect, nullopt /* TODO */, *m_object, true, maxIterations);
|
||||||
BlockFlattener::run(*m_context, *m_ast);
|
BlockFlattener::run(*m_context, *m_ast);
|
||||||
}},
|
}},
|
||||||
{"wordSizeTransform", [&]() {
|
{"wordSizeTransform", [&]() {
|
||||||
@ -335,6 +335,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
|
|||||||
GasMeter meter(dynamic_cast<EVMDialect const&>(*m_dialect), false, 200);
|
GasMeter meter(dynamic_cast<EVMDialect const&>(*m_dialect), false, 200);
|
||||||
OptimiserSuite::run(
|
OptimiserSuite::run(
|
||||||
*m_dialect,
|
*m_dialect,
|
||||||
|
nullopt, // TODO
|
||||||
&meter,
|
&meter,
|
||||||
*m_object,
|
*m_object,
|
||||||
true,
|
true,
|
||||||
@ -347,6 +348,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
|
|||||||
disambiguate();
|
disambiguate();
|
||||||
StackLimitEvader::run(*m_context, *m_object, CompilabilityChecker{
|
StackLimitEvader::run(*m_context, *m_object, CompilabilityChecker{
|
||||||
*m_dialect,
|
*m_dialect,
|
||||||
|
nullopt, // TODO
|
||||||
*m_object,
|
*m_object,
|
||||||
true
|
true
|
||||||
}.unreachableVariables);
|
}.unreachableVariables);
|
||||||
@ -461,6 +463,7 @@ void YulOptimizerTestCommon::updateContext()
|
|||||||
m_nameDispenser = make_unique<NameDispenser>(*m_dialect, *m_object->code, m_reservedIdentifiers);
|
m_nameDispenser = make_unique<NameDispenser>(*m_dialect, *m_object->code, m_reservedIdentifiers);
|
||||||
m_context = make_unique<OptimiserStepContext>(OptimiserStepContext{
|
m_context = make_unique<OptimiserStepContext>(OptimiserStepContext{
|
||||||
*m_dialect,
|
*m_dialect,
|
||||||
|
nullopt, // TODO
|
||||||
*m_nameDispenser,
|
*m_nameDispenser,
|
||||||
m_reservedIdentifiers,
|
m_reservedIdentifiers,
|
||||||
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
||||||
|
@ -441,6 +441,9 @@ u256 EVMInstructionInterpreter::eval(
|
|||||||
case Instruction::SWAP14:
|
case Instruction::SWAP14:
|
||||||
case Instruction::SWAP15:
|
case Instruction::SWAP15:
|
||||||
case Instruction::SWAP16:
|
case Instruction::SWAP16:
|
||||||
|
case Instruction::CALLF:
|
||||||
|
case Instruction::RETF:
|
||||||
|
case Instruction::JUMPF:
|
||||||
{
|
{
|
||||||
yulAssert(false, "");
|
yulAssert(false, "");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -222,7 +222,7 @@ public:
|
|||||||
{
|
{
|
||||||
Object obj;
|
Object obj;
|
||||||
obj.code = m_ast;
|
obj.code = m_ast;
|
||||||
StackCompressor::run(m_dialect, obj, true, 16);
|
StackCompressor::run(m_dialect, nullopt /* TODO */, obj, true, 16);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -251,6 +251,7 @@ private:
|
|||||||
NameDispenser m_nameDispenser{m_dialect, m_reservedIdentifiers};
|
NameDispenser m_nameDispenser{m_dialect, m_reservedIdentifiers};
|
||||||
OptimiserStepContext m_context{
|
OptimiserStepContext m_context{
|
||||||
m_dialect,
|
m_dialect,
|
||||||
|
nullopt, // TODO
|
||||||
m_nameDispenser,
|
m_nameDispenser,
|
||||||
m_reservedIdentifiers,
|
m_reservedIdentifiers,
|
||||||
solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
||||||
|
@ -195,6 +195,7 @@ unique_ptr<Block> Program::applyOptimisationSteps(
|
|||||||
set<YulString> const externallyUsedIdentifiers = {};
|
set<YulString> const externallyUsedIdentifiers = {};
|
||||||
OptimiserStepContext context{
|
OptimiserStepContext context{
|
||||||
_dialect,
|
_dialect,
|
||||||
|
nullopt, // TODO
|
||||||
_nameDispenser,
|
_nameDispenser,
|
||||||
externallyUsedIdentifiers,
|
externallyUsedIdentifiers,
|
||||||
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
||||||
|
Loading…
Reference in New Issue
Block a user