Initial still broken version.

This commit is contained in:
Daniel Kirchner 2022-12-14 12:06:27 +01:00
parent 71a860ecf5
commit 1d5dfcdaf0
39 changed files with 398 additions and 66 deletions

View File

@ -52,11 +52,11 @@ AssemblyItem const& Assembly::append(AssemblyItem _i)
{
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
m_deposit += static_cast<int>(_i.deposit());
m_items.emplace_back(std::move(_i));
if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid())
m_items.back().setLocation(m_currentSourceLocation);
m_items.back().m_modifierDepth = m_currentModifierDepth;
return m_items.back();
items().emplace_back(std::move(_i));
if (!items().back().location().isValid() && m_currentSourceLocation.isValid())
items().back().setLocation(m_currentSourceLocation);
items().back().m_modifierDepth = m_currentModifierDepth;
return items().back();
}
unsigned Assembly::codeSize(unsigned subTagSize) const
@ -67,7 +67,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
for (auto const& i: m_data)
ret += i.second.size();
for (AssemblyItem const& i: m_items)
for (AssemblyItem const& i: items())
ret += i.bytesRequired(tagSize, Precision::Approximate);
if (numberEncodingSize(ret) <= tagSize)
return static_cast<unsigned>(ret);
@ -189,7 +189,7 @@ void Assembly::assemblyStream(
{
Functionalizer f(_out, _prefix, _sourceCodes, *this);
for (auto const& i: m_items)
for (auto const& i: items())
f.feed(i, _debugInfoSelection);
f.flush();
@ -227,7 +227,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
Json::Value root;
root[".code"] = Json::arrayValue;
Json::Value& code = root[".code"];
for (AssemblyItem const& item: m_items)
for (AssemblyItem const& item: items())
{
int sourceIndex = -1;
if (item.location().sourceName)
@ -344,6 +344,10 @@ map<u256, u256> const& Assembly::optimiseInternal(
std::set<size_t> _tagsReferencedFromOutside
)
{
if (m_eofVersion.has_value())
// TODO
return *m_tagReplacements;
if (m_tagReplacements)
return *m_tagReplacements;
@ -354,10 +358,10 @@ map<u256, u256> const& Assembly::optimiseInternal(
Assembly& sub = *m_subs[subId];
map<u256, u256> const& subTagReplacements = sub.optimiseInternal(
settings,
JumpdestRemover::referencedTags(m_items, subId)
JumpdestRemover::referencedTags(items(), subId)
);
// Apply the replacements (can be empty).
BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId);
BlockDeduplicator::applyTagReplacement(items(), subTagReplacements, subId);
}
map<u256, u256> tagReplacements;
@ -368,7 +372,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
if (_settings.runInliner)
Inliner{
m_items,
items(),
_tagsReferencedFromOutside,
_settings.expectedExecutionsPerDeployment,
isCreation(),
@ -377,14 +381,14 @@ map<u256, u256> const& Assembly::optimiseInternal(
if (_settings.runJumpdestRemover)
{
JumpdestRemover jumpdestOpt{m_items};
JumpdestRemover jumpdestOpt{items()};
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
count++;
}
if (_settings.runPeephole)
{
PeepholeOptimiser peepOpt{m_items};
PeepholeOptimiser peepOpt{items()};
while (peepOpt.optimise())
{
count++;
@ -395,7 +399,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
// This only modifies PushTags, we have to run again to actually remove code.
if (_settings.runDeduplicate)
{
BlockDeduplicator deduplicator{m_items};
BlockDeduplicator deduplicator{items()};
if (deduplicator.deduplicate())
{
for (auto const& replacement: deduplicator.replacedTags())
@ -425,17 +429,17 @@ map<u256, u256> const& Assembly::optimiseInternal(
// function types that can be stored in storage.
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;
});
auto iter = m_items.begin();
while (iter != m_items.end())
auto iter = items().begin();
while (iter != items().end())
{
KnownState emptyState;
CommonSubexpressionEliminator eliminator{emptyState};
auto orig = iter;
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
iter = eliminator.feedItems(iter, items().end(), usesMSize);
bool shouldReplace = false;
AssemblyItems optimisedChunk;
try
@ -462,9 +466,9 @@ map<u256, u256> const& Assembly::optimiseInternal(
else
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++;
}
}
@ -493,6 +497,8 @@ LinkerObject const& Assembly::assemble() const
LinkerObject& ret = m_assembledObject;
bool const needsEOFContainer = m_eofVersion.has_value();
size_t subTagSize = 1;
map<u256, pair<string, vector<size_t>>> immutableReferencesBySub;
for (auto const& sub: m_subs)
@ -515,7 +521,7 @@ LinkerObject const& Assembly::assemble() const
bool setsImmutables = false;
bool pushesImmutables = false;
for (auto const& i: m_items)
for (auto const& i: items())
if (i.type() == AssignImmutable)
{
i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size());
@ -539,6 +545,7 @@ LinkerObject const& Assembly::assemble() const
unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode);
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());
for (auto const& sub: m_subs)
bytesRequiredIncludingData += static_cast<unsigned>(sub->assemble().bytecode.size());
@ -549,27 +556,55 @@ LinkerObject const& Assembly::assemble() const
// Insert EOF1 header.
bytesRef eofCodeLength(&ret.bytecode.back(), 0);
vector<bytesRef> eofFunctionLengths;
bytesRef eofDataLength(&ret.bytecode.back(), 0);
if (m_eofVersion.has_value())
if (needsEOFContainer)
{
// TODO: empty data is disallowed
ret.bytecode.push_back(0xef);
ret.bytecode.push_back(0x00);
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(0x00); // length of code
ret.bytecode.push_back(0x00);
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(0x00); // length of data
ret.bytecode.push_back(0x00);
eofDataLength = bytesRef(&ret.bytecode.back() + 1 - 2, 2);
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();
for (AssemblyItem const& i: m_items)
auto assembleItems = [&](AssemblyItems const& _items) {
for (AssemblyItem const& i: _items)
{
// store position of the invalid jump destination
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));
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:
assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling.");
}
}
};
assembleItems(items());
if (!immutableReferencesBySub.empty())
throw
@ -697,17 +747,26 @@ LinkerObject const& Assembly::assemble() const
"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.
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::INVALID));
auto const codeLength = ret.bytecode.size() - codeStart;
if (m_eofVersion.has_value())
if (needsEOFContainer)
{
assertThrow(codeLength > 0 && codeLength <= 0xffff, AssemblyException, "Invalid code section size.");
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();
for (auto const& [subIdPath, bytecodeOffset]: subRef)
@ -738,7 +797,7 @@ LinkerObject const& Assembly::assemble() const
{
size_t position = m_tagPositionsInBytecode.at(tagInfo.id);
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)
{
tagIndex = index;
@ -775,7 +834,7 @@ LinkerObject const& Assembly::assemble() const
}
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.
if (dataLength == 0)

View File

@ -51,13 +51,46 @@ class Assembly
public:
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 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.
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); }
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); }
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& sub(size_t _sub) { return *m_subs.at(_sub); }
size_t numSubs() const { return m_subs.size(); }
@ -83,6 +116,15 @@ public:
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 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; }
@ -99,10 +141,22 @@ public:
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
/// 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!
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; }
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
@ -189,11 +243,12 @@ protected:
};
std::map<std::string, NamedTagInfo> m_namedTags;
AssemblyItems m_items;
AssemblyItems m_mainItems;
std::map<util::h256, bytes> m_data;
/// Data that is appended to the very end of the contract.
bytes m_auxiliaryData;
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_libraries; ///< Identifiers of libraries to be linked.
std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.
@ -213,6 +268,7 @@ protected:
/// True, if the assembly contains contract creation code.
bool const m_creation = false;
std::optional<uint8_t> m_eofVersion;
std::optional<uint16_t> m_currentFunctionID;
/// Internal name of the assembly object, only used with the Yul backend
/// currently
std::string m_name;

