CIRCT 21.0.0git
Loading...
Searching...
No Matches
Namespace.h
Go to the documentation of this file.
1//===- Namespace.h - Utilities for generating names -------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file provides utilities for generating new names that do not conflict
10// with existing names.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef CIRCT_SUPPORT_NAMESPACE_H
15#define CIRCT_SUPPORT_NAMESPACE_H
16
17#include "circt/Support/LLVM.h"
19#include "mlir/IR/BuiltinOps.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/StringSet.h"
22#include "llvm/ADT/Twine.h"
23
24namespace circt {
25
26/// A namespace that is used to store existing names and generate new names in
27/// some scope within the IR. This exists to work around limitations of
28/// SymbolTables. This acts as a base class providing facilities common to all
29/// namespaces implementations.
30class Namespace {
31public:
33 // This fills an entry for an empty string beforehand so that `newName`
34 // doesn't return an empty string.
35 nextIndex.insert({"", 0});
36 }
37 Namespace(const Namespace &other) = default;
39 : nextIndex(std::move(other.nextIndex)), locked(other.locked) {}
40
41 Namespace &operator=(const Namespace &other) = default;
43 nextIndex = std::move(other.nextIndex);
44 locked = other.locked;
45 return *this;
46 }
47
48 void add(mlir::ModuleOp module) {
49 assert(module->getNumRegions() == 1);
50 for (auto &op : module.getBody(0)->getOperations())
51 if (auto symbol = op.getAttrOfType<mlir::StringAttr>(
52 SymbolTable::getSymbolAttrName()))
53 nextIndex.insert({symbol.getValue(), 0});
54 }
55
56 /// SymbolCache initializer; initialize from every key that is convertible to
57 /// a StringAttr in the SymbolCache.
58 void add(SymbolCache &symCache) {
59 for (auto &&[attr, _] : symCache)
60 if (auto strAttr = dyn_cast<StringAttr>(attr))
61 nextIndex.insert({strAttr.getValue(), 0});
62 }
63
64 void add(StringRef name) { nextIndex.insert({name, 0}); }
65
66 /// Removes a symbol from the namespace. Returns true if the symbol was
67 /// removed, false if the symbol was not found.
68 /// This is only allowed to be called _before_ any call to newName.
69 bool erase(llvm::StringRef symbol) {
70 assert(!locked && "Cannot erase names from a locked namespace");
71 return nextIndex.erase(symbol);
72 }
73
74 /// Empty the namespace.
75 void clear() {
76 nextIndex.clear();
77 locked = false;
78 }
79
80 /// Return a unique name, derived from the input `name`, and add the new name
81 /// to the internal namespace. There are two possible outcomes for the
82 /// returned name:
83 ///
84 /// 1. The original name is returned.
85 /// 2. The name is given a `_<n>` suffix where `<n>` is a number starting from
86 /// `0` and incrementing by one each time (`_0`, ...).
87 StringRef newName(const Twine &name) {
88 locked = true;
89 // Special case the situation where there is no name collision to avoid
90 // messing with the SmallString allocation below.
91 llvm::SmallString<64> tryName;
92 auto inserted = nextIndex.insert({name.toStringRef(tryName), 0});
93 if (inserted.second)
94 return inserted.first->getKey();
95
96 // Try different suffixes until we get a collision-free one.
97 if (tryName.empty())
98 name.toVector(tryName); // toStringRef may leave tryName unfilled
99
100 // Indexes less than nextIndex[tryName] are lready used, so skip them.
101 // Indexes larger than nextIndex[tryName] may be used in another name.
102 size_t &i = nextIndex[tryName];
103 tryName.push_back('_');
104 size_t baseLength = tryName.size();
105 do {
106 tryName.resize(baseLength);
107 Twine(i++).toVector(tryName); // append integer to tryName
108 inserted = nextIndex.insert({tryName, 0});
109 } while (!inserted.second);
110
111 return inserted.first->getKey();
112 }
113
114 /// Return a unique name, derived from the input `name` and ensure the
115 /// returned name has the input `suffix`. Also add the new name to the
116 /// internal namespace.
117 /// There are two possible outcomes for the returned name:
118 /// 1. The original name + `_<suffix>` is returned.
119 /// 2. The name is given a suffix `_<n>_<suffix>` where `<n>` is a number
120 /// starting from `0` and incrementing by one each time.
121 StringRef newName(const Twine &name, const Twine &suffix) {
122 locked = true;
123 // Special case the situation where there is no name collision to avoid
124 // messing with the SmallString allocation below.
125 llvm::SmallString<64> tryName;
126 auto inserted = nextIndex.insert(
127 {name.concat("_").concat(suffix).toStringRef(tryName), 0});
128 if (inserted.second)
129 return inserted.first->getKey();
130
131 // Try different suffixes until we get a collision-free one.
132 tryName.clear();
133 name.toVector(tryName); // toStringRef may leave tryName unfilled
134 tryName.push_back('_');
135 size_t baseLength = tryName.size();
136
137 // Get the initial number to start from. Since `:` is not a valid character
138 // in a verilog identifier, we use it separate the name and suffix.
139 // Next number for name+suffix is stored with key `name_:suffix`.
140 tryName.push_back(':');
141 suffix.toVector(tryName);
142
143 // Indexes less than nextIndex[tryName] are already used, so skip them.
144 // Indexes larger than nextIndex[tryName] may be used in another name.
145 size_t &i = nextIndex[tryName];
146 do {
147 tryName.resize(baseLength);
148 Twine(i++).toVector(tryName); // append integer to tryName
149 tryName.push_back('_');
150 suffix.toVector(tryName);
151 inserted = nextIndex.insert({tryName, 0});
152 } while (!inserted.second);
153
154 return inserted.first->getKey();
155 }
156
157protected:
158 // The "next index" that will be tried when trying to unique a string within a
159 // namespace. It follows that all values less than the "next index" value are
160 // already used.
161 llvm::StringMap<size_t> nextIndex;
162
163 // When true, no names can be erased from the namespace. This is to prevent
164 // erasing names after they have been used, thus leaving users of the
165 // namespace in an inconsistent state.
166 bool locked = false;
167};
168
169} // namespace circt
170
171#endif // CIRCT_SUPPORT_NAMESPACE_H
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition CalyxOps.cpp:540
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
Namespace & operator=(const Namespace &other)=default
void clear()
Empty the namespace.
Definition Namespace.h:75
llvm::StringMap< size_t > nextIndex
Definition Namespace.h:161
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
Definition Namespace.h:58
Namespace & operator=(Namespace &&other)
Definition Namespace.h:42
void add(mlir::ModuleOp module)
Definition Namespace.h:48
bool erase(llvm::StringRef symbol)
Removes a symbol from the namespace.
Definition Namespace.h:69
void add(StringRef name)
Definition Namespace.h:64
StringRef newName(const Twine &name, const Twine &suffix)
Return a unique name, derived from the input name and ensure the returned name has the input suffix.
Definition Namespace.h:121
Namespace(Namespace &&other)
Definition Namespace.h:38
Namespace(const Namespace &other)=default
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:87
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition SymCache.h:85
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.