Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions libsolutil/DisjointSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

using namespace solidity::util;

ContiguousDisjointSet::ContiguousDisjointSet(size_t const _numNodes):
template<typename ValueType>
ContiguousDisjointSet<ValueType>::ContiguousDisjointSet(size_t const _numNodes):
m_parents(_numNodes),
m_neighbors(_numNodes),
m_sizes(_numNodes, static_cast<value_type>(1)),
Expand All @@ -35,9 +36,11 @@ ContiguousDisjointSet::ContiguousDisjointSet(size_t const _numNodes):
std::iota(m_neighbors.begin(), m_neighbors.end(), 0);
}

size_t ContiguousDisjointSet::numSets() const { return m_numSets; }
template<typename ValueType>
size_t ContiguousDisjointSet<ValueType>::numSets() const { return m_numSets; }

ContiguousDisjointSet::value_type ContiguousDisjointSet::find(value_type const _element) const
template<typename ValueType>
typename ContiguousDisjointSet<ValueType>::value_type ContiguousDisjointSet<ValueType>::find(value_type const _element) const
{
solAssert(_element < m_parents.size());
// path halving
Expand All @@ -50,7 +53,8 @@ ContiguousDisjointSet::value_type ContiguousDisjointSet::find(value_type const _
return rootElement;
}

void ContiguousDisjointSet::merge(value_type const _x, value_type const _y, bool const _mergeBySize)
template<typename ValueType>
void ContiguousDisjointSet<ValueType>::merge(value_type const _x, value_type const _y, bool const _mergeBySize)
{
auto xRoot = find(_x);
auto yRoot = find(_y);
Expand All @@ -69,17 +73,20 @@ void ContiguousDisjointSet::merge(value_type const _x, value_type const _y, bool
--m_numSets;
}

bool ContiguousDisjointSet::sameSubset(value_type const _x, value_type const _y) const
template<typename ValueType>
bool ContiguousDisjointSet<ValueType>::sameSubset(value_type const _x, value_type const _y) const
{
return find(_x) == find(_y);
}

ContiguousDisjointSet::size_type ContiguousDisjointSet::sizeOfSubset(value_type const _x) const
template<typename ValueType>
typename ContiguousDisjointSet<ValueType>::size_type ContiguousDisjointSet<ValueType>::sizeOfSubset(value_type const _x) const
{
return m_sizes[find(_x)];
}

std::set<ContiguousDisjointSet::value_type> ContiguousDisjointSet::subset(value_type const _x) const
template<typename ValueType>
std::set<typename ContiguousDisjointSet<ValueType>::value_type> ContiguousDisjointSet<ValueType>::subset(value_type const _x) const
{
solAssert(_x < m_parents.size());
std::set<value_type> result{_x};
Expand All @@ -92,7 +99,8 @@ std::set<ContiguousDisjointSet::value_type> ContiguousDisjointSet::subset(value_
return result;
}

std::vector<std::set<ContiguousDisjointSet::value_type>> ContiguousDisjointSet::subsets() const
template<typename ValueType>
std::vector<std::set<typename ContiguousDisjointSet<ValueType>::value_type>> ContiguousDisjointSet<ValueType>::subsets() const
{
std::vector<std::set<value_type>> result;
std::vector<std::uint8_t> visited(m_parents.size(), false);
Expand All @@ -107,3 +115,5 @@ std::vector<std::set<ContiguousDisjointSet::value_type>> ContiguousDisjointSet::
}
return result;
}

template class solidity::util::ContiguousDisjointSet<std::uint32_t>;
3 changes: 2 additions & 1 deletion libsolutil/DisjointSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ namespace solidity::util
/// [1] https://en.wikipedia.org/wiki/Disjoint-set_data_structure
/// [2] Tarjan, Robert E., and Jan Van Leeuwen. "Worst-case analysis of set union algorithms."
/// Journal of the ACM (JACM) 31.2 (1984): 245-281.
template<typename ValueType>
class ContiguousDisjointSet
{
public:
using size_type = size_t;
using value_type = size_t;
using value_type = ValueType;

/// Constructs a new disjoint set datastructure with `_numNodes` elements and each element in its own individual set
explicit ContiguousDisjointSet(size_t _numNodes);
Expand Down
3 changes: 1 addition & 2 deletions libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,7 @@ Json YulStack::cfgJson() const
keepLiteralAssignments
);
std::unique_ptr<ssa::ControlFlowLiveness> liveness = std::make_unique<ssa::ControlFlowLiveness>(*controlFlow);
ssa::SSACFGJsonExporter exporter(*controlFlow, liveness.get());
return exporter.run();
return ssa::json::exportControlFlow(*controlFlow, liveness.get());
};

std::function<Json(std::vector<std::shared_ptr<ObjectNode>>)> exportCFGFromSubObjects;
Expand Down
3 changes: 1 addition & 2 deletions libyul/backends/evm/ssa/ControlFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ using namespace solidity::yul::ssa;

ControlFlowLiveness::ControlFlowLiveness(ControlFlow const& _controlFlow):
controlFlow(_controlFlow),
mainLiveness(std::make_unique<LivenessAnalysis>(*_controlFlow.mainGraph)),
functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique<LivenessAnalysis>(*_cfg); }) | ranges::to<std::vector>)
cfgLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique<LivenessAnalysis>(*_cfg); }) | ranges::to<std::vector>)
{ }