View File

@ -159,6 +159,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
}
case VerbatimBytecode:
return std::get<2>(*m_verbatimBytecode).size();
case CallF:
return 3;
case RetF:
return 1;
default:
break;
}
@ -173,6 +177,15 @@ size_t AssemblyItem::arguments() const
return get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable)
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
return 0;
}
@ -197,6 +210,11 @@ size_t AssemblyItem::returnValues() const
return 0;
case 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:
break;
}
@ -309,6 +327,12 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
case VerbatimBytecode:
text = string("verbatimbytecode_") + util::toHex(get<2>(*m_verbatimBytecode));
break;
case CallF:
text = "callf(" + to_string(static_cast<size_t>(data())) + ")";
break;
case RetF:
text = "retf";
break;
default:
assertThrow(false, InvalidOpcode, "");
}
@ -335,6 +359,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
case Push:
_out << " PUSH " << hex << _item.data() << dec;
break;
case CallF:
_out << " CALLF " << dec << _item.data();
break;
case RetF:
_out << " RETF";
break;
case PushTag:
{
size_t subId = _item.splitForeignPushTag().first;

View File

@ -50,7 +50,9 @@ enum AssemblyItemType
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.
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 };
@ -85,6 +87,16 @@ public:
m_instruction{},
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&&) = default;
@ -191,6 +203,7 @@ private:
AssemblyItemType m_type;
Instruction m_instruction; ///< 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
/// return variables and verbatim bytecode.
std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode;

