Skip to content

Commit 10dde18

Browse files
committed
Yul: add node id dispenser for numerical ids
1 parent 3d47deb commit 10dde18

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed

Diff for: libyul/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ add_library(yul
159159
optimiser/NameDisplacer.h
160160
optimiser/NameSimplifier.cpp
161161
optimiser/NameSimplifier.h
162+
optimiser/NodeIdDispenser.cpp
163+
optimiser/NodeIdDispenser.h
162164
optimiser/OptimiserStep.h
163165
optimiser/OptimizerUtilities.cpp
164166
optimiser/OptimizerUtilities.h

Diff for: libyul/optimiser/NodeIdDispenser.cpp

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
19+
#include <libyul/optimiser/NodeIdDispenser.h>
20+
21+
#include <libyul/optimiser/NameCollector.h>
22+
#include <libyul/optimiser/OptimizerUtilities.h>
23+
24+
#include <fmt/compile.h>
25+
26+
#include <range/v3/range/conversion.hpp>
27+
#include <range/v3/view/iota.hpp>
28+
29+
using namespace solidity::yul;
30+
31+
namespace
32+
{
33+
bool isInvalidLabel(
34+
std::string_view const _label,
35+
std::set<std::string, std::less<>> const& _reservedLabels,
36+
Dialect const& _dialect
37+
)
38+
{
39+
return isRestrictedIdentifier(_dialect, _label) || _reservedLabels.contains(_label);
40+
}
41+
}
42+
43+
NodeIdDispenser::NodeIdDispenser(ASTNodeRegistry const& _labelRegistry, std::set<std::string> const& _reservedLabels):
44+
m_registry(_labelRegistry),
45+
m_reservedLabels(_reservedLabels.begin(), _reservedLabels.end()),
46+
m_offset(_labelRegistry.maximumId() + 1)
47+
{}
48+
49+
NodeIdDispenser::NodeId NodeIdDispenser::newId(NodeId const parent)
50+
{
51+
m_mapping.push_back(resolveBaseId(parent));
52+
return m_mapping.size() - 1 + m_offset;
53+
}
54+
55+
NodeIdDispenser::NodeId NodeIdDispenser::newGhost()
56+
{
57+
return newId(ASTNodeRegistry::ghostId());
58+
}
59+
60+
NodeIdDispenser::NodeId NodeIdDispenser::resolveBaseId(NodeId _id) const
61+
{
62+
if (_id >= m_offset)
63+
_id = m_mapping[_id - m_offset];
64+
yulAssert(_id < m_offset, "We have at most one level of indirection, this violates this assumption");
65+
return _id;
66+
}
67+
68+
ASTNodeRegistry NodeIdDispenser::generateNewLabels(Block const&, Dialect const& _dialect) const
69+
{
70+
// this can be replaced by the actually used ids in the provided block once the AST uses ids instead of YulString
71+
std::set<NodeId> usedIds = ranges::views::iota(static_cast<size_t>(0), m_mapping.size() + m_offset) | ranges::to<std::set<NodeId>>;
72+
73+
if (usedIds.empty())
74+
return {};
75+
76+
auto const& originalLabels = m_registry.labels();
77+
78+
std::vector<uint8_t> reusedLabels (originalLabels.size());
79+
// this means that everything that is derived from empty / ghost needs to be generated
80+
reusedLabels[0] = true;
81+
reusedLabels[1] = true;
82+
83+
std::vector<std::string> labels{"", ASTNodeRegistry::ghostPlaceholder};
84+
labels.reserve(originalLabels.size()+2);
85+
// this is fine as `usedIds` is guaranteed to be not empty
86+
std::vector<size_t> idToLabelMap(*std::prev(usedIds.end()) + 1, 0);
87+
idToLabelMap[0] = 0;
88+
idToLabelMap[1] = 1;
89+
90+
std::set<std::string, std::less<>> alreadyDefinedLabels = m_reservedLabels + std::set{"", ASTNodeRegistry::ghostPlaceholder};
91+
92+
std::vector<NodeId> toGenerate;
93+
// filter out straightforward case: we just use whatever label was already there and put it into alreadyDefinedLabels
94+
// otherwise it goes into the toGenerate collection
95+
for (auto const& id: usedIds)
96+
{
97+
auto const baseId = resolveBaseId(id);
98+
auto const baseLabelIndex = m_registry.idToLabelIndex(baseId);
99+
auto const& baseLabel = originalLabels[baseLabelIndex];
100+
// if we haven't already reused the label, check that either the id didn't change, then we can just
101+
// take over the old label, otherwise check that it is a valid label and then reuse
102+
if (!reusedLabels[baseLabelIndex] && (baseId == id || !isInvalidLabel(baseLabel, m_reservedLabels, _dialect)))
103+
{
104+
labels.push_back(baseLabel);
105+
idToLabelMap[id] = labels.size() - 1;
106+
alreadyDefinedLabels.insert(baseLabel);
107+
reusedLabels[baseLabelIndex] = true;
108+
}
109+
else
110+
toGenerate.push_back(id);
111+
}
112+
113+
for (auto const& id: toGenerate)
114+
{
115+
auto const baseId = resolveBaseId(id);
116+
117+
// ghost variables get special treatment
118+
if (baseId == ASTNodeRegistry::ghostId())
119+
{
120+
idToLabelMap[id] = ASTNodeRegistry::ghostId();
121+
continue;
122+
}
123+
124+
auto const baseLabelIndex = m_registry.idToLabelIndex(baseId);
125+
auto const& baseLabel = originalLabels[baseLabelIndex];
126+
127+
std::string generatedLabel = baseLabel;
128+
size_t suffix = 1;
129+
do
130+
{
131+
generatedLabel = format(FMT_COMPILE("{}_{}"), baseLabel, suffix++);
132+
} while (isInvalidLabel(generatedLabel, alreadyDefinedLabels, _dialect));
133+
134+
labels.push_back(generatedLabel);
135+
idToLabelMap[id] = labels.size() - 1;
136+
alreadyDefinedLabels.insert(generatedLabel);
137+
}
138+
139+
return ASTNodeRegistry{labels, idToLabelMap};
140+
}

Diff for: libyul/optimiser/NodeIdDispenser.h

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
19+
#pragma once
20+
21+
#include <libyul/ASTNodeRegistry.h>
22+
23+
#include <functional>
24+
#include <set>
25+
26+
namespace solidity::yul
27+
{
28+
29+
struct Block;
30+
class Dialect;
31+
32+
class NodeIdDispenser
33+
{
34+
public:
35+
using NodeId = ASTNodeRegistry::NodeId;
36+
explicit NodeIdDispenser(
37+
ASTNodeRegistry const& _labels,
38+
std::set<std::string> const& _reserved = {}
39+
);
40+
41+
ASTNodeRegistry const& labels() const { return m_registry; }
42+
43+
NodeId newId(NodeId parent = 0);
44+
NodeId newGhost();
45+
ASTNodeRegistry generateNewLabels(Block const& _root, Dialect const& _dialect) const;
46+
private:
47+
NodeId resolveBaseId(NodeId _id) const;
48+
49+
ASTNodeRegistry const& m_registry;
50+
std::set<std::string, std::less<>> m_reservedLabels;
51+
size_t m_offset;
52+
std::vector<NodeId> m_mapping;
53+
};
54+
55+
}

0 commit comments

Comments
 (0)