std::string ControlFlowLiveness::toDot() const
Expand Down
30 changes: 20 additions & 10 deletions libyul/backends/evm/ssa/ControlFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <libyul/AST.h>
#include <libyul/Scope.h>

#include <range/v3/algorithm/find_if.hpp>

namespace solidity::yul::ssa
{

Expand All @@ -33,44 +35,52 @@ struct ControlFlowLiveness{
explicit ControlFlowLiveness(ControlFlow const& _controlFlow);

std::reference_wrapper<ControlFlow const> controlFlow;
std::unique_ptr<LivenessAnalysis> mainLiveness;
std::vector<std::unique_ptr<LivenessAnalysis>> functionLiveness;
std::vector<std::unique_ptr<LivenessAnalysis>> cfgLiveness;

std::string toDot() const;
};

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{};
using FunctionGraphID = std::uint32_t;

SSACFG const* functionGraph(Scope::Function const* _function)
static FunctionGraphID constexpr mainGraphID() noexcept { return 0; }

SSACFG const* mainGraph() const { return functionGraph(mainGraphID()); }

SSACFG const* functionGraph(Scope::Function const* _function) const
{
auto it = std::find_if(functionGraphMapping.begin(), functionGraphMapping.end(), [_function](auto const& tup) { return _function == std::get<0>(tup); });
auto it = ranges::find_if(functionGraphMapping, [_function](auto const& tup) { return _function == std::get<0>(tup); });
if (it != functionGraphMapping.end())
return std::get<1>(*it);
return nullptr;
}

SSACFG const* functionGraph(FunctionGraphID const _id) const
{
return functionGraphs.at(_id).get();
}

std::string toDot(ControlFlowLiveness const* _liveness=nullptr) const
{
if (_liveness)
yulAssert(&_liveness->controlFlow.get() == this);
std::ostringstream output;
output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n";
output << mainGraph->toDot(false, std::nullopt, _liveness ? _liveness->mainLiveness.get() : nullptr);

for (size_t index=0; index < functionGraphs.size(); ++index)
output << functionGraphs[index]->toDot(
false,
index+1,
_liveness ? _liveness->functionLiveness[index].get() : nullptr
index,
_liveness ? _liveness->cfgLiveness[index].get() : nullptr
);

output << "}\n";
return output.str();
}

std::vector<std::unique_ptr<SSACFG>> functionGraphs{};
std::vector<std::tuple<Scope::Function const*, SSACFG const*>> functionGraphMapping{};
};

}
46 changes: 19 additions & 27 deletions libyul/backends/evm/ssa/LivenessAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,11 @@ using namespace solidity::yul::ssa;

