From e8a2c63214457e7e2c4058849d259407964b9a44 Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Mon, 9 Sep 2024 11:38:21 +0200
Subject: [PATCH 001/394] Yul: Add SSACFG liveness analysis
---
libyul/CMakeLists.txt | 2 +
libyul/backends/evm/SSACFGLiveness.cpp | 132 +++++++++++++++++++++++++
libyul/backends/evm/SSACFGLiveness.h | 55 +++++++++++
3 files changed, 189 insertions(+)
create mode 100644 libyul/backends/evm/SSACFGLiveness.cpp
create mode 100644 libyul/backends/evm/SSACFGLiveness.h
diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index f621b2089e9b..4265b91d3f3e 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -65,6 +65,8 @@ add_library(yul
backends/evm/NoOutputAssembly.cpp
backends/evm/OptimizedEVMCodeTransform.cpp
backends/evm/OptimizedEVMCodeTransform.h
+ backends/evm/SSACFGLiveness.cpp
+ backends/evm/SSACFGLiveness.h
backends/evm/SSACFGLoopNestingForest.cpp
backends/evm/SSACFGLoopNestingForest.h
backends/evm/SSACFGTopologicalSort.cpp
diff --git a/libyul/backends/evm/SSACFGLiveness.cpp b/libyul/backends/evm/SSACFGLiveness.cpp
new file mode 100644
index 000000000000..6ed926304db2
--- /dev/null
+++ b/libyul/backends/evm/SSACFGLiveness.cpp
@@ -0,0 +1,132 @@
+/*
+ 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 .
+*/
+// SPDX-License-Identifier: GPL-3.0
+
+#include
+
+#include
+#include
+#include
+
+using namespace solidity::yul;
+
+SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg):
+ m_cfg(_cfg),
+ m_topologicalSort(_cfg),
+ m_loopNestingForest(m_topologicalSort),
+ m_liveIns(_cfg.numBlocks()),
+ m_liveOuts(_cfg.numBlocks())
+{
+ runDagDfs();
+ for (auto const loopRootNode: m_loopNestingForest.loopRootNodes())
+ runLoopTreeDfs(loopRootNode);
+}
+void SSACFGLiveness::runDagDfs()
+{
+ // SSA Book, Algorithm 9.2
+ for(auto const blockIdValue: m_topologicalSort.postOrder())
+ {
+ // post-order traversal
+ SSACFG::BlockId blockId{blockIdValue};
+ auto const filterLiterals = [this](auto const& valueId){
+ return !std::holds_alternative(m_cfg.valueInfo(valueId));
+ };
+ auto const& block = m_cfg.block(blockId);
+
+ // live <- PhiUses(B)
+ std::set live{};
+ block.forEachExit(
+ [&](SSACFG::BlockId const& _successor)
+ {
+ for (auto const& phi: m_cfg.block(_successor).phis)
+ {
+ auto const& info = m_cfg.valueInfo(phi);
+ yulAssert(std::holds_alternative(info), "value info of phi wasn't PhiValue");
+ auto const& entries = m_cfg.block(std::get(info).block).entries;
+ // this is getting the argument index of the phi function corresponding to the path going
+ // through "blockId", ie, the currently handled block
+ auto const it = entries.find(blockId);
+ yulAssert(it != entries.end());
+ auto const argIndex = static_cast(std::distance(entries.begin(), it));
+ if (argIndex < std::get(info).arguments.size())
+ {
+ // todo not sure why arg index would exceed phi arg length but it does happen
+ auto const arg = std::get(info).arguments.at(argIndex);
+ if (!std::holds_alternative(m_cfg.valueInfo(arg)))
+ live.insert(arg);
+ }
+ else
+ yulAssert(false);
+ }
+ });
+
+ // for each S \in succs(B) s.t. (B, S) not a back edge: live <- live \cup (LiveIn(S) - PhiDefs(S))
+ block.forEachExit(
+ [&](SSACFG::BlockId const& _successor) {
+ if (!m_topologicalSort.backEdge(blockId, _successor))
+ live += m_liveIns[_successor.value] - m_cfg.block(_successor).phis;
+ });
+ if (std::holds_alternative(block.exit))
+ live += std::get(block.exit).returnValues
+ | ranges::views::filter(filterLiterals);
+
+ // clean out unreachables
+ live = live
+ | ranges::views::filter(
+ [&](auto const& valueId)
+ { return !std::holds_alternative(m_cfg.valueInfo(valueId)); })
+ | ranges::to;
+
+ // LiveOut(B) <- live
+ m_liveOuts[blockId.value] = live;
+
+ // for each program point p in B, backwards, do:
+ for (auto const& op: block.operations | ranges::views::reverse)
+ {
+ // remove variables defined at p from live
+ live -= op.outputs | ranges::views::filter(filterLiterals) | ranges::to;
+ // add uses at p to live
+ live += op.inputs | ranges::views::filter(filterLiterals) | ranges::to;
+ }
+
+ // livein(b) <- live \cup PhiDefs(B)
+ m_liveIns[blockId.value] = live + block.phis;
+ }
+}
+void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader)
+{
+ // SSA Book, Algorithm 9.3
+ if (m_loopNestingForest.loopNodes().count(_loopHeader) > 0)
+ {
+ // the loop header block id
+ auto const& block = m_cfg.block(SSACFG::BlockId{_loopHeader});
+ // LiveLoop <- LiveIn(B_N) - PhiDefs(B_N)
+ auto liveLoop = m_liveIns[_loopHeader] - block.phis;
+ // must be live out of header if live in of children
+ m_liveOuts[_loopHeader] += liveLoop;
+ // for each blockId \in children(loopHeader)
+ for (size_t blockId = 0; blockId < m_cfg.numBlocks(); ++blockId)
+ if (m_loopNestingForest.loopParents()[blockId] == _loopHeader)
+ {
+ // propagate loop liveness information down to the loop header's children
+ m_liveIns[blockId] += liveLoop;
+ m_liveOuts[blockId] += liveLoop;
+
+ runLoopTreeDfs(blockId);
+ }
+ }
+}
diff --git a/libyul/backends/evm/SSACFGLiveness.h b/libyul/backends/evm/SSACFGLiveness.h
new file mode 100644
index 000000000000..bc254a03a24b
--- /dev/null
+++ b/libyul/backends/evm/SSACFGLiveness.h
@@ -0,0 +1,55 @@
+/*
+ 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 .
+*/
+// SPDX-License-Identifier: GPL-3.0
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace solidity::yul
+{
+
+/// Performs liveness analysis on an SSA CFG following Algorithm 9.1 in [1].
+///
+/// [1] Rastello, Fabrice, and Florent Bouchez Tichadou, eds. SSA-based Compiler Design. Springer, 2022.
+class SSACFGLiveness
+{
+public:
+ explicit SSACFGLiveness(SSACFG const& _cfg);
+
+ std::vector> const& liveIns() const { return m_liveIns; }
+ std::vector> const& liveOuts() const { return m_liveOuts; }
+
+private:
+
+ void runDagDfs();
+ void runLoopTreeDfs(size_t _loopHeader);
+
+ SSACFG const& m_cfg;
+ ForwardSSACFGTopologicalSort m_topologicalSort;
+ SSACFGLoopNestingForest m_loopNestingForest;
+ std::vector> m_liveIns;
+ std::vector> m_liveOuts;
+};
+
+}
From eb22cb6fd7b55cf70508cb76d6dda63659898df3 Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Wed, 18 Sep 2024 14:03:56 +0200
Subject: [PATCH 002/394] Yul: add optional liveness output to toDot of SSACFGs
---
libyul/CMakeLists.txt | 2 +
libyul/backends/evm/ControlFlow.cpp | 28 +++++++++
libyul/backends/evm/ControlFlow.h | 28 +++++++--
libyul/backends/evm/SSAControlFlowGraph.cpp | 30 ++++++++--
libyul/backends/evm/SSAControlFlowGraph.h | 7 ++-
test/libyul/SSAControlFlowGraphTest.cpp | 4 +-
test/libyul/SSAControlFlowGraphTest.h | 20 +++----
.../libyul/yulSSAControlFlowGraph/complex.yul | 57 ++++++++++++-------
.../yulSSAControlFlowGraph/complex2.yul | 57 ++++++++++++-------
.../yulSSAControlFlowGraph/function.yul | 15 +++--
test/libyul/yulSSAControlFlowGraph/if.yul | 9 ++-
test/libyul/yulSSAControlFlowGraph/switch.yul | 18 ++++--
12 files changed, 200 insertions(+), 75 deletions(-)
create mode 100644 libyul/backends/evm/ControlFlow.cpp
diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index 4265b91d3f3e..58c1b17b5c33 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -48,6 +48,8 @@ add_library(yul
backends/evm/AsmCodeGen.h
backends/evm/ConstantOptimiser.cpp
backends/evm/ConstantOptimiser.h
+ backends/evm/ControlFlow.cpp
+ backends/evm/ControlFlow.h
backends/evm/ControlFlowGraph.h
backends/evm/ControlFlowGraphBuilder.cpp
backends/evm/ControlFlowGraphBuilder.h
diff --git a/libyul/backends/evm/ControlFlow.cpp b/libyul/backends/evm/ControlFlow.cpp
new file mode 100644
index 000000000000..3ec123a8ed7c
--- /dev/null
+++ b/libyul/backends/evm/ControlFlow.cpp
@@ -0,0 +1,28 @@
+/*
+ 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 .
+*/
+// SPDX-License-Identifier: GPL-3.0
+
+#include
+#include
+
+using namespace solidity::yul;
+
+ControlFlowLiveness::ControlFlowLiveness(ControlFlow const& _controlFlow):
+ controlFlow(_controlFlow),
+ mainLiveness(std::make_unique(*_controlFlow.mainGraph)),
+ functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique(*_cfg); }) | ranges::to)
+{ }
diff --git a/libyul/backends/evm/ControlFlow.h b/libyul/backends/evm/ControlFlow.h
index 59bc1b677b57..6438f330c386 100644
--- a/libyul/backends/evm/ControlFlow.h
+++ b/libyul/backends/evm/ControlFlow.h
@@ -20,11 +20,22 @@
#include
#include
+#include
#include
namespace solidity::yul
{
+struct ControlFlow;
+
+struct ControlFlowLiveness{
+ explicit ControlFlowLiveness(ControlFlow const& _controlFlow);
+
+ std::reference_wrapper controlFlow;
+ std::unique_ptr mainLiveness;
+ std::vector> functionLiveness;
+};
+
struct ControlFlow
{
std::unique_ptr mainGraph{std::make_unique()};
@@ -39,14 +50,21 @@ struct ControlFlow
return nullptr;
}
- std::string toDot() const
+ 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);
- size_t index = 1;
- for (auto const& [function, functionGraph]: functionGraphMapping)
- output << functionGraph->toDot(false, index++);
+ 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
+ );
+
output << "}\n";
return output.str();
}
diff --git a/libyul/backends/evm/SSAControlFlowGraph.cpp b/libyul/backends/evm/SSAControlFlowGraph.cpp
index e98936aaabbd..dcbdcdca3b0c 100644
--- a/libyul/backends/evm/SSAControlFlowGraph.cpp
+++ b/libyul/backends/evm/SSAControlFlowGraph.cpp
@@ -18,6 +18,8 @@
#include
+#include
+
#include
#include
@@ -37,11 +39,13 @@ namespace
class SSACFGPrinter
{
public:
- SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId)
+ SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness):
+ m_liveness(_liveness)
{
printBlock(_cfg, _blockId, 0);
}
- SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function)
+ SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, SSACFGLiveness const* _liveness):
+ m_liveness(_liveness)
{
printFunction(_cfg, _functionIndex, _function);
}
@@ -97,6 +101,17 @@ class SSACFGPrinter
}
{
m_result << fmt::format("Block{1}_{0} [label=\"\\\nBlock {0}\\n", _id.value, _functionIndex);
+ if (m_liveness)
+ {
+ m_result << fmt::format(
+ "LiveIn: {}\\l\\\n",
+ fmt::join(m_liveness->liveIns()[_id.value] | ranges::views::transform(valueToString), ",")
+ );
+ m_result << fmt::format(
+ "LiveOut: {}\\l\\n",
+ fmt::join(m_liveness->liveOuts()[_id.value] | ranges::views::transform(valueToString), ",")
+ );
+ }
for (auto const& phi: _block.phis)
{
auto const* phiValue = std::get_if(&_cfg.valueInfo(phi));
@@ -224,18 +239,23 @@ class SSACFGPrinter
}
std::stringstream m_result{};
+ SSACFGLiveness const* m_liveness;
};
}
-std::string SSACFG::toDot(bool _includeDiGraphDefinition, std::optional _functionIndex) const
+std::string SSACFG::toDot(
+ bool _includeDiGraphDefinition,
+ std::optional _functionIndex,
+ SSACFGLiveness const* _liveness
+) const
{
std::ostringstream output;
if (_includeDiGraphDefinition)
output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n";
if (function)
- output << SSACFGPrinter(*this, _functionIndex ? *_functionIndex : static_cast(1), *function);
+ output << SSACFGPrinter(*this, _functionIndex ? *_functionIndex : static_cast(1), *function, _liveness);
else
- output << SSACFGPrinter(*this, entry);
+ output << SSACFGPrinter(*this, entry, _liveness);
if (_includeDiGraphDefinition)
output << "}\n";
return output.str();
diff --git a/libyul/backends/evm/SSAControlFlowGraph.h b/libyul/backends/evm/SSAControlFlowGraph.h
index fd24f06cbb2b..00f12fc71de6 100644
--- a/libyul/backends/evm/SSAControlFlowGraph.h
+++ b/libyul/backends/evm/SSAControlFlowGraph.h
@@ -37,6 +37,7 @@
namespace solidity::yul
{
+class SSACFGLiveness;
class SSACFG
{
@@ -207,7 +208,11 @@ class SSACFG
return it->second;
}
- std::string toDot(bool _includeDiGraphDefinition=true, std::optional _functionIndex=std::nullopt) const;
+ std::string toDot(
+ bool _includeDiGraphDefinition=true,
+ std::optional _functionIndex=std::nullopt,
+ SSACFGLiveness const* _liveness=nullptr
+ ) const;
private:
std::deque m_valueInfos;
std::map m_literals;
diff --git a/test/libyul/SSAControlFlowGraphTest.cpp b/test/libyul/SSAControlFlowGraphTest.cpp
index c0d1e87270ef..5f0c79da534d 100644
--- a/test/libyul/SSAControlFlowGraphTest.cpp
+++ b/test/libyul/SSAControlFlowGraphTest.cpp
@@ -72,8 +72,8 @@ TestCase::TestResult SSAControlFlowGraphTest::run(std::ostream& _stream, std::st
*m_dialect,
object->code()->root()
);
-
- m_obtainedResult = controlFlow->toDot();
+ ControlFlowLiveness liveness(*controlFlow);
+ m_obtainedResult = controlFlow->toDot(&liveness);
auto result = checkResult(_stream, _linePrefix, _formatted);
diff --git a/test/libyul/SSAControlFlowGraphTest.h b/test/libyul/SSAControlFlowGraphTest.h
index f976be03c339..09c10448695c 100644
--- a/test/libyul/SSAControlFlowGraphTest.h
+++ b/test/libyul/SSAControlFlowGraphTest.h
@@ -1,18 +1,18 @@
/*
-This file is part of solidity.
+ This file is part of solidity.
-solidity is free software: you can redistribute it and/or modify
+ 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.
+ 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.
+ 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 .
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see .
*/
// SPDX-License-Identifier: GPL-3.0
diff --git a/test/libyul/yulSSAControlFlowGraph/complex.yul b/test/libyul/yulSSAControlFlowGraph/complex.yul
index eef0b29fa3c0..799e53d06d42 100644
--- a/test/libyul/yulSSAControlFlowGraph/complex.yul
+++ b/test/libyul/yulSSAControlFlowGraph/complex.yul
@@ -52,7 +52,8 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nv2 := f(2, 1)\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: \l\nv2 := f(2, 1)\l\
// pop(v2)\l\
// "];
// Block0_0Exit [label="MainExit"];
@@ -61,12 +62,14 @@
// c := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\n"];
+// Block 0\nLiveIn: v0,v1\l\
+// LiveOut: v0,v1\l\n"];
// Block1_0 -> Block1_0Exit [arrowhead=none];
// Block1_0Exit [label="Jump" shape=oval];
// Block1_0Exit -> Block1_1;
// Block1_1 [label="\
-// Block 1\nv5 := φ(\l\
+// Block 1\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nv5 := φ(\l\
// Block 0 => 42,\l\
// Block 21 => v43\l\
// )\l\
@@ -77,7 +80,8 @@
// Block1_1Exit:0 -> Block1_4;
// Block1_1Exit:1 -> Block1_2;
// Block1_2 [label="\
-// Block 2\nv7 := mload(v5)\l\
+// Block 2\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5,v7\l\nv7 := mload(v5)\l\
// v8 := eq(0, v7)\l\
// "];
// Block1_2 -> Block1_2Exit;
@@ -85,80 +89,93 @@
// Block1_2Exit:0 -> Block1_7;
// Block1_2Exit:1 -> Block1_6;
// Block1_4 [label="\
-// Block 4\nsstore(3084, 12)\l\
+// Block 4\nLiveIn: \l\
+// LiveOut: \l\nsstore(3084, 12)\l\
// "];
// Block1_4Exit [label="FunctionReturn[0]"];
// Block1_4 -> Block1_4Exit;
// Block1_6 [label="\
-// Block 6\nsstore(514, 2)\l\
+// Block 6\nLiveIn: \l\
+// LiveOut: \l\nsstore(514, 2)\l\
// "];
// Block1_6 -> Block1_6Exit [arrowhead=none];
// Block1_6Exit [label="Jump" shape=oval];
// Block1_6Exit -> Block1_4;
// Block1_7 [label="\
-// Block 7\nv13 := eq(1, v7)\l\
+// Block 7\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5,v7\l\nv13 := eq(1, v7)\l\
// "];
// Block1_7 -> Block1_7Exit;
// Block1_7Exit [label="{ If v13| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_7Exit:0 -> Block1_10;
// Block1_7Exit:1 -> Block1_9;
// Block1_9 [label="\
-// Block 9\nsstore(1028, 4)\l\
+// Block 9\nLiveIn: \l\
+// LiveOut: \l\nsstore(1028, 4)\l\
// "];
// Block1_9Exit [label="FunctionReturn[0]"];
// Block1_9 -> Block1_9Exit;
// Block1_10 [label="\
-// Block 10\nv20 := eq(2, v7)\l\
+// Block 10\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5,v7\l\nv20 := eq(2, v7)\l\
// "];
// Block1_10 -> Block1_10Exit;
// Block1_10Exit [label="{ If v20| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_10Exit:0 -> Block1_13;
// Block1_10Exit:1 -> Block1_12;
// Block1_12 [label="\
-// Block 12\nsstore(1542, 6)\l\
+// Block 12\nLiveIn: \l\
+// LiveOut: \l\nsstore(1542, 6)\l\
// revert(0, 0)\l\
// "];
// Block1_12Exit [label="Terminated"];
// Block1_12 -> Block1_12Exit;
// Block1_13 [label="\
-// Block 13\nv25 := eq(3, v7)\l\
+// Block 13\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5\l\nv25 := eq(3, v7)\l\
// "];
// Block1_13 -> Block1_13Exit;
// Block1_13Exit [label="{ If v25| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_13Exit:0 -> Block1_16;
// Block1_13Exit:1 -> Block1_15;
// Block1_15 [label="\
-// Block 15\nsstore(2056, 8)\l\
+// Block 15\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2056, 8)\l\
// "];
// Block1_15 -> Block1_15Exit [arrowhead=none];
// Block1_15Exit [label="Jump" shape=oval];
// Block1_15Exit -> Block1_5;
// Block1_16 [label="\
-// Block 16\nv29 := mload(v1)\l\
+// Block 16\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nv29 := mload(v1)\l\
// "];
// Block1_16 -> Block1_16Exit;
// Block1_16Exit [label="{ If v29| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_16Exit:0 -> Block1_18;
// Block1_16Exit:1 -> Block1_17;
// Block1_5 [label="\
-// Block 5\nsstore(2827, 11)\l\
+// Block 5\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2827, 11)\l\
// "];
// Block1_5 -> Block1_5Exit [arrowhead=none];
// Block1_5Exit [label="Jump" shape=oval];
// Block1_5Exit -> Block1_3;
// Block1_17 [label="\
-// Block 17\nreturn(0, 0)\l\
+// Block 17\nLiveIn: \l\
+// LiveOut: \l\nreturn(0, 0)\l\
// "];
// Block1_17Exit [label="Terminated"];
// Block1_17 -> Block1_17Exit;
// Block1_18 [label="\
-// Block 18\nsstore(2570, 10)\l\
+// Block 18\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2570, 10)\l\
// "];
// Block1_18 -> Block1_18Exit [arrowhead=none];
// Block1_18Exit [label="Jump" shape=oval];
// Block1_18Exit -> Block1_5;
// Block1_3 [label="\
-// Block 3\nv43 := add(1, v5)\l\
+// Block 3\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v43\l\nv43 := add(1, v5)\l\
// v44 := calldataload(v43)\l\
// "];
// Block1_3 -> Block1_3Exit;
@@ -166,12 +183,14 @@
// Block1_3Exit:0 -> Block1_21;
// Block1_3Exit:1 -> Block1_20;
// Block1_20 [label="\
-// Block 20\nsstore(v43, 0)\l\
+// Block 20\nLiveIn: v43\l\
+// LiveOut: \l\nsstore(v43, 0)\l\
// "];
// Block1_20Exit [label="FunctionReturn[0]"];
// Block1_20 -> Block1_20Exit;
// Block1_21 [label="\
-// Block 21\nsstore(65535, 255)\l\
+// Block 21\nLiveIn: v0,v1,v43\l\
+// LiveOut: v0,v1,v43\l\nsstore(65535, 255)\l\
// "];
// Block1_21 -> Block1_21Exit [arrowhead=none];
// Block1_21Exit [label="Jump" shape=oval];
diff --git a/test/libyul/yulSSAControlFlowGraph/complex2.yul b/test/libyul/yulSSAControlFlowGraph/complex2.yul
index 743b0513cc56..b35f341417a2 100644
--- a/test/libyul/yulSSAControlFlowGraph/complex2.yul
+++ b/test/libyul/yulSSAControlFlowGraph/complex2.yul
@@ -59,7 +59,8 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nsstore(1, 1)\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: \l\nsstore(1, 1)\l\
// v2 := f(2, 1)\l\
// pop(v2)\l\
// v4 := sload(0)\l\
@@ -77,12 +78,14 @@
// c := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\n"];
+// Block 0\nLiveIn: v0,v1\l\
+// LiveOut: v0,v1\l\n"];
// Block1_0 -> Block1_0Exit [arrowhead=none];
// Block1_0Exit [label="Jump" shape=oval];
// Block1_0Exit -> Block1_1;
// Block1_1 [label="\
-// Block 1\nv5 := φ(\l\
+// Block 1\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nv5 := φ(\l\
// Block 0 => 42,\l\
// Block 21 => v43\l\
// )\l\
@@ -93,7 +96,8 @@
// Block1_1Exit:0 -> Block1_4;
// Block1_1Exit:1 -> Block1_2;
// Block1_2 [label="\
-// Block 2\nv7 := mload(v5)\l\
+// Block 2\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5,v7\l\nv7 := mload(v5)\l\
// v8 := eq(0, v7)\l\
// "];
// Block1_2 -> Block1_2Exit;
@@ -101,80 +105,93 @@
// Block1_2Exit:0 -> Block1_7;
// Block1_2Exit:1 -> Block1_6;
// Block1_4 [label="\
-// Block 4\nsstore(3084, 12)\l\
+// Block 4\nLiveIn: \l\
+// LiveOut: \l\nsstore(3084, 12)\l\
// "];
// Block1_4Exit [label="FunctionReturn[27]"];
// Block1_4 -> Block1_4Exit;
// Block1_6 [label="\
-// Block 6\nsstore(514, 2)\l\
+// Block 6\nLiveIn: \l\
+// LiveOut: \l\nsstore(514, 2)\l\
// "];
// Block1_6 -> Block1_6Exit [arrowhead=none];
// Block1_6Exit [label="Jump" shape=oval];
// Block1_6Exit -> Block1_4;
// Block1_7 [label="\
-// Block 7\nv13 := eq(1, v7)\l\
+// Block 7\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5,v7\l\nv13 := eq(1, v7)\l\
// "];
// Block1_7 -> Block1_7Exit;
// Block1_7Exit [label="{ If v13| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_7Exit:0 -> Block1_10;
// Block1_7Exit:1 -> Block1_9;
// Block1_9 [label="\
-// Block 9\nsstore(1028, 4)\l\
+// Block 9\nLiveIn: \l\
+// LiveOut: \l\nsstore(1028, 4)\l\
// "];
// Block1_9Exit [label="FunctionReturn[0]"];
// Block1_9 -> Block1_9Exit;
// Block1_10 [label="\
-// Block 10\nv20 := eq(2, v7)\l\
+// Block 10\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5,v7\l\nv20 := eq(2, v7)\l\
// "];
// Block1_10 -> Block1_10Exit;
// Block1_10Exit [label="{ If v20| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_10Exit:0 -> Block1_13;
// Block1_10Exit:1 -> Block1_12;
// Block1_12 [label="\
-// Block 12\nsstore(1542, 6)\l\
+// Block 12\nLiveIn: \l\
+// LiveOut: \l\nsstore(1542, 6)\l\
// revert(0, 0)\l\
// "];
// Block1_12Exit [label="Terminated"];
// Block1_12 -> Block1_12Exit;
// Block1_13 [label="\
-// Block 13\nv25 := eq(3, v7)\l\
+// Block 13\nLiveIn: v0,v1,v5,v7\l\
+// LiveOut: v0,v1,v5\l\nv25 := eq(3, v7)\l\
// "];
// Block1_13 -> Block1_13Exit;
// Block1_13Exit [label="{ If v25| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_13Exit:0 -> Block1_16;
// Block1_13Exit:1 -> Block1_15;
// Block1_15 [label="\
-// Block 15\nsstore(2056, 8)\l\
+// Block 15\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2056, 8)\l\
// "];
// Block1_15 -> Block1_15Exit [arrowhead=none];
// Block1_15Exit [label="Jump" shape=oval];
// Block1_15Exit -> Block1_5;
// Block1_16 [label="\
-// Block 16\nv29 := mload(v1)\l\
+// Block 16\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nv29 := mload(v1)\l\
// "];
// Block1_16 -> Block1_16Exit;
// Block1_16Exit [label="{ If v29| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block1_16Exit:0 -> Block1_18;
// Block1_16Exit:1 -> Block1_17;
// Block1_5 [label="\
-// Block 5\nsstore(2827, 11)\l\
+// Block 5\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2827, 11)\l\
// "];
// Block1_5 -> Block1_5Exit [arrowhead=none];
// Block1_5Exit [label="Jump" shape=oval];
// Block1_5Exit -> Block1_3;
// Block1_17 [label="\
-// Block 17\nreturn(0, 0)\l\
+// Block 17\nLiveIn: \l\
+// LiveOut: \l\nreturn(0, 0)\l\
// "];
// Block1_17Exit [label="Terminated"];
// Block1_17 -> Block1_17Exit;
// Block1_18 [label="\
-// Block 18\nsstore(2570, 10)\l\
+// Block 18\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v5\l\nsstore(2570, 10)\l\
// "];
// Block1_18 -> Block1_18Exit [arrowhead=none];
// Block1_18Exit [label="Jump" shape=oval];
// Block1_18Exit -> Block1_5;
// Block1_3 [label="\
-// Block 3\nv43 := add(1, v5)\l\
+// Block 3\nLiveIn: v0,v1,v5\l\
+// LiveOut: v0,v1,v43\l\nv43 := add(1, v5)\l\
// v44 := calldataload(v43)\l\
// "];
// Block1_3 -> Block1_3Exit;
@@ -182,12 +199,14 @@
// Block1_3Exit:0 -> Block1_21;
// Block1_3Exit:1 -> Block1_20;
// Block1_20 [label="\
-// Block 20\nsstore(v43, 0)\l\
+// Block 20\nLiveIn: v43\l\
+// LiveOut: \l\nsstore(v43, 0)\l\
// "];
// Block1_20Exit [label="FunctionReturn[0]"];
// Block1_20 -> Block1_20Exit;
// Block1_21 [label="\
-// Block 21\nsstore(65535, 255)\l\
+// Block 21\nLiveIn: v0,v1,v43\l\
+// LiveOut: v0,v1,v43\l\nsstore(65535, 255)\l\
// "];
// Block1_21 -> Block1_21Exit [arrowhead=none];
// Block1_21Exit [label="Jump" shape=oval];
diff --git a/test/libyul/yulSSAControlFlowGraph/function.yul b/test/libyul/yulSSAControlFlowGraph/function.yul
index 20a536b5e556..1b759f8b395a 100644
--- a/test/libyul/yulSSAControlFlowGraph/function.yul
+++ b/test/libyul/yulSSAControlFlowGraph/function.yul
@@ -27,7 +27,8 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nv0, v1 := i()\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: \l\nv0, v1 := i()\l\
// h(v0)\l\
// "];
// Block0_0Exit [label="Terminated"];
@@ -36,7 +37,8 @@
// r := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\nv3 := add(v1, v0)\l\
+// Block 0\nLiveIn: v0,v1\l\
+// LiveOut: v4\l\nv3 := add(v1, v0)\l\
// v4 := sub(v0, v3)\l\
// "];
// Block1_0Exit [label="FunctionReturn[v4]"];
@@ -45,7 +47,8 @@
// g()"];
// FunctionEntry_g_0 -> Block2_0;
// Block2_0 [label="\
-// Block 0\nsstore(257, 1)\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: \l\nsstore(257, 1)\l\
// "];
// Block2_0Exit [label="FunctionReturn[]"];
// Block2_0 -> Block2_0Exit;
@@ -53,7 +56,8 @@
// h(v0)"];
// FunctionEntry_h_0 -> Block3_0;
// Block3_0 [label="\
-// Block 0\nv2 := f(0, v0)\l\
+// Block 0\nLiveIn: v0\l\
+// LiveOut: \l\nv2 := f(0, v0)\l\
// h(v2)\l\
// "];
// Block3_0Exit [label="Terminated"];
@@ -62,7 +66,8 @@
// v, w := i()"];
// FunctionEntry_i_0 -> Block4_0;
// Block4_0 [label="\
-// Block 0\n"];
+// Block 0\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block4_0Exit [label="FunctionReturn[514, 771]"];
// Block4_0 -> Block4_0Exit;
// }
diff --git a/test/libyul/yulSSAControlFlowGraph/if.yul b/test/libyul/yulSSAControlFlowGraph/if.yul
index 9045d8034003..9c4d73a13a25 100644
--- a/test/libyul/yulSSAControlFlowGraph/if.yul
+++ b/test/libyul/yulSSAControlFlowGraph/if.yul
@@ -15,20 +15,23 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nv1 := calldataload(3)\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: v1\l\nv1 := calldataload(3)\l\
// "];
// Block0_0 -> Block0_0Exit;
// Block0_0Exit [label="{ If 0| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block0_0Exit:0 -> Block0_2;
// Block0_0Exit:1 -> Block0_1;
// Block0_1 [label="\
-// Block 1\nv4 := calldataload(77)\l\
+// Block 1\nLiveIn: \l\
+// LiveOut: v4\l\nv4 := calldataload(77)\l\
// "];
// Block0_1 -> Block0_1Exit [arrowhead=none];
// Block0_1Exit [label="Jump" shape=oval];
// Block0_1Exit -> Block0_2;
// Block0_2 [label="\
-// Block 2\nv5 := φ(\l\
+// Block 2\nLiveIn: v5\l\
+// LiveOut: \l\nv5 := φ(\l\
// Block 0 => v1,\l\
// Block 1 => v4\l\
// )\l\
diff --git a/test/libyul/yulSSAControlFlowGraph/switch.yul b/test/libyul/yulSSAControlFlowGraph/switch.yul
index f6c084b001a7..d744bd7bd247 100644
--- a/test/libyul/yulSSAControlFlowGraph/switch.yul
+++ b/test/libyul/yulSSAControlFlowGraph/switch.yul
@@ -22,7 +22,8 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nv1 := calldataload(3)\l\
+// Block 0\nLiveIn: \l\
+// LiveOut: v3\l\nv1 := calldataload(3)\l\
// v3 := sload(0)\l\
// v4 := eq(0, v3)\l\
// "];
@@ -31,20 +32,23 @@
// Block0_0Exit:0 -> Block0_3;
// Block0_0Exit:1 -> Block0_2;
// Block0_2 [label="\
-// Block 2\nv6 := calldataload(77)\l\
+// Block 2\nLiveIn: \l\
+// LiveOut: v6\l\nv6 := calldataload(77)\l\
// "];
// Block0_2 -> Block0_2Exit [arrowhead=none];
// Block0_2Exit [label="Jump" shape=oval];
// Block0_2Exit -> Block0_1;
// Block0_3 [label="\
-// Block 3\nv7 := eq(1, v3)\l\
+// Block 3\nLiveIn: v3\l\
+// LiveOut: \l\nv7 := eq(1, v3)\l\
// "];
// Block0_3 -> Block0_3Exit;
// Block0_3Exit [label="{ If v7| { <0> Zero | <1> NonZero }}" shape=Mrecord];
// Block0_3Exit:0 -> Block0_5;
// Block0_3Exit:1 -> Block0_4;
// Block0_1 [label="\
-// Block 1\nv13 := φ(\l\
+// Block 1\nLiveIn: v13\l\
+// LiveOut: \l\nv13 := φ(\l\
// Block 2 => v6,\l\
// Block 4 => v10,\l\
// Block 5 => v12\l\
@@ -54,13 +58,15 @@
// Block0_1Exit [label="MainExit"];
// Block0_1 -> Block0_1Exit;
// Block0_4 [label="\
-// Block 4\nv10 := calldataload(88)\l\
+// Block 4\nLiveIn: \l\
+// LiveOut: v10\l\nv10 := calldataload(88)\l\
// "];
// Block0_4 -> Block0_4Exit [arrowhead=none];
// Block0_4Exit [label="Jump" shape=oval];
// Block0_4Exit -> Block0_1;
// Block0_5 [label="\
-// Block 5\nv12 := calldataload(99)\l\
+// Block 5\nLiveIn: \l\
+// LiveOut: v12\l\nv12 := calldataload(99)\l\
// "];
// Block0_5 -> Block0_5Exit [arrowhead=none];
// Block0_5Exit [label="Jump" shape=oval];
From f35fd1f6df3f0d341ead02d9a83c39f018ba168e Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Wed, 18 Sep 2024 15:01:25 +0200
Subject: [PATCH 003/394] Yul: display dotted back edges in toDot
---
libyul/backends/evm/SSACFGLiveness.cpp | 8 +-
libyul/backends/evm/SSACFGLiveness.h | 2 +-
libyul/backends/evm/SSACFGTopologicalSort.h | 2 +
libyul/backends/evm/SSAControlFlowGraph.cpp | 111 ++++++++-----
.../libyul/yulSSAControlFlowGraph/complex.yul | 92 +++++------
.../yulSSAControlFlowGraph/complex2.yul | 92 +++++------
.../yulSSAControlFlowGraph/function.yul | 10 +-
test/libyul/yulSSAControlFlowGraph/if.yul | 14 +-
.../yulSSAControlFlowGraph/nested_for.yul | 151 ++++++++++--------
.../nested_function.yul | 55 ++++---
test/libyul/yulSSAControlFlowGraph/switch.yul | 30 ++--
11 files changed, 313 insertions(+), 254 deletions(-)
diff --git a/libyul/backends/evm/SSACFGLiveness.cpp b/libyul/backends/evm/SSACFGLiveness.cpp
index 6ed926304db2..b9513f70dfce 100644
--- a/libyul/backends/evm/SSACFGLiveness.cpp
+++ b/libyul/backends/evm/SSACFGLiveness.cpp
@@ -38,7 +38,7 @@ SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg):
void SSACFGLiveness::runDagDfs()
{
// SSA Book, Algorithm 9.2
- for(auto const blockIdValue: m_topologicalSort.postOrder())
+ for (auto const blockIdValue: m_topologicalSort.postOrder())
{
// post-order traversal
SSACFG::BlockId blockId{blockIdValue};
@@ -85,11 +85,7 @@ void SSACFGLiveness::runDagDfs()
| ranges::views::filter(filterLiterals);
// clean out unreachables
- live = live
- | ranges::views::filter(
- [&](auto const& valueId)
- { return !std::holds_alternative(m_cfg.valueInfo(valueId)); })
- | ranges::to;
+ live = live | ranges::views::filter([&](auto const& valueId) { return !std::holds_alternative(m_cfg.valueInfo(valueId)); }) | ranges::to;
// LiveOut(B) <- live
m_liveOuts[blockId.value] = live;
diff --git a/libyul/backends/evm/SSACFGLiveness.h b/libyul/backends/evm/SSACFGLiveness.h
index bc254a03a24b..6f486b737f13 100644
--- a/libyul/backends/evm/SSACFGLiveness.h
+++ b/libyul/backends/evm/SSACFGLiveness.h
@@ -39,7 +39,7 @@ class SSACFGLiveness
std::vector> const& liveIns() const { return m_liveIns; }
std::vector> const& liveOuts() const { return m_liveOuts; }
-
+ ForwardSSACFGTopologicalSort const& topologicalSort() const { return m_topologicalSort; }
private:
void runDagDfs();
diff --git a/libyul/backends/evm/SSACFGTopologicalSort.h b/libyul/backends/evm/SSACFGTopologicalSort.h
index 973f286f286a..025054b9504c 100644
--- a/libyul/backends/evm/SSACFGTopologicalSort.h
+++ b/libyul/backends/evm/SSACFGTopologicalSort.h
@@ -38,6 +38,8 @@ class ForwardSSACFGTopologicalSort
std::set const& backEdgeTargets() const { return m_backEdgeTargets; }
SSACFG const& cfg() const { return m_cfg; }
bool backEdge(SSACFG::BlockId const& _block1, SSACFG::BlockId const& _block2) const;
+ size_t preOrderIndexOf(size_t _block) const { return m_blockWisePreOrder[_block]; }
+ size_t maxSubtreePreOrderIndexOf(size_t _block) const { return m_blockWiseMaxSubtreePreOrder[_block]; }
private:
void dfs(size_t _vertex);
diff --git a/libyul/backends/evm/SSAControlFlowGraph.cpp b/libyul/backends/evm/SSAControlFlowGraph.cpp
index dcbdcdca3b0c..ebd6d84a9680 100644
--- a/libyul/backends/evm/SSAControlFlowGraph.cpp
+++ b/libyul/backends/evm/SSAControlFlowGraph.cpp
@@ -40,14 +40,14 @@ class SSACFGPrinter
{
public:
SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness):
- m_liveness(_liveness)
+ m_cfg(_cfg), m_functionIndex(0), m_liveness(_liveness)
{
- printBlock(_cfg, _blockId, 0);
+ printBlock(_blockId);
}
SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, SSACFGLiveness const* _liveness):
- m_liveness(_liveness)
+ m_cfg(_cfg), m_functionIndex(_functionIndex), m_liveness(_liveness)
{
- printFunction(_cfg, _functionIndex, _function);
+ printFunction(_function);
}
friend std::ostream& operator<<(std::ostream& stream, SSACFGPrinter const& printer) {
stream << printer.m_result.str();
@@ -77,6 +77,20 @@ class SSACFGPrinter
);
}
+ std::string formatBlockHandle(SSACFG::BlockId const& _id) const
+ {
+ return fmt::format("Block{}_{}", m_functionIndex, _id.value);
+ }
+
+ std::string formatEdge(SSACFG::BlockId const& _v, SSACFG::BlockId const& _w, std::optional const& _vPort = std::nullopt)
+ {
+ std::string const style = m_liveness && m_liveness->topologicalSort().backEdge(_v, _w) ? "dashed" : "solid";
+ if (_vPort)
+ return fmt::format("{}Exit:{} -> {} [style=\"{}\"];\n", formatBlockHandle(_v), *_vPort, formatBlockHandle(_w), style);
+ else
+ return fmt::format("{}Exit -> {} [style=\"{}\"];\n", formatBlockHandle(_v), formatBlockHandle(_w), style);
+ }
+
static std::string formatPhi(SSACFG const& _cfg, SSACFG::PhiValue const& _phiValue)
{
auto const transform = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); };
@@ -90,19 +104,25 @@ class SSACFGPrinter
return "φ()";
}
- void writeBlock(SSACFG const& _cfg, SSACFG::BlockId const& _id, SSACFG::BasicBlock const& _block, size_t const _functionIndex)
+ void writeBlock(SSACFG::BlockId const& _id, SSACFG::BasicBlock const& _block)
{
- auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); };
- bool entryBlock = _id.value == 0 && _functionIndex == 0;
+ auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(m_cfg, valueId); };
+ bool entryBlock = _id.value == 0 && m_functionIndex == 0;
if (entryBlock)
{
- m_result << fmt::format("Entry{} [label=\"Entry\"];\n", _functionIndex);
- m_result << fmt::format("Entry{} -> Block{}_{};\n", _functionIndex, _functionIndex, _id.value);
+ m_result << fmt::format("Entry{} [label=\"Entry\"];\n", m_functionIndex);
+ m_result << fmt::format("Entry{} -> {};\n", m_functionIndex, formatBlockHandle(_id));
}
{
- m_result << fmt::format("Block{1}_{0} [label=\"\\\nBlock {0}\\n", _id.value, _functionIndex);
if (m_liveness)
{
+ m_result << fmt::format(
+ "{} [label=\"\\\nBlock {}; ({}, max {})\\n",
+ formatBlockHandle(_id),
+ _id.value,
+ m_liveness->topologicalSort().preOrderIndexOf(_id.value),
+ m_liveness->topologicalSort().maxSubtreePreOrderIndexOf(_id.value)
+ );
m_result << fmt::format(
"LiveIn: {}\\l\\\n",
fmt::join(m_liveness->liveIns()[_id.value] | ranges::views::transform(valueToString), ",")
@@ -112,11 +132,13 @@ class SSACFGPrinter
fmt::join(m_liveness->liveOuts()[_id.value] | ranges::views::transform(valueToString), ",")
);
}
+ else
+ m_result << fmt::format("{} [label=\"\\\nBlock {}\\n", formatBlockHandle(_id), _id.value);
for (auto const& phi: _block.phis)
{
- auto const* phiValue = std::get_if(&_cfg.valueInfo(phi));
+ auto const* phiValue = std::get_if(&m_cfg.valueInfo(phi));
solAssert(phiValue);
- m_result << fmt::format("v{} := {}\\l\\\n", phi.value, formatPhi(_cfg, *phiValue));
+ m_result << fmt::format("v{} := {}\\l\\\n", phi.value, formatPhi(m_cfg, *phiValue));
}
for (auto const& operation: _block.operations)
{
@@ -143,29 +165,28 @@ class SSACFGPrinter
std::visit(GenericVisitor{
[&](SSACFG::BasicBlock::MainExit const&)
{
- m_result << fmt::format("Block{}_{}Exit [label=\"MainExit\"];\n", _functionIndex, _id.value);
- m_result << fmt::format("Block{1}_{0} -> Block{1}_{0}Exit;\n", _id.value, _functionIndex);
+ m_result << fmt::format("{}Exit [label=\"MainExit\"];\n", formatBlockHandle(_id));
+ m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id));
},
[&](SSACFG::BasicBlock::Jump const& _jump)
{
- m_result << fmt::format("Block{1}_{0} -> Block{1}_{0}Exit [arrowhead=none];\n", _id.value, _functionIndex);
- m_result << fmt::format("Block{}_{}Exit [label=\"Jump\" shape=oval];\n", _functionIndex, _id.value);
- m_result << fmt::format("Block{}_{}Exit -> Block{}_{};\n", _functionIndex, _id.value, _functionIndex, _jump.target.value);
+ m_result << fmt::format("{} -> {}Exit [arrowhead=none];\n", formatBlockHandle(_id), formatBlockHandle(_id));
+ m_result << fmt::format("{}Exit [label=\"Jump\" shape=oval];\n", formatBlockHandle(_id));
+ m_result << formatEdge(_id, _jump.target);
},
[&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump)
{
- m_result << "Block" << _functionIndex << "_" << _id.value << " -> Block" << _functionIndex << "_" << _id.value << "Exit;\n";
- m_result << "Block" << _functionIndex << "_" << _id.value << "Exit [label=\"{ If ";
- m_result << varToString(_cfg, _conditionalJump.condition);
- m_result << "| { <0> Zero | <1> NonZero }}\" shape=Mrecord];\n";
- m_result << "Block" << _functionIndex << "_" << _id.value;
- m_result << "Exit:0 -> Block" << _functionIndex << "_" << _conditionalJump.zero.value << ";\n";
- m_result << "Block" << _functionIndex << "_" << _id.value;
- m_result << "Exit:1 -> Block" << _functionIndex << "_" << _conditionalJump.nonZero.value << ";\n";
+ m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id));
+ m_result << fmt::format(
+ "{}Exit [label=\"{{ If {} | {{ <0> Zero | <1> NonZero }}}}\" shape=Mrecord];\n",
+ formatBlockHandle(_id), varToString(m_cfg, _conditionalJump.condition)
+ );
+ m_result << formatEdge(_id, _conditionalJump.zero, "0");
+ m_result << formatEdge(_id, _conditionalJump.nonZero, "1");
},
[&](SSACFG::BasicBlock::JumpTable const& jt)
{
- m_result << "Block" << _functionIndex << "_" << _id.value << " -> Block" << _functionIndex << "_" << _id.value << "Exit;\n";
+ m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id));
std::string options;
for (auto const& jumpCase: jt.cases)
{
@@ -176,28 +197,28 @@ class SSACFGPrinter
if (!options.empty())
options += " | ";
options += " default";
- m_result << fmt::format("Block{}_{}Exit [label=\"{{ JT | {{ {} }} }}\" shape=Mrecord];\n", _functionIndex, _id.value, options);
+ m_result << fmt::format("{}Exit [label=\"{{ JT | {{ {} }} }}\" shape=Mrecord];\n", formatBlockHandle(_id), options);
for (auto const& jumpCase: jt.cases)
- m_result << fmt::format("Block{}_{}Exit:{} -> Block{}_{};\n", _functionIndex, _id.value, formatNumber(jumpCase.first), _functionIndex, jumpCase.second.value);
- m_result << fmt::format("Block{}_{}Exit:default -> Block{}_{};\n", _functionIndex, _id.value, _functionIndex, jt.defaultCase.value);
+ m_result << formatEdge(_id, jumpCase.second, formatNumber(jumpCase.first));
+ m_result << formatEdge(_id, jt.defaultCase, "default");
},
[&](SSACFG::BasicBlock::FunctionReturn const& fr)
{
- m_result << "Block" << _functionIndex << "_" << _id.value << "Exit [label=\"FunctionReturn["
+ m_result << formatBlockHandle(_id) << "Exit [label=\"FunctionReturn["
<< fmt::format("{}", fmt::join(fr.returnValues | ranges::views::transform(valueToString), ", "))
<< "]\"];\n";
- m_result << "Block" << _functionIndex << "_" << _id.value << " -> Block" << _functionIndex << "_" << _id.value << "Exit;\n";
+ m_result << formatBlockHandle(_id) << " -> " << formatBlockHandle(_id) << "Exit;\n";
},
[&](SSACFG::BasicBlock::Terminated const&)
{
- m_result << "Block" << _functionIndex << "_" << _id.value << "Exit [label=\"Terminated\"];\n";
- m_result << "Block" << _functionIndex << "_" << _id.value << " -> Block" << _functionIndex << "_" << _id.value << "Exit;\n";
+ m_result << formatBlockHandle(_id) << "Exit [label=\"Terminated\"];\n";
+ m_result << formatBlockHandle(_id) << " -> " << formatBlockHandle(_id) << "Exit;\n";
}
}, _block.exit);
}
}
- void printBlock(SSACFG const& _cfg, SSACFG::BlockId const& _rootId, size_t const _functionIndex)
+ void printBlock(SSACFG::BlockId const& _rootId)
{
std::set explored{};
explored.insert(_rootId);
@@ -209,8 +230,8 @@ class SSACFGPrinter
{
auto const id = toVisit.front();
toVisit.pop_front();
- auto const& block = _cfg.block(id);
- writeBlock(_cfg, id, block, _functionIndex);
+ auto const& block = m_cfg.block(id);
+ writeBlock(id, block);
block.forEachExit(
[&](SSACFG::BlockId const& _exitBlock)
{
@@ -224,22 +245,24 @@ class SSACFGPrinter
}
}
- void printFunction(SSACFG const& _cfg, size_t const _functionIndex, Scope::Function const& _fun)
+ void printFunction(Scope::Function const& _fun)
{
static auto constexpr returnsTransform = [](auto const& functionReturnValue) { return functionReturnValue.get().name.str(); };
static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value); };
- m_result << "FunctionEntry_" << _fun.name.str() << "_" << _cfg.entry.value << " [label=\"";
- if (!_cfg.returns.empty())
- m_result << fmt::format("function {0}:\n {1} := {0}({2})", _fun.name.str(), fmt::join(_cfg.returns | ranges::views::transform(returnsTransform), ", "), fmt::join(_cfg.arguments | ranges::views::transform(argsTransform), ", "));
+ m_result << "FunctionEntry_" << _fun.name.str() << "_" << m_cfg.entry.value << " [label=\"";
+ if (!m_cfg.returns.empty())
+ m_result << fmt::format("function {0}:\n {1} := {0}({2})", _fun.name.str(), fmt::join(m_cfg.returns | ranges::views::transform(returnsTransform), ", "), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", "));
else
- m_result << fmt::format("function {0}:\n {0}({1})", _fun.name.str(), fmt::join(_cfg.arguments | ranges::views::transform(argsTransform), ", "));
+ m_result << fmt::format("function {0}:\n {0}({1})", _fun.name.str(), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", "));
m_result << "\"];\n";
- m_result << "FunctionEntry_" << _fun.name.str() << "_" << _cfg.entry.value << " -> Block" << _functionIndex << "_" << _cfg.entry.value << ";\n";
- printBlock(_cfg, _cfg.entry, _functionIndex);
+ m_result << "FunctionEntry_" << _fun.name.str() << "_" << m_cfg.entry.value << " -> Block" << m_functionIndex << "_" << m_cfg.entry.value << ";\n";
+ printBlock(m_cfg.entry);
}
- std::stringstream m_result{};
+ SSACFG const& m_cfg;
+ size_t m_functionIndex;
SSACFGLiveness const* m_liveness;
+ std::stringstream m_result{};
};
}
diff --git a/test/libyul/yulSSAControlFlowGraph/complex.yul b/test/libyul/yulSSAControlFlowGraph/complex.yul
index 799e53d06d42..6fb0453c186b 100644
--- a/test/libyul/yulSSAControlFlowGraph/complex.yul
+++ b/test/libyul/yulSSAControlFlowGraph/complex.yul
@@ -52,7 +52,7 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 0)\nLiveIn: \l\
// LiveOut: \l\nv2 := f(2, 1)\l\
// pop(v2)\l\
// "];
@@ -62,13 +62,13 @@
// c := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\nLiveIn: v0,v1\l\
+// Block 0; (0, max 17)\nLiveIn: v0,v1\l\
// LiveOut: v0,v1\l\n"];
// Block1_0 -> Block1_0Exit [arrowhead=none];
// Block1_0Exit [label="Jump" shape=oval];
-// Block1_0Exit -> Block1_1;
+// Block1_0Exit -> Block1_1 [style="solid"];
// Block1_1 [label="\
-// Block 1\nLiveIn: v0,v1,v5\l\
+// Block 1; (1, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nv5 := φ(\l\
// Block 0 => 42,\l\
// Block 21 => v43\l\
@@ -76,123 +76,123 @@
// v6 := lt(v0, v5)\l\
// "];
// Block1_1 -> Block1_1Exit;
-// Block1_1Exit [label="{ If v6| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_1Exit:0 -> Block1_4;
-// Block1_1Exit:1 -> Block1_2;
+// Block1_1Exit [label="{ If v6 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_1Exit:0 -> Block1_4 [style="solid"];
+// Block1_1Exit:1 -> Block1_2 [style="solid"];
// Block1_2 [label="\
-// Block 2\nLiveIn: v0,v1,v5\l\
+// Block 2; (2, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5,v7\l\nv7 := mload(v5)\l\
// v8 := eq(0, v7)\l\
// "];
// Block1_2 -> Block1_2Exit;
-// Block1_2Exit [label="{ If v8| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_2Exit:0 -> Block1_7;
-// Block1_2Exit:1 -> Block1_6;
+// Block1_2Exit [label="{ If v8 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_2Exit:0 -> Block1_7 [style="solid"];
+// Block1_2Exit:1 -> Block1_6 [style="solid"];
// Block1_4 [label="\
-// Block 4\nLiveIn: \l\
+// Block 4; (4, max 4)\nLiveIn: \l\
// LiveOut: \l\nsstore(3084, 12)\l\
// "];
// Block1_4Exit [label="FunctionReturn[0]"];
// Block1_4 -> Block1_4Exit;
// Block1_6 [label="\
-// Block 6\nLiveIn: \l\
+// Block 6; (3, max 4)\nLiveIn: \l\
// LiveOut: \l\nsstore(514, 2)\l\
// "];
// Block1_6 -> Block1_6Exit [arrowhead=none];
// Block1_6Exit [label="Jump" shape=oval];
-// Block1_6Exit -> Block1_4;
+// Block1_6Exit -> Block1_4 [style="solid"];
// Block1_7 [label="\
-// Block 7\nLiveIn: v0,v1,v5,v7\l\
+// Block 7; (5, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5,v7\l\nv13 := eq(1, v7)\l\
// "];
// Block1_7 -> Block1_7Exit;
-// Block1_7Exit [label="{ If v13| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_7Exit:0 -> Block1_10;
-// Block1_7Exit:1 -> Block1_9;
+// Block1_7Exit [label="{ If v13 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_7Exit:0 -> Block1_10 [style="solid"];
+// Block1_7Exit:1 -> Block1_9 [style="solid"];
// Block1_9 [label="\
-// Block 9\nLiveIn: \l\
+// Block 9; (6, max 6)\nLiveIn: \l\
// LiveOut: \l\nsstore(1028, 4)\l\
// "];
// Block1_9Exit [label="FunctionReturn[0]"];
// Block1_9 -> Block1_9Exit;
// Block1_10 [label="\
-// Block 10\nLiveIn: v0,v1,v5,v7\l\
+// Block 10; (7, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5,v7\l\nv20 := eq(2, v7)\l\
// "];
// Block1_10 -> Block1_10Exit;
-// Block1_10Exit [label="{ If v20| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_10Exit:0 -> Block1_13;
-// Block1_10Exit:1 -> Block1_12;
+// Block1_10Exit [label="{ If v20 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_10Exit:0 -> Block1_13 [style="solid"];
+// Block1_10Exit:1 -> Block1_12 [style="solid"];
// Block1_12 [label="\
-// Block 12\nLiveIn: \l\
+// Block 12; (8, max 8)\nLiveIn: \l\
// LiveOut: \l\nsstore(1542, 6)\l\
// revert(0, 0)\l\
// "];
// Block1_12Exit [label="Terminated"];
// Block1_12 -> Block1_12Exit;
// Block1_13 [label="\
-// Block 13\nLiveIn: v0,v1,v5,v7\l\
+// Block 13; (9, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5\l\nv25 := eq(3, v7)\l\
// "];
// Block1_13 -> Block1_13Exit;
-// Block1_13Exit [label="{ If v25| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_13Exit:0 -> Block1_16;
-// Block1_13Exit:1 -> Block1_15;
+// Block1_13Exit [label="{ If v25 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_13Exit:0 -> Block1_16 [style="solid"];
+// Block1_13Exit:1 -> Block1_15 [style="solid"];
// Block1_15 [label="\
-// Block 15\nLiveIn: v0,v1,v5\l\
+// Block 15; (10, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2056, 8)\l\
// "];
// Block1_15 -> Block1_15Exit [arrowhead=none];
// Block1_15Exit [label="Jump" shape=oval];
-// Block1_15Exit -> Block1_5;
+// Block1_15Exit -> Block1_5 [style="solid"];
// Block1_16 [label="\
-// Block 16\nLiveIn: v0,v1,v5\l\
+// Block 16; (15, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nv29 := mload(v1)\l\
// "];
// Block1_16 -> Block1_16Exit;
-// Block1_16Exit [label="{ If v29| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_16Exit:0 -> Block1_18;
-// Block1_16Exit:1 -> Block1_17;
+// Block1_16Exit [label="{ If v29 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_16Exit:0 -> Block1_18 [style="solid"];
+// Block1_16Exit:1 -> Block1_17 [style="solid"];
// Block1_5 [label="\
-// Block 5\nLiveIn: v0,v1,v5\l\
+// Block 5; (11, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2827, 11)\l\
// "];
// Block1_5 -> Block1_5Exit [arrowhead=none];
// Block1_5Exit [label="Jump" shape=oval];
-// Block1_5Exit -> Block1_3;
+// Block1_5Exit -> Block1_3 [style="solid"];
// Block1_17 [label="\
-// Block 17\nLiveIn: \l\
+// Block 17; (16, max 16)\nLiveIn: \l\
// LiveOut: \l\nreturn(0, 0)\l\
// "];
// Block1_17Exit [label="Terminated"];
// Block1_17 -> Block1_17Exit;
// Block1_18 [label="\
-// Block 18\nLiveIn: v0,v1,v5\l\
+// Block 18; (17, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2570, 10)\l\
// "];
// Block1_18 -> Block1_18Exit [arrowhead=none];
// Block1_18Exit [label="Jump" shape=oval];
-// Block1_18Exit -> Block1_5;
+// Block1_18Exit -> Block1_5 [style="solid"];
// Block1_3 [label="\
-// Block 3\nLiveIn: v0,v1,v5\l\
+// Block 3; (12, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v43\l\nv43 := add(1, v5)\l\
// v44 := calldataload(v43)\l\
// "];
// Block1_3 -> Block1_3Exit;
-// Block1_3Exit [label="{ If v44| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_3Exit:0 -> Block1_21;
-// Block1_3Exit:1 -> Block1_20;
+// Block1_3Exit [label="{ If v44 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_3Exit:0 -> Block1_21 [style="solid"];
+// Block1_3Exit:1 -> Block1_20 [style="solid"];
// Block1_20 [label="\
-// Block 20\nLiveIn: v43\l\
+// Block 20; (13, max 13)\nLiveIn: v43\l\
// LiveOut: \l\nsstore(v43, 0)\l\
// "];
// Block1_20Exit [label="FunctionReturn[0]"];
// Block1_20 -> Block1_20Exit;
// Block1_21 [label="\
-// Block 21\nLiveIn: v0,v1,v43\l\
+// Block 21; (14, max 14)\nLiveIn: v0,v1,v43\l\
// LiveOut: v0,v1,v43\l\nsstore(65535, 255)\l\
// "];
// Block1_21 -> Block1_21Exit [arrowhead=none];
// Block1_21Exit [label="Jump" shape=oval];
-// Block1_21Exit -> Block1_1;
+// Block1_21Exit -> Block1_1 [style="dashed"];
// }
diff --git a/test/libyul/yulSSAControlFlowGraph/complex2.yul b/test/libyul/yulSSAControlFlowGraph/complex2.yul
index b35f341417a2..c3ea8b94f8aa 100644
--- a/test/libyul/yulSSAControlFlowGraph/complex2.yul
+++ b/test/libyul/yulSSAControlFlowGraph/complex2.yul
@@ -59,7 +59,7 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 0)\nLiveIn: \l\
// LiveOut: \l\nsstore(1, 1)\l\
// v2 := f(2, 1)\l\
// pop(v2)\l\
@@ -78,13 +78,13 @@
// c := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\nLiveIn: v0,v1\l\
+// Block 0; (0, max 17)\nLiveIn: v0,v1\l\
// LiveOut: v0,v1\l\n"];
// Block1_0 -> Block1_0Exit [arrowhead=none];
// Block1_0Exit [label="Jump" shape=oval];
-// Block1_0Exit -> Block1_1;
+// Block1_0Exit -> Block1_1 [style="solid"];
// Block1_1 [label="\
-// Block 1\nLiveIn: v0,v1,v5\l\
+// Block 1; (1, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nv5 := φ(\l\
// Block 0 => 42,\l\
// Block 21 => v43\l\
@@ -92,123 +92,123 @@
// v6 := lt(v0, v5)\l\
// "];
// Block1_1 -> Block1_1Exit;
-// Block1_1Exit [label="{ If v6| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_1Exit:0 -> Block1_4;
-// Block1_1Exit:1 -> Block1_2;
+// Block1_1Exit [label="{ If v6 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_1Exit:0 -> Block1_4 [style="solid"];
+// Block1_1Exit:1 -> Block1_2 [style="solid"];
// Block1_2 [label="\
-// Block 2\nLiveIn: v0,v1,v5\l\
+// Block 2; (2, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5,v7\l\nv7 := mload(v5)\l\
// v8 := eq(0, v7)\l\
// "];
// Block1_2 -> Block1_2Exit;
-// Block1_2Exit [label="{ If v8| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_2Exit:0 -> Block1_7;
-// Block1_2Exit:1 -> Block1_6;
+// Block1_2Exit [label="{ If v8 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_2Exit:0 -> Block1_7 [style="solid"];
+// Block1_2Exit:1 -> Block1_6 [style="solid"];
// Block1_4 [label="\
-// Block 4\nLiveIn: \l\
+// Block 4; (4, max 4)\nLiveIn: \l\
// LiveOut: \l\nsstore(3084, 12)\l\
// "];
// Block1_4Exit [label="FunctionReturn[27]"];
// Block1_4 -> Block1_4Exit;
// Block1_6 [label="\
-// Block 6\nLiveIn: \l\
+// Block 6; (3, max 4)\nLiveIn: \l\
// LiveOut: \l\nsstore(514, 2)\l\
// "];
// Block1_6 -> Block1_6Exit [arrowhead=none];
// Block1_6Exit [label="Jump" shape=oval];
-// Block1_6Exit -> Block1_4;
+// Block1_6Exit -> Block1_4 [style="solid"];
// Block1_7 [label="\
-// Block 7\nLiveIn: v0,v1,v5,v7\l\
+// Block 7; (5, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5,v7\l\nv13 := eq(1, v7)\l\
// "];
// Block1_7 -> Block1_7Exit;
-// Block1_7Exit [label="{ If v13| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_7Exit:0 -> Block1_10;
-// Block1_7Exit:1 -> Block1_9;
+// Block1_7Exit [label="{ If v13 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_7Exit:0 -> Block1_10 [style="solid"];
+// Block1_7Exit:1 -> Block1_9 [style="solid"];
// Block1_9 [label="\
-// Block 9\nLiveIn: \l\
+// Block 9; (6, max 6)\nLiveIn: \l\
// LiveOut: \l\nsstore(1028, 4)\l\
// "];
// Block1_9Exit [label="FunctionReturn[0]"];
// Block1_9 -> Block1_9Exit;
// Block1_10 [label="\
-// Block 10\nLiveIn: v0,v1,v5,v7\l\
+// Block 10; (7, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5,v7\l\nv20 := eq(2, v7)\l\
// "];
// Block1_10 -> Block1_10Exit;
-// Block1_10Exit [label="{ If v20| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_10Exit:0 -> Block1_13;
-// Block1_10Exit:1 -> Block1_12;
+// Block1_10Exit [label="{ If v20 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_10Exit:0 -> Block1_13 [style="solid"];
+// Block1_10Exit:1 -> Block1_12 [style="solid"];
// Block1_12 [label="\
-// Block 12\nLiveIn: \l\
+// Block 12; (8, max 8)\nLiveIn: \l\
// LiveOut: \l\nsstore(1542, 6)\l\
// revert(0, 0)\l\
// "];
// Block1_12Exit [label="Terminated"];
// Block1_12 -> Block1_12Exit;
// Block1_13 [label="\
-// Block 13\nLiveIn: v0,v1,v5,v7\l\
+// Block 13; (9, max 17)\nLiveIn: v0,v1,v5,v7\l\
// LiveOut: v0,v1,v5\l\nv25 := eq(3, v7)\l\
// "];
// Block1_13 -> Block1_13Exit;
-// Block1_13Exit [label="{ If v25| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_13Exit:0 -> Block1_16;
-// Block1_13Exit:1 -> Block1_15;
+// Block1_13Exit [label="{ If v25 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_13Exit:0 -> Block1_16 [style="solid"];
+// Block1_13Exit:1 -> Block1_15 [style="solid"];
// Block1_15 [label="\
-// Block 15\nLiveIn: v0,v1,v5\l\
+// Block 15; (10, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2056, 8)\l\
// "];
// Block1_15 -> Block1_15Exit [arrowhead=none];
// Block1_15Exit [label="Jump" shape=oval];
-// Block1_15Exit -> Block1_5;
+// Block1_15Exit -> Block1_5 [style="solid"];
// Block1_16 [label="\
-// Block 16\nLiveIn: v0,v1,v5\l\
+// Block 16; (15, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nv29 := mload(v1)\l\
// "];
// Block1_16 -> Block1_16Exit;
-// Block1_16Exit [label="{ If v29| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_16Exit:0 -> Block1_18;
-// Block1_16Exit:1 -> Block1_17;
+// Block1_16Exit [label="{ If v29 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_16Exit:0 -> Block1_18 [style="solid"];
+// Block1_16Exit:1 -> Block1_17 [style="solid"];
// Block1_5 [label="\
-// Block 5\nLiveIn: v0,v1,v5\l\
+// Block 5; (11, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2827, 11)\l\
// "];
// Block1_5 -> Block1_5Exit [arrowhead=none];
// Block1_5Exit [label="Jump" shape=oval];
-// Block1_5Exit -> Block1_3;
+// Block1_5Exit -> Block1_3 [style="solid"];
// Block1_17 [label="\
-// Block 17\nLiveIn: \l\
+// Block 17; (16, max 16)\nLiveIn: \l\
// LiveOut: \l\nreturn(0, 0)\l\
// "];
// Block1_17Exit [label="Terminated"];
// Block1_17 -> Block1_17Exit;
// Block1_18 [label="\
-// Block 18\nLiveIn: v0,v1,v5\l\
+// Block 18; (17, max 17)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v5\l\nsstore(2570, 10)\l\
// "];
// Block1_18 -> Block1_18Exit [arrowhead=none];
// Block1_18Exit [label="Jump" shape=oval];
-// Block1_18Exit -> Block1_5;
+// Block1_18Exit -> Block1_5 [style="solid"];
// Block1_3 [label="\
-// Block 3\nLiveIn: v0,v1,v5\l\
+// Block 3; (12, max 14)\nLiveIn: v0,v1,v5\l\
// LiveOut: v0,v1,v43\l\nv43 := add(1, v5)\l\
// v44 := calldataload(v43)\l\
// "];
// Block1_3 -> Block1_3Exit;
-// Block1_3Exit [label="{ If v44| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block1_3Exit:0 -> Block1_21;
-// Block1_3Exit:1 -> Block1_20;
+// Block1_3Exit [label="{ If v44 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block1_3Exit:0 -> Block1_21 [style="solid"];
+// Block1_3Exit:1 -> Block1_20 [style="solid"];
// Block1_20 [label="\
-// Block 20\nLiveIn: v43\l\
+// Block 20; (13, max 13)\nLiveIn: v43\l\
// LiveOut: \l\nsstore(v43, 0)\l\
// "];
// Block1_20Exit [label="FunctionReturn[0]"];
// Block1_20 -> Block1_20Exit;
// Block1_21 [label="\
-// Block 21\nLiveIn: v0,v1,v43\l\
+// Block 21; (14, max 14)\nLiveIn: v0,v1,v43\l\
// LiveOut: v0,v1,v43\l\nsstore(65535, 255)\l\
// "];
// Block1_21 -> Block1_21Exit [arrowhead=none];
// Block1_21Exit [label="Jump" shape=oval];
-// Block1_21Exit -> Block1_1;
+// Block1_21Exit -> Block1_1 [style="dashed"];
// }
diff --git a/test/libyul/yulSSAControlFlowGraph/function.yul b/test/libyul/yulSSAControlFlowGraph/function.yul
index 1b759f8b395a..acf1a37aa43f 100644
--- a/test/libyul/yulSSAControlFlowGraph/function.yul
+++ b/test/libyul/yulSSAControlFlowGraph/function.yul
@@ -27,7 +27,7 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 0)\nLiveIn: \l\
// LiveOut: \l\nv0, v1 := i()\l\
// h(v0)\l\
// "];
@@ -37,7 +37,7 @@
// r := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\nLiveIn: v0,v1\l\
+// Block 0; (0, max 0)\nLiveIn: v0,v1\l\
// LiveOut: v4\l\nv3 := add(v1, v0)\l\
// v4 := sub(v0, v3)\l\
// "];
@@ -47,7 +47,7 @@
// g()"];
// FunctionEntry_g_0 -> Block2_0;
// Block2_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 0)\nLiveIn: \l\
// LiveOut: \l\nsstore(257, 1)\l\
// "];
// Block2_0Exit [label="FunctionReturn[]"];
@@ -56,7 +56,7 @@
// h(v0)"];
// FunctionEntry_h_0 -> Block3_0;
// Block3_0 [label="\
-// Block 0\nLiveIn: v0\l\
+// Block 0; (0, max 0)\nLiveIn: v0\l\
// LiveOut: \l\nv2 := f(0, v0)\l\
// h(v2)\l\
// "];
@@ -66,7 +66,7 @@
// v, w := i()"];
// FunctionEntry_i_0 -> Block4_0;
// Block4_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 0)\nLiveIn: \l\
// LiveOut: \l\n"];
// Block4_0Exit [label="FunctionReturn[514, 771]"];
// Block4_0 -> Block4_0Exit;
diff --git a/test/libyul/yulSSAControlFlowGraph/if.yul b/test/libyul/yulSSAControlFlowGraph/if.yul
index 9c4d73a13a25..e2a66ab74fba 100644
--- a/test/libyul/yulSSAControlFlowGraph/if.yul
+++ b/test/libyul/yulSSAControlFlowGraph/if.yul
@@ -15,22 +15,22 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 2)\nLiveIn: \l\
// LiveOut: v1\l\nv1 := calldataload(3)\l\
// "];
// Block0_0 -> Block0_0Exit;
-// Block0_0Exit [label="{ If 0| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_0Exit:0 -> Block0_2;
-// Block0_0Exit:1 -> Block0_1;
+// Block0_0Exit [label="{ If 0 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_0Exit:0 -> Block0_2 [style="solid"];
+// Block0_0Exit:1 -> Block0_1 [style="solid"];
// Block0_1 [label="\
-// Block 1\nLiveIn: \l\
+// Block 1; (1, max 2)\nLiveIn: \l\
// LiveOut: v4\l\nv4 := calldataload(77)\l\
// "];
// Block0_1 -> Block0_1Exit [arrowhead=none];
// Block0_1Exit [label="Jump" shape=oval];
-// Block0_1Exit -> Block0_2;
+// Block0_1Exit -> Block0_2 [style="solid"];
// Block0_2 [label="\
-// Block 2\nLiveIn: v5\l\
+// Block 2; (2, max 2)\nLiveIn: v5\l\
// LiveOut: \l\nv5 := φ(\l\
// Block 0 => v1,\l\
// Block 1 => v4\l\
diff --git a/test/libyul/yulSSAControlFlowGraph/nested_for.yul b/test/libyul/yulSSAControlFlowGraph/nested_for.yul
index 0ed17c870a1f..b088e3b55ed8 100644
--- a/test/libyul/yulSSAControlFlowGraph/nested_for.yul
+++ b/test/libyul/yulSSAControlFlowGraph/nested_for.yul
@@ -26,170 +26,195 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\n"];
+// Block 0; (0, max 24)\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block0_0 -> Block0_0Exit [arrowhead=none];
// Block0_0Exit [label="Jump" shape=oval];
-// Block0_0Exit -> Block0_1;
+// Block0_0Exit -> Block0_1 [style="solid"];
// Block0_1 [label="\
-// Block 1\nv2 := φ(\l\
+// Block 1; (1, max 24)\nLiveIn: v2\l\
+// LiveOut: v2\l\nv2 := φ(\l\
// Block 0 => 0,\l\
// Block 3 => v36\l\
// )\l\
// v3 := lt(3, v2)\l\
// "];
// Block0_1 -> Block0_1Exit;
-// Block0_1Exit [label="{ If v3| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_1Exit:0 -> Block0_4;
-// Block0_1Exit:1 -> Block0_2;
+// Block0_1Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_1Exit:0 -> Block0_4 [style="solid"];
+// Block0_1Exit:1 -> Block0_2 [style="solid"];
// Block0_2 [label="\
-// Block 2\n"];
+// Block 2; (2, max 23)\nLiveIn: v2\l\
+// LiveOut: v2\l\n"];
// Block0_2 -> Block0_2Exit [arrowhead=none];
// Block0_2Exit [label="Jump" shape=oval];
-// Block0_2Exit -> Block0_5;
+// Block0_2Exit -> Block0_5 [style="solid"];
// Block0_4 [label="\
-// Block 4\n"];
+// Block 4; (24, max 24)\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block0_4Exit [label="MainExit"];
// Block0_4 -> Block0_4Exit;
// Block0_5 [label="\
-// Block 5\nv4 := φ(\l\
+// Block 5; (3, max 23)\nLiveIn: v2,v4\l\
+// LiveOut: v2,v4\l\nv4 := φ(\l\
// Block 2 => 0,\l\
// Block 7 => v35\l\
// )\l\
// v5 := lt(3, v4)\l\
// "];
// Block0_5 -> Block0_5Exit;
-// Block0_5Exit [label="{ If v5| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_5Exit:0 -> Block0_8;
-// Block0_5Exit:1 -> Block0_6;
+// Block0_5Exit [label="{ If v5 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_5Exit:0 -> Block0_8 [style="solid"];
+// Block0_5Exit:1 -> Block0_6 [style="solid"];
// Block0_6 [label="\
-// Block 6\n"];
+// Block 6; (4, max 21)\nLiveIn: v2,v4\l\
+// LiveOut: v2,v4\l\n"];
// Block0_6 -> Block0_6Exit [arrowhead=none];
// Block0_6Exit [label="Jump" shape=oval];
-// Block0_6Exit -> Block0_9;
+// Block0_6Exit -> Block0_9 [style="solid"];
// Block0_8 [label="\
-// Block 8\n"];
+// Block 8; (22, max 23)\nLiveIn: v2\l\
+// LiveOut: v2\l\n"];
// Block0_8 -> Block0_8Exit [arrowhead=none];
// Block0_8Exit [label="Jump" shape=oval];
-// Block0_8Exit -> Block0_3;
+// Block0_8Exit -> Block0_3 [style="solid"];
// Block0_9 [label="\
-// Block 9\nv6 := φ(\l\
+// Block 9; (5, max 21)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\nv6 := φ(\l\
// Block 6 => 0,\l\
// Block 11 => v31\l\
// )\l\
// v7 := lt(3, v6)\l\
// "];
// Block0_9 -> Block0_9Exit;
-// Block0_9Exit [label="{ If v7| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_9Exit:0 -> Block0_12;
-// Block0_9Exit:1 -> Block0_10;
+// Block0_9Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_9Exit:0 -> Block0_12 [style="solid"];
+// Block0_9Exit:1 -> Block0_10 [style="solid"];
// Block0_3 [label="\
-// Block 3\nv36 := add(1, v2)\l\
+// Block 3; (23, max 23)\nLiveIn: v2\l\
+// LiveOut: v36\l\nv36 := add(1, v2)\l\
// "];
// Block0_3 -> Block0_3Exit [arrowhead=none];
// Block0_3Exit [label="Jump" shape=oval];
-// Block0_3Exit -> Block0_1;
+// Block0_3Exit -> Block0_1 [style="dashed"];
// Block0_10 [label="\
-// Block 10\n"];
+// Block 10; (6, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_10 -> Block0_10Exit;
-// Block0_10Exit [label="{ If 0| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_10Exit:0 -> Block0_14;
-// Block0_10Exit:1 -> Block0_13;
+// Block0_10Exit [label="{ If 0 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_10Exit:0 -> Block0_14 [style="solid"];
+// Block0_10Exit:1 -> Block0_13 [style="solid"];
// Block0_12 [label="\
-// Block 12\n"];
+// Block 12; (20, max 21)\nLiveIn: v2,v4\l\
+// LiveOut: v2,v4\l\n"];
// Block0_12 -> Block0_12Exit [arrowhead=none];
// Block0_12Exit [label="Jump" shape=oval];
-// Block0_12Exit -> Block0_7;
+// Block0_12Exit -> Block0_7 [style="solid"];
// Block0_13 [label="\
-// Block 13\n"];
+// Block 13; (7, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_13 -> Block0_13Exit [arrowhead=none];
// Block0_13Exit [label="Jump" shape=oval];
-// Block0_13Exit -> Block0_15;
+// Block0_13Exit -> Block0_15 [style="solid"];
// Block0_14 [label="\
-// Block 14\n"];
+// Block 14; (12, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_14 -> Block0_14Exit;
-// Block0_14Exit [label="{ If 1| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_14Exit:0 -> Block0_20;
-// Block0_14Exit:1 -> Block0_19;
+// Block0_14Exit [label="{ If 1 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_14Exit:0 -> Block0_20 [style="solid"];
+// Block0_14Exit:1 -> Block0_19 [style="solid"];
// Block0_7 [label="\
-// Block 7\nv35 := add(1, v4)\l\
+// Block 7; (21, max 21)\nLiveIn: v2,v4\l\
+// LiveOut: v2,v35\l\nv35 := add(1, v4)\l\
// "];
// Block0_7 -> Block0_7Exit [arrowhead=none];
// Block0_7Exit [label="Jump" shape=oval];
-// Block0_7Exit -> Block0_5;
+// Block0_7Exit -> Block0_5 [style="dashed"];
// Block0_15 [label="\
-// Block 15\nv8 := φ(\l\
+// Block 15; (8, max 19)\nLiveIn: v2,v4,v6,v8\l\
+// LiveOut: v2,v4,v6,v8\l\nv8 := φ(\l\
// Block 13 => 0,\l\
// Block 17 => v16\l\
// )\l\
// v9 := lt(3, v8)\l\
// "];
// Block0_15 -> Block0_15Exit;
-// Block0_15Exit [label="{ If v9| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_15Exit:0 -> Block0_18;
-// Block0_15Exit:1 -> Block0_16;
+// Block0_15Exit [label="{ If v9 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_15Exit:0 -> Block0_18 [style="solid"];
+// Block0_15Exit:1 -> Block0_16 [style="solid"];
// Block0_19 [label="\
-// Block 19\n"];
+// Block 19; (13, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_19 -> Block0_19Exit [arrowhead=none];
// Block0_19Exit [label="Jump" shape=oval];
-// Block0_19Exit -> Block0_21;
+// Block0_19Exit -> Block0_21 [style="solid"];
// Block0_20 [label="\
-// Block 20\n"];
+// Block 20; (18, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_20 -> Block0_20Exit [arrowhead=none];
// Block0_20Exit [label="Jump" shape=oval];
-// Block0_20Exit -> Block0_11;
+// Block0_20Exit -> Block0_11 [style="solid"];
// Block0_16 [label="\
-// Block 16\nv13 := add(v4, v2)\l\
+// Block 16; (9, max 10)\nLiveIn: v2,v4,v6,v8\l\
+// LiveOut: v2,v4,v6,v8\l\nv13 := add(v4, v2)\l\
// v14 := add(v6, v13)\l\
// sstore(v14, v8)\l\
// "];
// Block0_16 -> Block0_16Exit [arrowhead=none];
// Block0_16Exit [label="Jump" shape=oval];
-// Block0_16Exit -> Block0_17;
+// Block0_16Exit -> Block0_17 [style="solid"];
// Block0_18 [label="\
-// Block 18\n"];
+// Block 18; (11, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_18 -> Block0_18Exit [arrowhead=none];
// Block0_18Exit [label="Jump" shape=oval];
-// Block0_18Exit -> Block0_14;
+// Block0_18Exit -> Block0_14 [style="solid"];
// Block0_21 [label="\
-// Block 21\nv19 := φ(\l\
+// Block 21; (14, max 19)\nLiveIn: v2,v4,v6,v19\l\
+// LiveOut: v2,v4,v6,v19\l\nv19 := φ(\l\
// Block 19 => 0,\l\
// Block 23 => v26\l\
// )\l\
// v20 := lt(3, v19)\l\
// "];
// Block0_21 -> Block0_21Exit;
-// Block0_21Exit [label="{ If v20| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_21Exit:0 -> Block0_24;
-// Block0_21Exit:1 -> Block0_22;
+// Block0_21Exit [label="{ If v20 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_21Exit:0 -> Block0_24 [style="solid"];
+// Block0_21Exit:1 -> Block0_22 [style="solid"];
// Block0_11 [label="\
-// Block 11\nv31 := add(1, v6)\l\
+// Block 11; (19, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v31\l\nv31 := add(1, v6)\l\
// "];
// Block0_11 -> Block0_11Exit [arrowhead=none];
// Block0_11Exit [label="Jump" shape=oval];
-// Block0_11Exit -> Block0_9;
+// Block0_11Exit -> Block0_9 [style="dashed"];
// Block0_17 [label="\
-// Block 17\nv16 := add(1, v8)\l\
+// Block 17; (10, max 10)\nLiveIn: v2,v4,v6,v8\l\
+// LiveOut: v2,v4,v6,v16\l\nv16 := add(1, v8)\l\
// "];
// Block0_17 -> Block0_17Exit [arrowhead=none];
// Block0_17Exit [label="Jump" shape=oval];
-// Block0_17Exit -> Block0_15;
+// Block0_17Exit -> Block0_15 [style="dashed"];
// Block0_22 [label="\
-// Block 22\nv24 := add(v4, v2)\l\
+// Block 22; (15, max 16)\nLiveIn: v2,v4,v6,v19\l\
+// LiveOut: v2,v4,v6,v19\l\nv24 := add(v4, v2)\l\
// v25 := add(v6, v24)\l\
// sstore(v25, v19)\l\
// "];
// Block0_22 -> Block0_22Exit [arrowhead=none];
// Block0_22Exit [label="Jump" shape=oval];
-// Block0_22Exit -> Block0_23;
+// Block0_22Exit -> Block0_23 [style="solid"];
// Block0_24 [label="\
-// Block 24\n"];
+// Block 24; (17, max 19)\nLiveIn: v2,v4,v6\l\
+// LiveOut: v2,v4,v6\l\n"];
// Block0_24 -> Block0_24Exit [arrowhead=none];
// Block0_24Exit [label="Jump" shape=oval];
-// Block0_24Exit -> Block0_20;
+// Block0_24Exit -> Block0_20 [style="solid"];
// Block0_23 [label="\
-// Block 23\nv26 := add(1, v19)\l\
+// Block 23; (16, max 16)\nLiveIn: v2,v4,v6,v19\l\
+// LiveOut: v2,v4,v6,v26\l\nv26 := add(1, v19)\l\
// "];
// Block0_23 -> Block0_23Exit [arrowhead=none];
// Block0_23Exit [label="Jump" shape=oval];
-// Block0_23Exit -> Block0_21;
+// Block0_23Exit -> Block0_21 [style="dashed"];
// }
diff --git a/test/libyul/yulSSAControlFlowGraph/nested_function.yul b/test/libyul/yulSSAControlFlowGraph/nested_function.yul
index 859b24151592..acda15ce81a5 100644
--- a/test/libyul/yulSSAControlFlowGraph/nested_function.yul
+++ b/test/libyul/yulSSAControlFlowGraph/nested_function.yul
@@ -38,14 +38,16 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\n"];
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block0_0Exit [label="MainExit"];
// Block0_0 -> Block0_0Exit;
// FunctionEntry_f_0 [label="function f:
// r := f(v0, v1)"];
// FunctionEntry_f_0 -> Block1_0;
// Block1_0 [label="\
-// Block 0\nv3 := add(v1, v0)\l\
+// Block 0; (0, max 0)\nLiveIn: v0,v1\l\
+// LiveOut: v4\l\nv3 := add(v1, v0)\l\
// v4 := sub(v0, v3)\l\
// "];
// Block1_0Exit [label="FunctionReturn[v4]"];
@@ -54,7 +56,8 @@
// g()"];
// FunctionEntry_g_0 -> Block2_0;
// Block2_0 [label="\
-// Block 0\nv1 := v()\l\
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: \l\nv1 := v()\l\
// v2 := f(2, v1)\l\
// v3 := z()\l\
// sstore(v2, v3)\l\
@@ -65,7 +68,8 @@
// r := z()"];
// FunctionEntry_z_0 -> Block3_0;
// Block3_0 [label="\
-// Block 0\nv1 := w()\l\
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: v1\l\nv1 := w()\l\
// "];
// Block3_0Exit [label="FunctionReturn[v1]"];
// Block3_0 -> Block3_0Exit;
@@ -73,14 +77,16 @@
// rw1 := w()"];
// FunctionEntry_w_0 -> Block4_0;
// Block4_0 [label="\
-// Block 0\n"];
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block4_0Exit [label="FunctionReturn[1]"];
// Block4_0 -> Block4_0Exit;
// FunctionEntry_v_0 [label="function v:
// r := v()"];
// FunctionEntry_v_0 -> Block5_0;
// Block5_0 [label="\
-// Block 0\nv1 := w()\l\
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: v1\l\nv1 := w()\l\
// "];
// Block5_0Exit [label="FunctionReturn[v1]"];
// Block5_0 -> Block5_0Exit;
@@ -88,27 +94,31 @@
// rw2 := w()"];
// FunctionEntry_w_0 -> Block6_0;
// Block6_0 [label="\
-// Block 0\n"];
+// Block 0; (0, max 0)\nLiveIn: \l\
+// LiveOut: \l\n"];
// Block6_0Exit [label="FunctionReturn[17]"];
// Block6_0 -> Block6_0Exit;
// FunctionEntry_cycle1_0 [label="function cycle1:
// r := cycle1()"];
// FunctionEntry_cycle1_0 -> Block7_0;
// Block7_0 [label="\
-// Block 0\nv2 := mload(3)\l\
+// Block 0; (0, max 2)\nLiveIn: \l\
+// LiveOut: \l\nv2 := mload(3)\l\
// "];
// Block7_0 -> Block7_0Exit;
-// Block7_0Exit [label="{ If v2| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block7_0Exit:0 -> Block7_2;
-// Block7_0Exit:1 -> Block7_1;
+// Block7_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block7_0Exit:0 -> Block7_2 [style="solid"];
+// Block7_0Exit:1 -> Block7_1 [style="solid"];
// Block7_1 [label="\
-// Block 1\nv3 := cycle2()\l\
+// Block 1; (1, max 2)\nLiveIn: \l\
+// LiveOut: v3\l\nv3 := cycle2()\l\
// "];
// Block7_1 -> Block7_1Exit [arrowhead=none];
// Block7_1Exit [label="Jump" shape=oval];
-// Block7_1Exit -> Block7_2;
+// Block7_1Exit -> Block7_2 [style="solid"];
// Block7_2 [label="\
-// Block 2\nv4 := φ(\l\
+// Block 2; (2, max 2)\nLiveIn: v4\l\
+// LiveOut: v4\l\nv4 := φ(\l\
// Block 0 => 0,\l\
// Block 1 => v3\l\
// )\l\
@@ -119,20 +129,23 @@
// r := cycle2()"];
// FunctionEntry_cycle2_0 -> Block8_0;
// Block8_0 [label="\
-// Block 0\nv2 := mload(4)\l\
+// Block 0; (0, max 2)\nLiveIn: \l\
+// LiveOut: \l\nv2 := mload(4)\l\
// "];
// Block8_0 -> Block8_0Exit;
-// Block8_0Exit [label="{ If v2| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block8_0Exit:0 -> Block8_2;
-// Block8_0Exit:1 -> Block8_1;
+// Block8_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block8_0Exit:0 -> Block8_2 [style="solid"];
+// Block8_0Exit:1 -> Block8_1 [style="solid"];
// Block8_1 [label="\
-// Block 1\nv3 := cycle1()\l\
+// Block 1; (1, max 2)\nLiveIn: \l\
+// LiveOut: v3\l\nv3 := cycle1()\l\
// "];
// Block8_1 -> Block8_1Exit [arrowhead=none];
// Block8_1Exit [label="Jump" shape=oval];
-// Block8_1Exit -> Block8_2;
+// Block8_1Exit -> Block8_2 [style="solid"];
// Block8_2 [label="\
-// Block 2\nv4 := φ(\l\
+// Block 2; (2, max 2)\nLiveIn: v4\l\
+// LiveOut: v4\l\nv4 := φ(\l\
// Block 0 => 0,\l\
// Block 1 => v3\l\
// )\l\
diff --git a/test/libyul/yulSSAControlFlowGraph/switch.yul b/test/libyul/yulSSAControlFlowGraph/switch.yul
index d744bd7bd247..10573b012298 100644
--- a/test/libyul/yulSSAControlFlowGraph/switch.yul
+++ b/test/libyul/yulSSAControlFlowGraph/switch.yul
@@ -22,32 +22,32 @@
// Entry0 [label="Entry"];
// Entry0 -> Block0_0;
// Block0_0 [label="\
-// Block 0\nLiveIn: \l\
+// Block 0; (0, max 5)\nLiveIn: \l\
// LiveOut: v3\l\nv1 := calldataload(3)\l\
// v3 := sload(0)\l\
// v4 := eq(0, v3)\l\
// "];
// Block0_0 -> Block0_0Exit;
-// Block0_0Exit [label="{ If v4| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_0Exit:0 -> Block0_3;
-// Block0_0Exit:1 -> Block0_2;
+// Block0_0Exit [label="{ If v4 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_0Exit:0 -> Block0_3 [style="solid"];
+// Block0_0Exit:1 -> Block0_2 [style="solid"];
// Block0_2 [label="\
-// Block 2\nLiveIn: \l\
+// Block 2; (1, max 2)\nLiveIn: \l\
// LiveOut: v6\l\nv6 := calldataload(77)\l\
// "];
// Block0_2 -> Block0_2Exit [arrowhead=none];
// Block0_2Exit [label="Jump" shape=oval];
-// Block0_2Exit -> Block0_1;
+// Block0_2Exit -> Block0_1 [style="solid"];
// Block0_3 [label="\
-// Block 3\nLiveIn: v3\l\
+// Block 3; (3, max 5)\nLiveIn: v3\l\
// LiveOut: \l\nv7 := eq(1, v3)\l\
// "];
// Block0_3 -> Block0_3Exit;
-// Block0_3Exit [label="{ If v7| { <0> Zero | <1> NonZero }}" shape=Mrecord];
-// Block0_3Exit:0 -> Block0_5;
-// Block0_3Exit:1 -> Block0_4;
+// Block0_3Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord];
+// Block0_3Exit:0 -> Block0_5 [style="solid"];
+// Block0_3Exit:1 -> Block0_4 [style="solid"];
// Block0_1 [label="\
-// Block 1\nLiveIn: v13\l\
+// Block 1; (2, max 2)\nLiveIn: v13\l\
// LiveOut: \l\nv13 := φ(\l\
// Block 2 => v6,\l\
// Block 4 => v10,\l\
@@ -58,17 +58,17 @@
// Block0_1Exit [label="MainExit"];
// Block0_1 -> Block0_1Exit;
// Block0_4 [label="\
-// Block 4\nLiveIn: \l\
+// Block 4; (4, max 4)\nLiveIn: \l\
// LiveOut: v10\l\nv10 := calldataload(88)\l\
// "];
// Block0_4 -> Block0_4Exit [arrowhead=none];
// Block0_4Exit [label="Jump" shape=oval];
-// Block0_4Exit -> Block0_1;
+// Block0_4Exit -> Block0_1 [style="solid"];
// Block0_5 [label="\
-// Block 5\nLiveIn: \l\
+// Block 5; (5, max 5)\nLiveIn: \l\
// LiveOut: v12\l\nv12 := calldataload(99)\l\
// "];
// Block0_5 -> Block0_5Exit [arrowhead=none];
// Block0_5Exit [label="Jump" shape=oval];
-// Block0_5Exit -> Block0_1;
+// Block0_5Exit -> Block0_1 [style="solid"];
// }
From 8a77a26861046a0a2a37cfc908edcc8947ffb91a Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Thu, 19 Sep 2024 14:56:20 +0200
Subject: [PATCH 004/394] SSACFGLiveness: add operation-wise live out
---
libyul/backends/evm/SSACFGLiveness.cpp | 32 ++++++++++++++++++++-
libyul/backends/evm/SSACFGLiveness.h | 12 +++++---
libyul/backends/evm/SSAControlFlowGraph.cpp | 4 +--
3 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/libyul/backends/evm/SSACFGLiveness.cpp b/libyul/backends/evm/SSACFGLiveness.cpp
index b9513f70dfce..85bd41d10ed6 100644
--- a/libyul/backends/evm/SSACFGLiveness.cpp
+++ b/libyul/backends/evm/SSACFGLiveness.cpp
@@ -29,12 +29,16 @@ SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg):
m_topologicalSort(_cfg),
m_loopNestingForest(m_topologicalSort),
m_liveIns(_cfg.numBlocks()),
- m_liveOuts(_cfg.numBlocks())
+ m_liveOuts(_cfg.numBlocks()),
+ m_operationLiveOuts(_cfg.numBlocks())
{
runDagDfs();
for (auto const loopRootNode: m_loopNestingForest.loopRootNodes())
runLoopTreeDfs(loopRootNode);
+
+ fillOperationsLiveOut();
}
+
void SSACFGLiveness::runDagDfs()
{
// SSA Book, Algorithm 9.2
@@ -103,6 +107,7 @@ void SSACFGLiveness::runDagDfs()
m_liveIns[blockId.value] = live + block.phis;
}
}
+
void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader)
{
// SSA Book, Algorithm 9.3
@@ -126,3 +131,28 @@ void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader)
}
}
}
+
+void SSACFGLiveness::fillOperationsLiveOut()
+{
+ auto const filterLiterals = [this](auto const& valueId){
+ return !std::holds_alternative(m_cfg.valueInfo(valueId));
+ };
+ for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
+ {
+ auto const& operations = m_cfg.block(SSACFG::BlockId{blockIdValue}).operations;
+ auto& liveOuts = m_operationLiveOuts[blockIdValue];
+ liveOuts.resize(operations.size());
+ if (!operations.empty())
+ {
+ auto live = m_liveOuts[blockIdValue];
+ auto rit = liveOuts.rbegin();
+ for (auto const& op: operations | ranges::views::reverse)
+ {
+ *rit = live;
+ live -= op.outputs | ranges::views::filter(filterLiterals) | ranges::to;
+ live += op.inputs | ranges::views::filter(filterLiterals) | ranges::to;
+ ++rit;
+ }
+ }
+ }
+}
diff --git a/libyul/backends/evm/SSACFGLiveness.h b/libyul/backends/evm/SSACFGLiveness.h
index 6f486b737f13..72752cfefd45 100644
--- a/libyul/backends/evm/SSACFGLiveness.h
+++ b/libyul/backends/evm/SSACFGLiveness.h
@@ -35,21 +35,25 @@ namespace solidity::yul
class SSACFGLiveness
{
public:
+ using LivenessData = std::set;
explicit SSACFGLiveness(SSACFG const& _cfg);
- std::vector> const& liveIns() const { return m_liveIns; }
- std::vector> const& liveOuts() const { return m_liveOuts; }
+ LivenessData const& liveIn(SSACFG::BlockId _blockId) const { return m_liveIns[_blockId.value]; }
+ LivenessData const& liveOut(SSACFG::BlockId _blockId) const { return m_liveOuts[_blockId.value]; }
+ std::vector const& operationsLiveOut(SSACFG::BlockId _blockId) const { return m_operationLiveOuts[_blockId.value]; }
ForwardSSACFGTopologicalSort const& topologicalSort() const { return m_topologicalSort; }
private:
void runDagDfs();
void runLoopTreeDfs(size_t _loopHeader);
+ void fillOperationsLiveOut();
SSACFG const& m_cfg;
ForwardSSACFGTopologicalSort m_topologicalSort;
SSACFGLoopNestingForest m_loopNestingForest;
- std::vector> m_liveIns;
- std::vector> m_liveOuts;
+ std::vector m_liveIns;
+ std::vector m_liveOuts;
+ std::vector> m_operationLiveOuts;
};
}
diff --git a/libyul/backends/evm/SSAControlFlowGraph.cpp b/libyul/backends/evm/SSAControlFlowGraph.cpp
index ebd6d84a9680..35c9db0dfe8c 100644
--- a/libyul/backends/evm/SSAControlFlowGraph.cpp
+++ b/libyul/backends/evm/SSAControlFlowGraph.cpp
@@ -125,11 +125,11 @@ class SSACFGPrinter
);
m_result << fmt::format(
"LiveIn: {}\\l\\\n",
- fmt::join(m_liveness->liveIns()[_id.value] | ranges::views::transform(valueToString), ",")
+ fmt::join(m_liveness->liveIn(_id) | ranges::views::transform(valueToString), ",")
);
m_result << fmt::format(
"LiveOut: {}\\l\\n",
- fmt::join(m_liveness->liveOuts()[_id.value] | ranges::views::transform(valueToString), ",")
+ fmt::join(m_liveness->liveOut(_id) | ranges::views::transform(valueToString), ",")
);
}
else
From fecd1329cd7a2bb0b77428ac8d02bb6b86c72d24 Mon Sep 17 00:00:00 2001
From: rodiazet
Date: Wed, 9 Oct 2024 13:06:41 +0200
Subject: [PATCH 005/394] eof: Support EOF asm analysis
---
libyul/AsmAnalysis.cpp | 43 +++++++++++++++++--
libyul/AsmAnalysis.h | 4 ++
test/Common.cpp | 2 +
test/TestCase.cpp | 30 ++++++++++++-
test/TestCase.h | 4 ++
.../eof/call_intruction_in_eof.yul | 13 ++++++
6 files changed, 91 insertions(+), 5 deletions(-)
create mode 100644 test/libyul/yulSyntaxTests/eof/call_intruction_in_eof.yul
diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp
index cf8c720f6b36..682931dc414d 100644
--- a/libyul/AsmAnalysis.cpp
+++ b/libyul/AsmAnalysis.cpp
@@ -625,12 +625,16 @@ void AsmAnalyzer::expectValidIdentifier(YulName _identifier, SourceLocation cons
bool AsmAnalyzer::validateInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
{
// NOTE: This function uses the default EVM version instead of the currently selected one.
- // TODO: Add EOF support
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}, std::nullopt).builtin(YulName(_instructionIdentifier));
if (builtin && builtin->instruction.has_value())
return validateInstructions(builtin->instruction.value(), _location);
- else
- return false;
+
+ // TODO: Change `prague()` to `EVMVersion{}` once EOF gets deployed
+ auto const eofBuiltin = EVMDialect::strictAssemblyForEVM(EVMVersion::prague(), 1).builtin(YulName(_instructionIdentifier));
+ if (eofBuiltin && eofBuiltin->instruction.has_value())
+ return validateInstructions(eofBuiltin->instruction.value(), _location);
+
+ return false;
}
bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocation const& _location)
@@ -702,9 +706,42 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
"PC instruction is a low-level EVM feature. "
"Because of that PC is disallowed in strict assembly."
);
+ else if (m_eofVersion.has_value() && (
+ _instr == evmasm::Instruction::CALL ||
+ _instr == evmasm::Instruction::CALLCODE ||
+ _instr == evmasm::Instruction::DELEGATECALL ||
+ _instr == evmasm::Instruction::SELFDESTRUCT ||
+ _instr == evmasm::Instruction::JUMP ||
+ _instr == evmasm::Instruction::JUMPI ||
+ _instr == evmasm::Instruction::PC ||
+ _instr == evmasm::Instruction::CREATE ||
+ _instr == evmasm::Instruction::CODESIZE ||
+ _instr == evmasm::Instruction::CODECOPY ||
+ _instr == evmasm::Instruction::EXTCODESIZE ||
+ _instr == evmasm::Instruction::EXTCODECOPY ||
+ _instr == evmasm::Instruction::GAS
+ ))
+ {
+ m_errorReporter.typeError(
+ 9132_error,
+ _location,
+ fmt::format(
+ "The \"{instruction}\" instruction is {kind} VMs (you are currently compiling to EOF).",
+ fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)),
+ fmt::arg("kind", "only available in legacy bytecode")
+ )
+ );
+ }
else
+ {
+ // Sanity check
+ solAssert(m_evmVersion.hasOpcode(_instr, m_eofVersion));
return false;
+ }
+ // Sanity check
+ // PC is not available in strict assembly but it is always valid opcode in legacy evm.
+ solAssert(_instr == evmasm::Instruction::PC || !m_evmVersion.hasOpcode(_instr, m_eofVersion));
return true;
}
diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h
index 64cab51f54c5..d8a2b2b1ac5f 100644
--- a/libyul/AsmAnalysis.h
+++ b/libyul/AsmAnalysis.h
@@ -70,7 +70,10 @@ class AsmAnalyzer
m_dataNames(std::move(_dataNames))
{
if (EVMDialect const* evmDialect = dynamic_cast(&m_dialect))
+ {
m_evmVersion = evmDialect->evmVersion();
+ m_eofVersion = evmDialect->eofVersion();
+ }
}
bool analyze(Block const& _block);
@@ -125,6 +128,7 @@ class AsmAnalyzer
AsmAnalysisInfo& m_info;
langutil::ErrorReporter& m_errorReporter;
langutil::EVMVersion m_evmVersion;
+ std::optional m_eofVersion;
Dialect const& m_dialect;
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set m_dataNames;
diff --git a/test/Common.cpp b/test/Common.cpp
index ae725774c48a..bab95c457b6a 100644
--- a/test/Common.cpp
+++ b/test/Common.cpp
@@ -155,6 +155,8 @@ void CommonOptions::validate() const
std::cout << "- ABI coder: v1 (default: v2)" << std::endl;
std::cout << std::endl << "DO NOT COMMIT THE UPDATED EXPECTATIONS." << std::endl << std::endl;
}
+
+ assertThrow(!eofVersion().has_value() || evmVersion() >= langutil::EVMVersion::prague(), ConfigException, "EOF is unavailable before Prague fork.");
}
bool CommonOptions::parse(int argc, char const* const* argv)
diff --git a/test/TestCase.cpp b/test/TestCase.cpp
index ae89bb56f31a..942153d3cd6c 100644
--- a/test/TestCase.cpp
+++ b/test/TestCase.cpp
@@ -96,8 +96,7 @@ TestCase::TestResult TestCase::checkResult(std::ostream& _stream, const std::str
return TestResult::Success;
}
-EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename):
- TestCase(_filename)
+void EVMVersionRestrictedTestCase::processEVMVersionSetting()
{
std::string versionString = m_reader.stringSetting("EVMVersion", "any");
if (versionString == "any")
@@ -139,3 +138,30 @@ EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _f
if (!comparisonResult)
m_shouldRun = false;
}
+
+void EVMVersionRestrictedTestCase::processBytecodeFormatSetting()
+{
+ std::optional eofVersion = solidity::test::CommonOptions::get().eofVersion();
+ // TODO: Update if EOF moved to Osaka
+ // EOF only available since Prague
+ solAssert(!eofVersion.has_value() ||solidity::test::CommonOptions::get().evmVersion() >= langutil::EVMVersion::prague());
+
+ std::string bytecodeFormatString = m_reader.stringSetting("bytecodeFormat", "legacy");
+ if (bytecodeFormatString == "legacy,>=EOFv1" || bytecodeFormatString == ">=EOFv1,legacy")
+ return;
+
+ // TODO: This is naive implementation because for now we support only one EOF version.
+ if (bytecodeFormatString == "legacy" && eofVersion.has_value())
+ m_shouldRun = false;
+ else if (bytecodeFormatString == ">=EOFv1" && !eofVersion.has_value())
+ m_shouldRun = false;
+ else if (bytecodeFormatString != "legacy" && bytecodeFormatString != ">=EOFv1" )
+ BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid bytecodeFormat flag: \"" + bytecodeFormatString + "\""});
+}
+
+EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename):
+ TestCase(_filename)
+{
+ processEVMVersionSetting();
+ processBytecodeFormatSetting();
+}
diff --git a/test/TestCase.h b/test/TestCase.h
index 301dbfb658e4..bdf51d2a5566 100644
--- a/test/TestCase.h
+++ b/test/TestCase.h
@@ -112,6 +112,10 @@ class TestCase
class EVMVersionRestrictedTestCase: public TestCase
{
+private:
+ void processEVMVersionSetting();
+ void processBytecodeFormatSetting();
+
protected:
EVMVersionRestrictedTestCase(std::string const& _filename);
};
diff --git a/test/libyul/yulSyntaxTests/eof/call_intruction_in_eof.yul b/test/libyul/yulSyntaxTests/eof/call_intruction_in_eof.yul
new file mode 100644
index 000000000000..d26af22a70dc
--- /dev/null
+++ b/test/libyul/yulSyntaxTests/eof/call_intruction_in_eof.yul
@@ -0,0 +1,13 @@
+object "a" {
+ code {
+ let success := call(gas(), 0x1, 0, 128, 4, 128, 0)
+ }
+}
+// ====
+// EVMVersion: >=shanghai
+// bytecodeFormat: >=EOFv1
+// ----
+// TypeError 9132: (47-51): The "call" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
+// TypeError 9132: (52-55): The "gas" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
+// TypeError 3950: (52-57): Expected expression to evaluate to one value, but got 0 values instead.
+// DeclarationError 3812: (32-82): Variable count mismatch for declaration of "success": 1 variables and 0 values.
From 91a37af31ae42d73aa591fca1e42de2fa37a7007 Mon Sep 17 00:00:00 2001
From: Matheus Aguiar
Date: Tue, 8 Oct 2024 12:19:32 -0300
Subject: [PATCH 006/394] Set next version (0.8.29)
---
CMakeLists.txt | 2 +-
Changelog.md | 11 +++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7c36ff73b436..b78269a3d6c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
-set(PROJECT_VERSION "0.8.28")
+set(PROJECT_VERSION "0.8.29")
# OSX target needed in order to support std::visit
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
diff --git a/Changelog.md b/Changelog.md
index eabd5b9eee9d..13af14b75b97 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,14 @@
+### 0.8.29 (unreleased)
+
+Language Features:
+
+
+Compiler Features:
+
+
+Bugfixes:
+
+
### 0.8.28 (2024-10-09)
Language Features:
From 7210680329061d0a1f0e03a1431a9781fd79708d Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Thu, 10 Oct 2024 17:05:48 +0200
Subject: [PATCH 007/394] Yul: SSACFGLiveness simplifications
---
libyul/backends/evm/SSACFGLiveness.cpp | 50 +++++++++++++-------------
libyul/backends/evm/SSACFGLiveness.h | 2 +-
2 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/libyul/backends/evm/SSACFGLiveness.cpp b/libyul/backends/evm/SSACFGLiveness.cpp
index 85bd41d10ed6..4537c8c5d8fc 100644
--- a/libyul/backends/evm/SSACFGLiveness.cpp
+++ b/libyul/backends/evm/SSACFGLiveness.cpp
@@ -24,6 +24,17 @@
using namespace solidity::yul;
+namespace
+{
+constexpr auto literalsFilter(SSACFG const& _cfg)
+{
+ return [&_cfg](SSACFG::ValueId const& _valueId) -> bool
+ {
+ return !std::holds_alternative(_cfg.valueInfo(_valueId));;
+ };
+}
+}
+
SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg):
m_cfg(_cfg),
m_topologicalSort(_cfg),
@@ -46,9 +57,6 @@ void SSACFGLiveness::runDagDfs()
{
// post-order traversal
SSACFG::BlockId blockId{blockIdValue};
- auto const filterLiterals = [this](auto const& valueId){
- return !std::holds_alternative(m_cfg.valueInfo(valueId));
- };
auto const& block = m_cfg.block(blockId);
// live <- PhiUses(B)
@@ -66,15 +74,10 @@ void SSACFGLiveness::runDagDfs()
auto const it = entries.find(blockId);
yulAssert(it != entries.end());
auto const argIndex = static_cast(std::distance(entries.begin(), it));
- if (argIndex < std::get(info).arguments.size())
- {
- // todo not sure why arg index would exceed phi arg length but it does happen
- auto const arg = std::get(info).arguments.at(argIndex);
- if (!std::holds_alternative(m_cfg.valueInfo(arg)))
- live.insert(arg);
- }
- else
- yulAssert(false);
+ yulAssert(argIndex < std::get(info).arguments.size());
+ auto const arg = std::get(info).arguments.at(argIndex);
+ if (!std::holds_alternative(m_cfg.valueInfo(arg)))
+ live.insert(arg);
}
});
@@ -86,7 +89,7 @@ void SSACFGLiveness::runDagDfs()
});
if (std::holds_alternative(block.exit))
live += std::get(block.exit).returnValues
- | ranges::views::filter(filterLiterals);
+ | ranges::views::filter(literalsFilter(m_cfg));
// clean out unreachables
live = live | ranges::views::filter([&](auto const& valueId) { return !std::holds_alternative(m_cfg.valueInfo(valueId)); }) | ranges::to;
@@ -98,9 +101,9 @@ void SSACFGLiveness::runDagDfs()
for (auto const& op: block.operations | ranges::views::reverse)
{
// remove variables defined at p from live
- live -= op.outputs | ranges::views::filter(filterLiterals) | ranges::to;
+ live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to;
// add uses at p to live
- live += op.inputs | ranges::views::filter(filterLiterals) | ranges::to;
+ live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to;
}
// livein(b) <- live \cup PhiDefs(B)
@@ -120,23 +123,20 @@ void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader)
// must be live out of header if live in of children
m_liveOuts[_loopHeader] += liveLoop;
// for each blockId \in children(loopHeader)
- for (size_t blockId = 0; blockId < m_cfg.numBlocks(); ++blockId)
- if (m_loopNestingForest.loopParents()[blockId] == _loopHeader)
+ for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
+ if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader)
{
// propagate loop liveness information down to the loop header's children
- m_liveIns[blockId] += liveLoop;
- m_liveOuts[blockId] += liveLoop;
+ m_liveIns[blockIdValue] += liveLoop;
+ m_liveOuts[blockIdValue] += liveLoop;
- runLoopTreeDfs(blockId);
+ runLoopTreeDfs(blockIdValue);
}
}
}
void SSACFGLiveness::fillOperationsLiveOut()
{
- auto const filterLiterals = [this](auto const& valueId){
- return !std::holds_alternative(m_cfg.valueInfo(valueId));
- };
for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
{
auto const& operations = m_cfg.block(SSACFG::BlockId{blockIdValue}).operations;
@@ -149,8 +149,8 @@ void SSACFGLiveness::fillOperationsLiveOut()
for (auto const& op: operations | ranges::views::reverse)
{
*rit = live;
- live -= op.outputs | ranges::views::filter(filterLiterals) | ranges::to;
- live += op.inputs | ranges::views::filter(filterLiterals) | ranges::to;
+ live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to;
+ live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to;
++rit;
}
}
diff --git a/libyul/backends/evm/SSACFGLiveness.h b/libyul/backends/evm/SSACFGLiveness.h
index 72752cfefd45..b0b9ccb7a3dc 100644
--- a/libyul/backends/evm/SSACFGLiveness.h
+++ b/libyul/backends/evm/SSACFGLiveness.h
@@ -29,7 +29,7 @@
namespace solidity::yul
{
-/// Performs liveness analysis on an SSA CFG following Algorithm 9.1 in [1].
+/// Performs liveness analysis on a reducible SSA CFG following Algorithm 9.1 in [1].
///
/// [1] Rastello, Fabrice, and Florent Bouchez Tichadou, eds. SSA-based Compiler Design. Springer, 2022.
class SSACFGLiveness
From 3edd2e173828737f6250256380e2d1193e232969 Mon Sep 17 00:00:00 2001
From: Ustas <82833595+ustas-eth@users.noreply.github.com>
Date: Sun, 21 Jan 2024 13:48:21 +0000
Subject: [PATCH 008/394] [docs] Add warning about `codehash` behavior
depending on the account state
Co-authored-by: r0qs
---
docs/types/value-types.rst | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst
index 27baf5485104..5efb080554a8 100644
--- a/docs/types/value-types.rst
+++ b/docs/types/value-types.rst
@@ -344,6 +344,13 @@ You can query the deployed code for any smart contract. Use ``.code`` to get the
``bytes memory``, which might be empty. Use ``.codehash`` to get the Keccak-256 hash of that code
(as a ``bytes32``). Note that ``addr.codehash`` is cheaper than using ``keccak256(addr.code)``.
+.. warning::
+ The output of ``addr.codehash`` may be ``0`` if the account associated with ``addr`` is empty or non-existent
+ (i.e., it has no code, zero balance, and zero nonce as defined by `EIP-161 `_).
+ If the account has no code but a non-zero balance or nonce, then ``addr.codehash`` will output the Keccak-256 hash of empty data
+ (i.e., ``keccak256("")`` which is equal to ``c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470``), as defined by
+ `EIP-1052 `_.
+
.. note::
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
current contract using ``address(this).balance``.
From 79ad9610dd2d94f11b204d418db5159d7c95eea8 Mon Sep 17 00:00:00 2001
From: sukey2008
Date: Thu, 10 Oct 2024 21:33:44 +0200
Subject: [PATCH 009/394] docs: fix several spelling and grammar issues
---
docs/types/value-types.rst | 6 +++---
test/externalTests/README.md | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst
index 5efb080554a8..9b5ee447e6d6 100644
--- a/docs/types/value-types.rst
+++ b/docs/types/value-types.rst
@@ -9,7 +9,7 @@ are used as function arguments or in assignments.
Unlike :ref:`reference types `, value type declarations do not
specify a data location since they are small enough to be stored on the stack.
-The only exception are :ref:`state variables `.
+The only exception is :ref:`state variables `.
Those are by default located in storage, but can also be marked as
:ref:`transient `, :ref:`constant or immutable `.
@@ -204,7 +204,7 @@ must be explicit via ``payable()``.
Explicit conversions to and from ``address`` are allowed for ``uint160``, integer literals,
``bytes20`` and contract types.
-Only expressions of type ``address`` and contract-type can be converted to the type ``address
+Only expressions of type ``address`` and contract type can be converted to the type ``address
payable`` via the explicit conversion ``payable(...)``. For contract-type, this conversion is only
allowed if the contract can receive Ether, i.e., the contract either has a :ref:`receive
` or a payable fallback function. Note that ``payable(0)`` is valid and is
@@ -215,7 +215,7 @@ an exception to this rule.
declare its type as ``address payable`` to make this requirement visible. Also,
try to make this distinction or conversion as early as possible.
- The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0.
+ The distinction between ``address`` and ``address payable`` was introduced in version 0.5.0.
Also starting from that version, contracts are not implicitly convertible to the ``address`` type, but can still be explicitly converted to
``address`` or to ``address payable``, if they have a receive or payable fallback function.
diff --git a/test/externalTests/README.md b/test/externalTests/README.md
index 8df5b4dfe043..91e2ef9aff4d 100644
--- a/test/externalTests/README.md
+++ b/test/externalTests/README.md
@@ -88,6 +88,6 @@ branches if they exist. If no changes on our part were necessary, it is complete
e.g. the `master_060` of an external project in Solidity 0.8.x.
Since each project is handled separately, this approach may result in a mix of version-specific branches
-between different external projects. For example, in one project we could could have `master_050` on
+between different external projects. For example, in one project we could have `master_050` on
both `develop` and `breaking` and in another `breaking` could use `master_080` while `develop` still
uses `master_060`.
From e8b5ca83f37b6a126db6a0a1dcfa00decff63647 Mon Sep 17 00:00:00 2001
From: Jeff Wentworth
Date: Fri, 11 Oct 2024 21:25:17 +0900
Subject: [PATCH 010/394] Update Natspec format to correct devdoc return
parameters (#15187)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Update natspec-format.rst for devdoc return values
* update changelog
* values ==> returns
* Fix Solidity and improve doc output
* Update Changelog.md
Co-authored-by: Nikola Matić
* Apply suggestions from code review
Co-authored-by: Nikola Matić
---------
Co-authored-by: Nikola Matić
---
docs/natspec-format.rst | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst
index b187ea84c74f..f1910648378f 100644
--- a/docs/natspec-format.rst
+++ b/docs/natspec-format.rst
@@ -73,8 +73,9 @@ The following example shows a contract and a function using all available tags.
/// @dev The Alexandr N. Tetearing algorithm could increase precision
/// @param rings The number of rings from dendrochronological sample
/// @return Age in years, rounded up for partial years
- function age(uint256 rings) external virtual pure returns (uint256) {
- return rings + 1;
+ /// @return Name of the tree
+ function age(uint256 rings) external virtual pure returns (uint256, string memory) {
+ return (rings + 1, "tree");
}
/// @notice Returns the amount of leaves the tree has.
@@ -91,8 +92,8 @@ The following example shows a contract and a function using all available tags.
}
contract KumquatTree is Tree, Plant {
- function age(uint256 rings) external override pure returns (uint256) {
- return rings + 2;
+ function age(uint256 rings) external override pure returns (uint256, string memory) {
+ return (rings + 2, "Kumquat");
}
/// Return the amount of leaves that this specific kind of tree has
@@ -196,7 +197,7 @@ User Documentation
------------------
The above documentation will produce the following user documentation
-JSON file as output:
+JSON file as output for the ``Tree`` contract:
.. code-block:: json
@@ -209,6 +210,10 @@ JSON file as output:
{
"notice" : "Calculate tree age in years, rounded up, for live trees"
}
+ "leaves()" :
+ {
+ "notice" : "Returns the amount of leaves the tree has."
+ }
},
"notice" : "You can use this contract for only the most basic simulation"
}
@@ -243,7 +248,14 @@ file should also be produced and should look like this:
{
"rings" : "The number of rings from dendrochronological sample"
},
- "return" : "age in years, rounded up for partial years"
+ "returns" : {
+ "_0" : "Age in years, rounded up for partial years",
+ "_1" : "Name of the tree"
+ }
+ },
+ "leaves()" :
+ {
+ "details" : "Returns only a fixed number."
}
},
"title" : "A simulator for trees"
From 520ee5a9c009098a4d94cdc9fbb78896585a8057 Mon Sep 17 00:00:00 2001
From: clonker <1685266+clonker@users.noreply.github.com>
Date: Fri, 11 Oct 2024 15:07:47 +0200
Subject: [PATCH 011/394] Compiler stack: Protect loadGeneratedIR from
contracts that cannot be deployed
---
libsolidity/interface/CompilerStack.cpp | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index c68df205f941..007b6187c747 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -982,7 +982,9 @@ Json CompilerStack::yulIRAst(std::string const& _contractName) const
// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
- return loadGeneratedIR(contract(_contractName).yulIR).astJson();
+ auto const& currentContract = contract(_contractName);
+ yulAssert(currentContract.contract);
+ return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIR).astJson() : Json{};
}
Json CompilerStack::yulCFGJson(std::string const& _contractName) const
@@ -992,7 +994,9 @@ Json CompilerStack::yulCFGJson(std::string const& _contractName) const
// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
- return loadGeneratedIR(contract(_contractName).yulIR).cfgJson();
+ auto const& currentContract = contract(_contractName);
+ yulAssert(currentContract.contract);
+ return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIR).cfgJson() : Json{};
}
std::string const& CompilerStack::yulIROptimized(std::string const& _contractName) const
@@ -1008,7 +1012,9 @@ Json CompilerStack::yulIROptimizedAst(std::string const& _contractName) const
// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
- return loadGeneratedIR(contract(_contractName).yulIROptimized).astJson();
+ auto const& currentContract = contract(_contractName);
+ yulAssert(currentContract.contract);
+ return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIROptimized).astJson() : Json{};
}
evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractName) const
From c38e4e66a181ff623a21073a78bdefe61ca80c7d Mon Sep 17 00:00:00 2001
From: rodiazet
Date: Fri, 11 Oct 2024 14:57:24 +0200
Subject: [PATCH 012/394] Pass `evmVersion` and `eofVersion` to `YulStack` in
`ObjectCompilerTest`
---
test/libyul/ControlFlowSideEffectsTest.cpp | 4 +-
test/libyul/EVMCodeTransformTest.cpp | 5 +-
test/libyul/FunctionSideEffects.cpp | 4 +-
test/libyul/KnowledgeBaseTest.cpp | 4 +-
test/libyul/ObjectCompilerTest.cpp | 6 +-
test/libyul/ObjectCompilerTest.h | 2 +-
test/libyul/ObjectParser.cpp | 4 +-
test/libyul/Parser.cpp | 172 +++++++++---------
test/libyul/YulInterpreterTest.cpp | 3 +-
test/libyul/objectCompiler/datacopy.yul | 2 +
.../libyul/objectCompiler/dataoffset_code.yul | 2 +
.../libyul/objectCompiler/dataoffset_data.yul | 2 +
.../libyul/objectCompiler/dataoffset_self.yul | 2 +
test/libyul/objectCompiler/datasize_code.yul | 2 +
test/libyul/objectCompiler/datasize_data.yul | 2 +
test/libyul/objectCompiler/datasize_self.yul | 2 +
.../libyul/objectCompiler/function_series.yul | 1 +
.../identical_subobjects_full_debug_info.yul | 1 +
.../identical_subobjects_no_debug_info.yul | 1 +
...dentical_subobjects_partial_debug_info.yul | 1 +
...bobjects_partial_debug_info_no_use_src.yul | 1 +
...cal_subobjects_with_subject_references.yul | 1 +
..._long_name_does_not_end_up_in_bytecode.yul | 2 +
.../leading_and_trailing_dots.yul | 2 +
test/libyul/objectCompiler/linkersymbol.yul | 2 +
.../objectCompiler/long_object_name.yul | 1 +
test/libyul/objectCompiler/manySubObjects.yul | 2 +
test/libyul/objectCompiler/metadata.yul | 2 +
.../libyul/objectCompiler/namedObjectCode.yul | 2 +
.../objectCompiler/nested_optimizer.yul | 1 +
test/libyul/objectCompiler/simple.yul | 2 +
.../objectCompiler/simple_optimizer.yul | 1 +
.../libyul/objectCompiler/sourceLocations.yul | 2 +
.../libyul/objectCompiler/subObjectAccess.yul | 2 +
test/libyul/objectCompiler/verbatim_bug.yul | 1 +
35 files changed, 144 insertions(+), 102 deletions(-)
diff --git a/test/libyul/ControlFlowSideEffectsTest.cpp b/test/libyul/ControlFlowSideEffectsTest.cpp
index 50c69b179dd1..b9d883dcde06 100644
--- a/test/libyul/ControlFlowSideEffectsTest.cpp
+++ b/test/libyul/ControlFlowSideEffectsTest.cpp
@@ -62,9 +62,9 @@ TestCase::TestResult ControlFlowSideEffectsTest::run(std::ostream& _stream, std:
if (!obj.hasCode())
BOOST_THROW_EXCEPTION(std::runtime_error("Parsing input failed."));
- // TODO: Add EOF support
ControlFlowSideEffectsCollector sideEffects(
- EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(), std::nullopt),
+ EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion(),
+ solidity::test::CommonOptions::get().eofVersion()),
obj.code()->root()
);
m_obtainedResult.clear();
diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp
index 23c24ce305fd..75d86136e9e0 100644
--- a/test/libyul/EVMCodeTransformTest.cpp
+++ b/test/libyul/EVMCodeTransformTest.cpp
@@ -67,14 +67,13 @@ TestCase::TestResult EVMCodeTransformTest::run(std::ostream& _stream, std::strin
return TestResult::FatalError;
}
- // TODO: Add EOF support
evmasm::Assembly assembly{solidity::test::CommonOptions::get().evmVersion(), false, std::nullopt, {}};
EthAssemblyAdapter adapter(assembly);
EVMObjectCompiler::compile(
*stack.parserResult(),
adapter,
- // TODO: Make sure that why we cannot pass here solidity::test::CommonOptions::get().evmVersion() and assembly.eofVersion()
- EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}, std::nullopt),
+ EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion(),
+ solidity::test::CommonOptions::get().eofVersion()),
m_stackOpt,
std::nullopt
);
diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp
index 7a153de7d362..a878d375d02b 100644
--- a/test/libyul/FunctionSideEffects.cpp
+++ b/test/libyul/FunctionSideEffects.cpp
@@ -88,9 +88,9 @@ TestCase::TestResult FunctionSideEffects::run(std::ostream& _stream, std::string
if (!obj.hasCode())
BOOST_THROW_EXCEPTION(std::runtime_error("Parsing input failed."));
- // TODO: Add EOF support
std::map functionSideEffects = SideEffectsPropagator::sideEffects(
- EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(), std::nullopt),
+ EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion(),
+ solidity::test::CommonOptions::get().eofVersion()),
CallGraphGenerator::callGraph(obj.code()->root())
);
diff --git a/test/libyul/KnowledgeBaseTest.cpp b/test/libyul/KnowledgeBaseTest.cpp
index badeb21fe7f9..253e7e0d5ae0 100644
--- a/test/libyul/KnowledgeBaseTest.cpp
+++ b/test/libyul/KnowledgeBaseTest.cpp
@@ -63,8 +63,8 @@ class KnowledgeBaseTest
return KnowledgeBase([this](YulName _var) { return util::valueOrNullptr(m_values, _var); });
}
- // TODO: Add EOF support
- EVMDialect m_dialect{EVMVersion{}, std::nullopt, true};
+ EVMDialect m_dialect{solidity::test::CommonOptions::get().evmVersion(),
+ solidity::test::CommonOptions::get().eofVersion(), true};
std::shared_ptr