View File

@ -175,6 +175,10 @@ enum class Instruction: uint8_t
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.
CALLF = 0xb0,
RETF,
JUMPF,
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only

View File

@ -160,6 +160,8 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
case PushDeployTimeAddress:
case AssignImmutable:
case VerbatimBytecode:
case CallF:
case RetF:
return true;
case Push:
case PushTag:

View File

@ -543,6 +543,7 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
yul::OptimiserSuite::run(
_dialect,
assembly().eofVersion(),
&meter,
_object,
_optimiserSettings.optimizeStackAllocation,

View File

@ -33,6 +33,7 @@ using namespace solidity::util;
CompilabilityChecker::CompilabilityChecker(
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
Object const& _object,
bool _optimizeStackAllocation
)
@ -50,7 +51,7 @@ CompilabilityChecker::CompilabilityChecker(
builtinContext.subIDs[_object.name] = 1;
for (auto const& subNode: _object.subObjects)
builtinContext.subIDs[subNode->name] = 1;
NoOutputAssembly assembly;
NoOutputAssembly assembly(_eofVersion.has_value());
CodeTransform transform(
assembly,
analysisInfo,

View File

@ -44,7 +44,12 @@ namespace solidity::yul
*/
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, int> stackDeficit;
};

View File

@ -184,6 +184,7 @@ void YulStack::optimize(Object& _object, bool _isCreation)
meter = make_unique<GasMeter>(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
OptimiserSuite::run(
dialect,
m_eofVersion,
meter.get(),
_object,
m_optimiserSettings.optimizeStackAllocation,

View File

@ -55,10 +55,12 @@ class AbstractAssembly
public:
using LabelID = size_t;
using SubID = size_t;
using FunctionID = uint16_t;
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
virtual ~AbstractAssembly() = default;
virtual bool supportsFunctions() const = 0;
/// Set a new source location valid starting from the next instruction.
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
/// Retrieve the current height of the stack. This does not have to be zero
@ -99,6 +101,14 @@ public:
virtual void appendAssemblySize() = 0;
/// 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 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.
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
/// Appends the size of the given sub-assembly or data.

View File

@ -123,7 +123,7 @@ inline bool canBeFreelyGenerated(StackSlot const& _slot)
/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow.
struct CFG
{
explicit CFG() {}
explicit CFG(bool _useFunctions): useFunctions(_useFunctions) {}
CFG(CFG const&) = delete;
CFG(CFG&&) = delete;
CFG& operator=(CFG const&) = delete;
@ -220,6 +220,7 @@ struct CFG
bool canContinue = true;
};
bool useFunctions = false;
/// The main entry point, i.e. the start of the outermost Yul block.
BasicBlock* entry = nullptr;
/// Subgraphs for functions.

View File

@ -209,10 +209,11 @@ void markNeedsCleanStack(CFG& _cfg)
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
Block const& _block
)
{
auto result = std::make_unique<CFG>();
auto result = std::make_unique<CFG>(_eofVersion.has_value());
result->entry = &result->makeBlock(debugDataOf(_block));
ControlFlowSideEffectsCollector sideEffects(_dialect, _block);
@ -541,7 +542,7 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
Scope::Function const& function = lookupFunction(_call.functionName.name);
canContinue = m_graph.functionInfo.at(&function).canContinue;
Stack inputs;
if (canContinue)
if (!m_graph.useFunctions && canContinue)
inputs.emplace_back(FunctionCallReturnLabelSlot{_call});
for (auto const& arg: _call.arguments | ranges::views::reverse)
inputs.emplace_back(std::visit(*this, arg));

View File

@ -31,7 +31,12 @@ class ControlFlowGraphBuilder
public:
ControlFlowGraphBuilder(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()(Literal const& _literal);
@ -81,6 +86,7 @@ private:
AsmAnalysisInfo const& m_info;
std::map<FunctionDefinition const*, ControlFlowSideEffects> const& m_functionSideEffects;
Dialect const& m_dialect;
std::optional<uint8_t> m_eofVersion;
CFG::BasicBlock* m_currentBlock = nullptr;
Scope* m_scope = nullptr;
struct ForLoopInfo

View File

@ -22,6 +22,7 @@
#pragma once
#include <libyul/Dialect.h>
#include <libyul/Scope.h>
#include <libyul/backends/evm/AbstractAssembly.h>
#include <libyul/ASTForward.h>
@ -46,6 +47,8 @@ struct BuiltinContext
Object const* currentObject = nullptr;
/// Mapping from named objects to abstract assembly sub IDs.
std::map<YulString, AbstractAssembly::SubID> subIDs;
std::map<Scope::Function const*, AbstractAssembly::FunctionID> functionIDs;
};
struct BuiltinFunctionForEVM: public BuiltinFunction

View File

@ -86,6 +86,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
*_object.analysisInfo,
*_object.code,
m_dialect,
m_eofVersion,
context,
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
);

View File

@ -44,6 +44,11 @@ EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly):
{
}
bool EthAssemblyAdapter::supportsFunctions() const
{
return m_assembly.supportsFunctions();
}
void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _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())};
}
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)
{
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())

