-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Generate Yul SSA CFG #15359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Generate Yul SSA CFG #15359
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| This file is part of solidity. | ||
|
|
||
| solidity is free software: you can redistribute it and/or modify | ||
| it under the terms of the GNU General Public License as published by | ||
| the Free Software Foundation, either version 3 of the License, or | ||
| (at your option) any later version. | ||
|
|
||
| solidity is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU General Public License for more details. | ||
|
|
||
| You should have received a copy of the GNU General Public License | ||
| along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <libyul/AST.h> | ||
| #include <libyul/Scope.h> | ||
| #include <libyul/backends/evm/SSAControlFlowGraph.h> | ||
|
|
||
| namespace solidity::yul | ||
| { | ||
|
|
||
| struct ControlFlow | ||
| { | ||
| std::unique_ptr<SSACFG> mainGraph{std::make_unique<SSACFG>()}; | ||
| std::vector<std::unique_ptr<SSACFG>> functionGraphs{}; | ||
| std::vector<std::tuple<Scope::Function const*, SSACFG const*>> functionGraphMapping{}; | ||
|
|
||
| SSACFG const* functionGraph(Scope::Function const* _function) | ||
| { | ||
| auto it = std::find_if(functionGraphMapping.begin(), functionGraphMapping.end(), [_function](auto const& tup) { return _function == std::get<0>(tup); }); | ||
| if (it != functionGraphMapping.end()) | ||
| return std::get<1>(*it); | ||
| return nullptr; | ||
| } | ||
| }; | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| /* | ||
| This file is part of solidity. | ||
|
|
||
| solidity is free software: you can redistribute it and/or modify | ||
| it under the terms of the GNU General Public License as published by | ||
| the Free Software Foundation, either version 3 of the License, or | ||
| (at your option) any later version. | ||
|
|
||
| solidity is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU General Public License for more details. | ||
|
|
||
| You should have received a copy of the GNU General Public License | ||
| along with solidity. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| /** | ||
| * Control flow graph and stack layout structures used during code generation. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <libyul/AST.h> | ||
| #include <libyul/AsmAnalysisInfo.h> | ||
| #include <libyul/Dialect.h> | ||
| #include <libyul/Exceptions.h> | ||
| #include <libyul/Scope.h> | ||
|
|
||
| #include <libsolutil/Numeric.h> | ||
|
|
||
| #include <range/v3/view/map.hpp> | ||
| #include <deque> | ||
| #include <functional> | ||
| #include <list> | ||
| #include <vector> | ||
|
|
||
| namespace solidity::yul | ||
| { | ||
|
|
||
| class SSACFG | ||
| { | ||
| public: | ||
| SSACFG() = default; | ||
| SSACFG(SSACFG const&) = delete; | ||
| SSACFG(SSACFG&&) = delete; | ||
| SSACFG& operator=(SSACFG const&) = delete; | ||
| SSACFG& operator=(SSACFG&&) = delete; | ||
| ~SSACFG() = default; | ||
|
|
||
| struct BlockId | ||
| { | ||
| size_t value = std::numeric_limits<size_t>::max(); | ||
| bool operator<(BlockId const& _rhs) const { return value < _rhs.value; } | ||
| bool operator==(BlockId const& _rhs) const { return value == _rhs.value; } | ||
| bool operator!=(BlockId const& _rhs) const { return value != _rhs.value; } | ||
| }; | ||
| struct ValueId | ||
| { | ||
| size_t value = std::numeric_limits<size_t>::max(); | ||
| bool hasValue() const { return value != std::numeric_limits<size_t>::max(); } | ||
| bool operator<(ValueId const& _rhs) const { return value < _rhs.value; } | ||
| bool operator==(ValueId const& _rhs) const { return value == _rhs.value; } | ||
| bool operator!=(ValueId const& _rhs) const { return value != _rhs.value; } | ||
| }; | ||
|
|
||
| struct BuiltinCall | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| std::reference_wrapper<BuiltinFunction const> builtin; | ||
| std::reference_wrapper<FunctionCall const> call; | ||
| }; | ||
| struct Call | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| std::reference_wrapper<Scope::Function const> function; | ||
| std::reference_wrapper<FunctionCall const> call; | ||
| bool const canContinue = true; | ||
| }; | ||
|
|
||
| struct Operation { | ||
| std::vector<ValueId> outputs{}; | ||
| std::variant<BuiltinCall, Call> kind; | ||
| std::vector<ValueId> inputs{}; | ||
| }; | ||
| struct BasicBlock | ||
| { | ||
| struct MainExit {}; | ||
| struct ConditionalJump | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| ValueId condition; | ||
| BlockId nonZero; | ||
| BlockId zero; | ||
| }; | ||
| struct Jump | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| BlockId target; | ||
| }; | ||
| struct JumpTable | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| ValueId value; | ||
| std::map<u256, BlockId> cases; | ||
| BlockId defaultCase; | ||
| }; | ||
| struct FunctionReturn | ||
| { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| std::vector<ValueId> returnValues; | ||
| }; | ||
| struct Terminated {}; | ||
| langutil::DebugData::ConstPtr debugData; | ||
| std::set<BlockId> entries; | ||
| std::set<ValueId> phis; | ||
| std::vector<Operation> operations; | ||
| std::variant<MainExit, Jump, ConditionalJump, JumpTable, FunctionReturn, Terminated> exit = MainExit{}; | ||
| template<typename Callable> | ||
| void forEachExit(Callable&& _callable) const | ||
| { | ||
| if (auto* jump = std::get_if<Jump>(&exit)) | ||
| _callable(jump->target); | ||
| else if (auto* conditionalJump = std::get_if<ConditionalJump>(&exit)) | ||
| { | ||
| _callable(conditionalJump->nonZero); | ||
| _callable(conditionalJump->zero); | ||
| } | ||
| else if (auto* jumpTable = std::get_if<JumpTable>(&exit)) | ||
| { | ||
| for (auto _case: jumpTable->cases | ranges::views::values) | ||
| _callable(_case); | ||
| _callable(jumpTable->defaultCase); | ||
| } | ||
| } | ||
| }; | ||
| BlockId makeBlock(langutil::DebugData::ConstPtr _debugData) | ||
| { | ||
| BlockId blockId { m_blocks.size() }; | ||
| m_blocks.emplace_back(BasicBlock{std::move(_debugData), {}, {}, {}, BasicBlock::Terminated{}}); | ||
| return blockId; | ||
| } | ||
| BasicBlock& block(BlockId _id) { return m_blocks.at(_id.value); } | ||
| BasicBlock const& block(BlockId _id) const { return m_blocks.at(_id.value); } | ||
| size_t numBlocks() const { return m_blocks.size(); } | ||
| private: | ||
| std::vector<BasicBlock> m_blocks; | ||
| public: | ||
| struct LiteralValue { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| u256 value; | ||
| }; | ||
| struct VariableValue { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| BlockId definingBlock; | ||
| }; | ||
| struct PhiValue { | ||
| langutil::DebugData::ConstPtr debugData; | ||
| BlockId block; | ||
| std::vector<ValueId> arguments; | ||
| }; | ||
| struct UnreachableValue {}; | ||
| using ValueInfo = std::variant<UnreachableValue, VariableValue, LiteralValue, PhiValue>; | ||
| ValueInfo& valueInfo(ValueId const _var) | ||
| { | ||
| return m_valueInfos.at(_var.value); | ||
| } | ||
| ValueInfo const& valueInfo(ValueId const _var) const | ||
| { | ||
| return m_valueInfos.at(_var.value); | ||
| } | ||
| ValueId newPhi(BlockId const _definingBlock) | ||
| { | ||
| ValueId id { m_valueInfos.size() }; | ||
| auto block = m_blocks.at(_definingBlock.value); | ||
| m_valueInfos.emplace_back(PhiValue{debugDataOf(block), _definingBlock, {}}); | ||
| return id; | ||
| } | ||
| ValueId newVariable(BlockId const _definingBlock) | ||
| { | ||
| ValueId id { m_valueInfos.size() }; | ||
| auto block = m_blocks.at(_definingBlock.value); | ||
| m_valueInfos.emplace_back(VariableValue{debugDataOf(block), _definingBlock}); | ||
| return id; | ||
| } | ||
| ValueId unreachableValue() | ||
| { | ||
| if (!m_unreachableValue) | ||
| { | ||
| m_unreachableValue = ValueId { m_valueInfos.size() }; | ||
| m_valueInfos.emplace_back(UnreachableValue{}); | ||
| } | ||
| return *m_unreachableValue; | ||
| } | ||
| ValueId newLiteral(langutil::DebugData::ConstPtr _debugData, u256 _value) | ||
| { | ||
| auto [it, inserted] = m_literals.emplace(_value, SSACFG::ValueId{m_valueInfos.size()}); | ||
| if (inserted) | ||
| m_valueInfos.emplace_back(LiteralValue{std::move(_debugData), _value}); | ||
| else | ||
| { | ||
| yulAssert(_value == it->first); | ||
| yulAssert(std::holds_alternative<LiteralValue>(m_valueInfos.at(it->second.value))); | ||
| yulAssert(std::get<LiteralValue>(m_valueInfos.at(it->second.value)).value == _value); | ||
| } | ||
| yulAssert(it->second.value < m_valueInfos.size()); | ||
| return it->second; | ||
| } | ||
| private: | ||
| std::deque<ValueInfo> m_valueInfos; | ||
| std::map<u256, ValueId> m_literals; | ||
| std::optional<ValueId> m_unreachableValue; | ||
| public: | ||
| langutil::DebugData::ConstPtr debugData; | ||
| BlockId entry = BlockId{0}; | ||
| std::set<BlockId> exits; | ||
| Scope::Function const* function = nullptr; | ||
| bool canContinue = true; | ||
| std::vector<std::tuple<std::reference_wrapper<Scope::Variable const>, ValueId>> arguments; | ||
| std::vector<std::reference_wrapper<Scope::Variable const>> returns; | ||
| std::vector<std::reference_wrapper<Scope::Function const>> functions; | ||
r0qs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Container for artificial calls generated for switch statements. | ||
| std::list<FunctionCall> ghostCalls; | ||
| }; | ||
|
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.