namespace
{
constexpr auto excludingLiteralsFilter(SSACFG const& _cfg)
constexpr auto excludingLiteralsFilter()
{
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
return [](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
{
return !std::holds_alternative<SSACFG::LiteralValue>(_cfg.valueInfo(_valueId));
};
}
constexpr auto unreachableFilter(SSACFG const& _cfg)
{
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
{
return std::holds_alternative<SSACFG::UnreachableValue>(_cfg.valueInfo(_valueId));
return !_valueId.isLiteral();
};
}
}
Expand Down Expand Up @@ -149,18 +142,18 @@ LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId
[](SSACFG::BasicBlock::MainExit const&) {},
[&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn)
{
for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter()))
result.insert(valueId);
},
[&](SSACFG::BasicBlock::JumpTable const& _jt)
{
if (excludingLiteralsFilter(m_cfg)(_jt.value))
if (excludingLiteralsFilter()(_jt.value))
result.insert(_jt.value);
},
[](SSACFG::BasicBlock::Jump const&) {},
[&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump)
{
if (excludingLiteralsFilter(m_cfg)(_conditionalJump.condition))
if (excludingLiteralsFilter()(_conditionalJump.condition))
result.insert(_conditionalJump.condition);
},
[](SSACFG::BasicBlock::Terminated const&) {}};
Expand Down Expand Up @@ -218,12 +211,11 @@ void LivenessAnalysis::runDagDfs()
{
for (auto const& phi: m_cfg.block(_successor).phis)
{
auto const& info = m_cfg.valueInfo(phi);
yulAssert(std::holds_alternative<SSACFG::PhiValue>(info), "value info of phi wasn't PhiValue");
auto const argIndex = m_cfg.phiArgumentIndex(blockId, _successor);
yulAssert(argIndex < std::get<SSACFG::PhiValue>(info).arguments.size());
auto const arg = std::get<SSACFG::PhiValue>(info).arguments.at(argIndex);
if (!std::holds_alternative<SSACFG::LiteralValue>(m_cfg.valueInfo(arg)))
auto const& info = m_cfg.phiInfo(phi);
auto const& argIndex = m_cfg.phiArgumentIndex(blockId, _successor);
yulAssert(argIndex < info.arguments.size());
auto const& arg = info.arguments.at(argIndex);
if (!arg.isLiteral())
live.insert(arg);
}
});
Expand All @@ -242,11 +234,11 @@ void LivenessAnalysis::runDagDfs()
});

if (std::holds_alternative<SSACFG::BasicBlock::FunctionReturn>(block.exit))
for (auto const& returnValue: std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
for (auto const& returnValue: std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter()))
live.insert(returnValue);

// clean out unreachables
live.eraseIf([&](auto const& _entry) { return unreachableFilter(m_cfg)(_entry.first); });
live.eraseIf([&](auto const& _entry) { return _entry.first.isUnreachable(); });

// LiveOut(B) <- live
m_liveOuts[blockId.value] = live;
Expand All @@ -259,9 +251,9 @@ void LivenessAnalysis::runDagDfs()
for (auto const& op: block.operations | ranges::views::reverse)
{
// remove variables defined at p from live
live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to<std::vector>);
// add uses at p to live
live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to<std::vector>);
}
}

Expand All @@ -272,7 +264,7 @@ void LivenessAnalysis::runDagDfs()
}
}

void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
void LivenessAnalysis::runLoopTreeDfs(SSACFG::BlockId::ValueType const _loopHeader)
{
// SSA Book, Algorithm 9.3
if (m_loopNestingForest.loopNodes().contains(_loopHeader))
Expand All @@ -286,7 +278,7 @@ void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
// must be live out of header if live in of children
m_liveOuts[_loopHeader].maxUnion(liveLoop);
// for each blockId \in children(loopHeader)
for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
for (SSACFG::BlockId::ValueType blockIdValue = 0u; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader)
{
// propagate loop liveness information down to the loop header's children
Expand All @@ -313,9 +305,9 @@ void LivenessAnalysis::fillOperationsLiveOut()
for (auto const& op: operations | ranges::views::reverse)
{
*rit = live;
for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter()))
live.erase(output);
for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter()))
live.insert(input);
++rit;
}
Expand Down
5 changes: 2 additions & 3 deletions libyul/backends/evm/ssa/LivenessAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ class LivenessAnalysis

LivenessData() = default;
template<std::input_iterator Iter, std::sentinel_for<Iter> Sentinel>
LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) {
}
LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) {}
explicit LivenessData(LiveCounts&& _liveCounts): m_liveCounts(std::move(_liveCounts)) {}

bool contains(Value const& _valueId) const;
Expand Down Expand Up @@ -113,7 +112,7 @@ class LivenessAnalysis

private:
void runDagDfs();
void runLoopTreeDfs(std::size_t _loopHeader);
void runLoopTreeDfs(SSACFG::BlockId::ValueType _loopHeader);
void fillOperationsLiveOut();
LivenessData blockExitValues(SSACFG::BlockId const& _blockId) const;

Expand Down
Loading