View File

@ -40,6 +40,7 @@ class EthAssemblyAdapter: public AbstractAssembly
{
public:
explicit EthAssemblyAdapter(evmasm::Assembly& _assembly);
bool supportsFunctions() const override;
void setSourceLocation(langutil::SourceLocation const& _location) override;
int stackHeight() const override;
void setStackHeight(int height) override;
@ -56,6 +57,11 @@ public:
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
void appendAssemblySize() 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 appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;

View File

@ -104,6 +104,33 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::cr
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&)
{
appendInstruction(evmasm::Instruction::PUSH1);

View File

@ -37,6 +37,13 @@ struct SourceLocation;
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.
@ -45,9 +52,13 @@ namespace solidity::yul
class NoOutputAssembly: public AbstractAssembly
{
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;
bool supportsFunctions() const override { return m_hasFunctions; }
void setSourceLocation(langutil::SourceLocation const&) override {}
int stackHeight() const override { return m_stackHeight; }
void setStackHeight(int height) override { m_stackHeight = height; }
@ -66,6 +77,11 @@ public:
void appendAssemblySize() 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 appendDataSize(std::vector<SubID> const& _subPath) override;
SubID appendData(bytes const& _data) override;
@ -78,6 +94,8 @@ public:
void markAsInvalid() override {}
private:
bool m_hasFunctions = false;
std::shared_ptr<NoOutputAssemblyContext> m_context;
int m_stackHeight = 0;
};

View File

@ -45,12 +45,28 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
AsmAnalysisInfo& _analysisInfo,
Block const& _block,
EVMDialect const& _dialect,
std::optional<uint8_t> _eofVersion,
BuiltinContext& _builtinContext,
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);
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(
_assembly,
_builtinContext,
@ -68,10 +84,11 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
{
bool useReturnLabel = !m_dfg.useFunctions && _call.canContinue;
// Validate stack.
{
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.
for (auto&& [arg, slot]: ranges::zip_view(
_call.functionCall.get().arguments | ranges::views::reverse,
@ -79,7 +96,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
))
validateSlot(slot, arg);
// Assert that we got the correct return label on stack.
if (_call.canContinue)
if (useReturnLabel)
{
auto const* returnLabelSlot = get_if<FunctionCallReturnLabelSlot>(
&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.
{
m_assembly.setSourceLocation(originLocationOf(_call));
if (m_dfg.useFunctions)
m_assembly.appendFunctionCall(m_builtinContext.functionIDs.at(&_call.function.get()));
else
m_assembly.appendJumpTo(
getFunctionLabel(_call.function),
static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - (_call.canContinue ? 1 : 0),
AbstractAssembly::JumpType::IntoFunction
);
if (_call.canContinue)
if (useReturnLabel)
m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get()));
}
// Update 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();
// Push return values to m_stack.
for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size()))
@ -184,7 +204,7 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
m_builtinContext(_builtinContext),
m_dfg(_dfg),
m_stackLayout(_stackLayout),
m_functionLabels([&](){
m_functionLabels(_dfg.useFunctions ? decltype(m_functionLabels)() : [&](){
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
set<YulString> assignedFunctionNames;
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)
{
yulAssert(!m_dfg.useFunctions);
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
}
@ -491,10 +512,14 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
return StackSlot{_varSlot};
}) | ranges::to<Stack>;
if (!m_dfg.useFunctions)
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
// Create the function return layout and jump.
createStackLayout(debugDataOf(_functionReturn), exitStack);
if (m_dfg.useFunctions)
m_assembly.appendFunctionReturn();
else
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
},
[&](CFG::BasicBlock::Terminated const&)
@ -516,19 +541,23 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
{
bool useReturnLabel = !m_dfg.useFunctions && _functionInfo.canContinue;
yulAssert(!m_currentFunctionInfo, "");
ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);
yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, "");
// Create function entry layout in m_stack.
if (_functionInfo.canContinue)
if (useReturnLabel)
m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
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.setSourceLocation(originLocationOf(_functionInfo));
if (!m_dfg.useFunctions)
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
// Create the entry layout of the function body block and visit.
@ -536,5 +565,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInf
(*this)(*_functionInfo.entry);
m_stack.clear();
if (m_dfg.useFunctions)
m_assembly.endFunction();
m_assembly.setStackHeight(0);
}

View File

@ -52,6 +52,7 @@ public:
AsmAnalysisInfo& _analysisInfo,
Block const& _block,
EVMDialect const& _dialect,
std::optional<uint8_t> _eofVersion,
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions
);

View File

@ -50,7 +50,7 @@ using namespace std;
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
{
StackLayout stackLayout;
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry);
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)
{
StackLayout stackLayout;
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
CFG::FunctionInfo const* functionInfo = nullptr;
if (!_functionName.empty())
{
@ -463,6 +463,8 @@ optional<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
return StackSlot{_varSlot};
}) | ranges::to<Stack>;
if (!m_layout.useFunctions)
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
return stack;
},

View File

@ -37,6 +37,7 @@ struct StackLayout
/// The resulting stack layout after executing the block.
Stack exitLayout;
};
bool useFunctions = false;
std::map<CFG::BasicBlock const*, BlockInfo> blockInfos;
/// For each operation the complete stack layout that:
/// - has the slots required for the operation at the stack top.

View File

@ -74,6 +74,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
// expectedExecutionsPerDeployment is currently unused.
OptimiserStepContext context{
m_dialect,
nullopt,
nameDispenser,
reservedIdentifiers,
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment

View File

@ -35,6 +35,7 @@ class NameDispenser;
struct OptimiserStepContext
{
Dialect const& dialect;
std::optional<uint8_t> eofVersion;
NameDispenser& dispenser;
std::set<YulString> const& reservedIdentifiers;
/// The value nullopt represents creation code

View File

@ -238,6 +238,7 @@ void eliminateVariablesOptimizedCodegen(
bool StackCompressor::run(
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
Object& _object,
bool _optimizeStackAllocation,
size_t _maxIterations
@ -258,7 +259,7 @@ bool StackCompressor::run(
if (usesOptimizedCodeGenerator)
{
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(
_dialect,
*_object.code,
@ -269,7 +270,7 @@ bool StackCompressor::run(
else
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())
return true;
eliminateVariables(

View File

@ -48,6 +48,7 @@ public:
/// @returns true if it was successful.
static bool run(
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
Object& _object,
bool _optimizeStackAllocation,
size_t _maxIterations

View File

@ -131,12 +131,13 @@ void StackLimitEvader::run(
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
{
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));
}
else
run(_context, _object, CompilabilityChecker{
_context.dialect,
_context.eofVersion,
_object,
true
}.unreachableVariables);

View File

@ -135,6 +135,7 @@ void outputPerformanceMetrics(map<string, int64_t> const& _metrics)
void OptimiserSuite::run(
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
GasMeter const* _meter,
Object& _object,
bool _optimizeStackAllocation,
@ -161,7 +162,7 @@ void OptimiserSuite::run(
Block& ast = *_object.code;
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
OptimiserStepContext context{_dialect, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
OptimiserStepContext context{_dialect, _eofVersion, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
OptimiserSuite suite(context, Debug::None);
@ -182,6 +183,7 @@ void OptimiserSuite::run(
if (!usesOptimizedCodeGenerator)
StackCompressor::run(
_dialect,
_eofVersion,
_object,
_optimizeStackAllocation,
stackCompressorMaxIterations
@ -202,6 +204,7 @@ void OptimiserSuite::run(
{
StackCompressor::run(
_dialect,
_eofVersion,
_object,
_optimizeStackAllocation,
stackCompressorMaxIterations

View File

@ -64,6 +64,7 @@ public:
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
static void run(
Dialect const& _dialect,
std::optional<uint8_t> _eofVersion,
GasMeter const* _meter,
Object& _object,
bool _optimizeStackAllocation,

View File

@ -39,7 +39,12 @@ string check(string const& _input)
Object obj;
std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false);
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;
for (auto const& function: functions)
out += function.first.str() + ": " + to_string(function.second) + " ";

View File

@ -208,7 +208,7 @@ TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _
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";
ControlFlowGraphPrinter printer{output};

View File

@ -51,7 +51,7 @@ protected:
NameDispenser dispenser(m_dialect, *m_object->code);
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);
m_ssaValues(*m_object->code);

View File

@ -225,7 +225,7 @@ TestCase::TestResult StackLayoutGeneratorTest::run(ostream& _stream, string cons
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);
output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";

View File

@ -323,7 +323,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
FunctionHoister::run(*m_context, *m_ast);
FunctionGrouper::run(*m_context, *m_ast);
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);
}},
{"wordSizeTransform", [&]() {
@ -335,6 +335,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
GasMeter meter(dynamic_cast<EVMDialect const&>(*m_dialect), false, 200);
OptimiserSuite::run(
*m_dialect,
nullopt, // TODO
&meter,
*m_object,
true,
@ -347,6 +348,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
disambiguate();
StackLimitEvader::run(*m_context, *m_object, CompilabilityChecker{
*m_dialect,
nullopt, // TODO
*m_object,
true
}.unreachableVariables);
@ -461,6 +463,7 @@ void YulOptimizerTestCommon::updateContext()
m_nameDispenser = make_unique<NameDispenser>(*m_dialect, *m_object->code, m_reservedIdentifiers);
m_context = make_unique<OptimiserStepContext>(OptimiserStepContext{
*m_dialect,
nullopt, // TODO
*m_nameDispenser,
m_reservedIdentifiers,
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment

View File

@ -441,6 +441,9 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::SWAP14:
case Instruction::SWAP15:
case Instruction::SWAP16:
case Instruction::CALLF:
case Instruction::RETF:
case Instruction::JUMPF:
{
yulAssert(false, "");
return 0;

View File

@ -222,7 +222,7 @@ public:
{
Object obj;
obj.code = m_ast;
StackCompressor::run(m_dialect, obj, true, 16);
StackCompressor::run(m_dialect, nullopt /* TODO */, obj, true, 16);
break;
}
default:
@ -251,6 +251,7 @@ private:
NameDispenser m_nameDispenser{m_dialect, m_reservedIdentifiers};
OptimiserStepContext m_context{
m_dialect,
nullopt, // TODO
m_nameDispenser,
m_reservedIdentifiers,
solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment

View File

@ -195,6 +195,7 @@ unique_ptr<Block> Program::applyOptimisationSteps(
set<YulString> const externallyUsedIdentifiers = {};
OptimiserStepContext context{
_dialect,
nullopt, // TODO
_nameDispenser,
externallyUsedIdentifiers,
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment