Skip to content

Commit f1a3f6a

Browse files
committed
Make NEW_ID create IDs whose string allocation is delayed
1 parent b067518 commit f1a3f6a

File tree

7 files changed

+164
-69
lines changed

7 files changed

+164
-69
lines changed

kernel/io.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ void format_emit_idstring(std::string &result, std::string_view spec, int *dynam
580580
{
581581
if (spec == "%s") {
582582
// Format checking will have guaranteed num_dynamic_ints == 0.
583-
result += arg.c_str();
583+
arg.append_to(&result);
584584
return;
585585
}
586586
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str());

kernel/rtlil.cc

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include <string.h>
3030
#include <algorithm>
31+
#include <charconv>
3132
#include <optional>
3233
#include <string_view>
3334

@@ -37,6 +38,8 @@ bool RTLIL::IdString::destruct_guard_ok = false;
3738
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
3839
std::vector<char*> RTLIL::IdString::global_id_storage_;
3940
std::unordered_map<std::string_view, int> RTLIL::IdString::global_id_index_;
41+
std::unordered_map<int, const std::string*> RTLIL::IdString::global_negative_id_prefix_storage_;
42+
std::unordered_map<int, char*> RTLIL::IdString::global_negative_id_storage_;
4043
#ifndef YOSYS_NO_IDS_REFCNT
4144
std::unordered_map<int, int> RTLIL::IdString::global_refcount_storage_;
4245
std::vector<int> RTLIL::IdString::global_free_idx_list_;
@@ -67,6 +70,78 @@ void RTLIL::IdString::prepopulate()
6770
#undef X
6871
}
6972

73+
static std::optional<int> parse_autoidx(std::string_view v)
74+
{
75+
// autoidx values can never be <= 0, so there can never be a leading 0 digit.
76+
if (v.empty() || v[0] == '0')
77+
return std::nullopt;
78+
for (char ch : v) {
79+
if (ch < '0' || ch > '9')
80+
return std::nullopt;
81+
}
82+
int p_autoidx;
83+
if (std::from_chars(v.begin(), v.end(), p_autoidx).ec != std::errc())
84+
return std::nullopt;
85+
return p_autoidx;
86+
}
87+
88+
int RTLIL::IdString::really_insert(std::string_view p, std::unordered_map<std::string_view, int>::iterator &it)
89+
{
90+
ensure_prepopulated();
91+
92+
if (p.empty())
93+
return 0;
94+
95+
log_assert(p[0] == '$' || p[0] == '\\');
96+
for (char ch : p)
97+
if ((unsigned)ch <= (unsigned)' ')
98+
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", ch, std::string(p).c_str());
99+
100+
if (p.substr(0, 6) == "$auto$") {
101+
size_t autoidx_pos = p.find_last_of('$') + 1;
102+
std::optional<int> p_autoidx = parse_autoidx(p.substr(autoidx_pos));
103+
if (p_autoidx.has_value()) {
104+
auto prefix_it = global_negative_id_prefix_storage_.find(-*p_autoidx);
105+
if (prefix_it != global_negative_id_prefix_storage_.end() && p.substr(0, autoidx_pos) == *prefix_it->second)
106+
return -*p_autoidx;
107+
// Ensure NEW_ID/NEW_ID_SUFFIX will not create collisions with the ID
108+
// we're about to create.
109+
autoidx = std::max(autoidx, *p_autoidx + 1);
110+
}
111+
}
112+
113+
#ifndef YOSYS_NO_IDS_REFCNT
114+
if (global_free_idx_list_.empty()) {
115+
log_assert(global_id_storage_.size() < 0x40000000);
116+
global_free_idx_list_.push_back(global_id_storage_.size());
117+
global_id_storage_.push_back(nullptr);
118+
}
119+
120+
int idx = global_free_idx_list_.back();
121+
global_free_idx_list_.pop_back();
122+
char* buf = static_cast<char*>(malloc(p.size() + 1));
123+
memcpy(buf, p.data(), p.size());
124+
buf[p.size()] = 0;
125+
global_id_storage_.at(idx) = buf;
126+
global_id_index_.insert(it, {std::string_view(buf, p.size()), idx});
127+
#else
128+
int idx = global_id_storage_.size();
129+
global_id_storage_.push_back(strdup(p));
130+
global_id_index_[global_id_storage_.back()] = idx;
131+
#endif
132+
133+
if (yosys_xtrace) {
134+
log("#X# New IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
135+
log_backtrace("-X- ", yosys_xtrace-1);
136+
}
137+
138+
#ifdef YOSYS_XTRACE_GET_PUT
139+
if (yosys_xtrace)
140+
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, refcount(idx));
141+
#endif
142+
return idx;
143+
}
144+
70145
static constexpr bool check_well_known_id_order()
71146
{
72147
int size = sizeof(IdTable) / sizeof(IdTable[0]);
@@ -81,9 +156,9 @@ static constexpr bool check_well_known_id_order()
81156
static_assert(check_well_known_id_order());
82157

83158
struct IdStringCollector {
84-
IdStringCollector(int size) : live(size, false) {}
85-
86-
void trace(IdString id) { live[id.index_] = true; }
159+
void trace(IdString id) {
160+
live.insert(id.index_);
161+
}
87162
template <typename T> void trace(const T* v) {
88163
trace(*v);
89164
}
@@ -172,23 +247,23 @@ struct IdStringCollector {
172247
trace(action.memid);
173248
}
174249

175-
std::vector<bool> live;
250+
std::unordered_set<int> live;
176251
};
177252

178253
void RTLIL::OwningIdString::collect_garbage()
179254
{
180255
#ifndef YOSYS_NO_IDS_REFCNT
181-
int size = GetSize(global_id_storage_);
182-
IdStringCollector collector(size);
256+
IdStringCollector collector;
183257
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
184258
collector.trace(*design);
185259
}
260+
int size = GetSize(global_id_storage_);
186261
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
187-
if (collector.live[i])
188-
continue;
189262
char *&storage = global_id_storage_.at(i);
190263
if (storage == nullptr)
191264
continue;
265+
if (collector.live.find(i) != collector.live.end())
266+
continue;
192267
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
193268
continue;
194269

@@ -202,6 +277,23 @@ void RTLIL::OwningIdString::collect_garbage()
202277
storage = nullptr;
203278
global_free_idx_list_.push_back(i);
204279
}
280+
281+
for (auto it = global_negative_id_prefix_storage_.begin(); it != global_negative_id_prefix_storage_.end();) {
282+
if (collector.live.find(it->first) != collector.live.end()) {
283+
++it;
284+
continue;
285+
}
286+
if (global_refcount_storage_.find(it->first) != global_refcount_storage_.end()) {
287+
++it;
288+
continue;
289+
}
290+
auto str_it = global_negative_id_storage_.find(it->first);
291+
if (str_it != global_negative_id_storage_.end()) {
292+
free(str_it->second);
293+
global_negative_id_storage_.erase(str_it);
294+
}
295+
it = global_negative_id_prefix_storage_.erase(it);
296+
}
205297
#endif
206298
}
207299

kernel/rtlil.h

Lines changed: 48 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "kernel/yosys_common.h"
2424
#include "kernel/yosys.h"
2525

26-
#include <charconv>
2726
#include <string_view>
2827
#include <unordered_map>
2928

@@ -151,8 +150,16 @@ struct RTLIL::IdString
151150
~destruct_guard_t() { destruct_guard_ok = false; }
152151
} destruct_guard;
153152

153+
// String storage for nonnegative IDs
154154
static std::vector<char*> global_id_storage_;
155+
// Lookup table for nonnegative IDs
155156
static std::unordered_map<std::string_view, int> global_id_index_;
157+
// Shared prefix string storage for negative IDs. Append the negated (i.e. positive) ID
158+
// to this string to get the real string.
159+
// The prefix strings must live forever.
160+
static std::unordered_map<int, const std::string*> global_negative_id_prefix_storage_;
161+
// Explicit string storage for negative IDs
162+
static std::unordered_map<int, char*> global_negative_id_storage_;
156163
#ifndef YOSYS_NO_IDS_REFCNT
157164
// All (index, refcount) pairs in this map have refcount > 0.
158165
static std::unordered_map<int, int> global_refcount_storage_;
@@ -198,58 +205,15 @@ struct RTLIL::IdString
198205
#endif
199206
return it->second;
200207
}
208+
return really_insert(p, it);
209+
}
201210

202-
ensure_prepopulated();
203-
204-
if (p.empty())
205-
return 0;
206-
207-
log_assert(p[0] == '$' || p[0] == '\\');
208-
for (char ch : p)
209-
if ((unsigned)ch <= (unsigned)' ')
210-
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", ch, std::string(p).c_str());
211-
212-
if (p.substr(0, 6) == "$auto$") {
213-
// Ensure new_id(_suffix) will not create collisions.
214-
size_t autoidx_pos = p.find_last_of('$');
215-
int p_autoidx;
216-
std::string_view v = p.substr(autoidx_pos + 1);
217-
if (std::from_chars(v.begin(), v.end(), p_autoidx).ec == std::errc()) {
218-
autoidx = std::max(autoidx, p_autoidx + 1);
219-
}
220-
}
221-
222-
#ifndef YOSYS_NO_IDS_REFCNT
223-
if (global_free_idx_list_.empty()) {
224-
log_assert(global_id_storage_.size() < 0x40000000);
225-
global_free_idx_list_.push_back(global_id_storage_.size());
226-
global_id_storage_.push_back(nullptr);
227-
}
228-
229-
int idx = global_free_idx_list_.back();
230-
global_free_idx_list_.pop_back();
231-
char* buf = static_cast<char*>(malloc(p.size() + 1));
232-
memcpy(buf, p.data(), p.size());
233-
buf[p.size()] = 0;
234-
global_id_storage_.at(idx) = buf;
235-
global_id_index_.insert(it, {std::string_view(buf, p.size()), idx});
236-
#else
237-
int idx = global_id_storage_.size();
238-
global_id_storage_.push_back(strdup(p));
239-
global_id_index_[global_id_storage_.back()] = idx;
240-
#endif
241-
242-
if (yosys_xtrace) {
243-
log("#X# New IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
244-
log_backtrace("-X- ", yosys_xtrace-1);
245-
}
246-
247-
#ifdef YOSYS_XTRACE_GET_PUT
248-
if (yosys_xtrace)
249-
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, refcount(idx));
250-
#endif
251-
252-
return idx;
211+
// Inserts an ID with string `prefix + autoidx', incrementing autoidx.
212+
// `prefix` must start with '$auto$', end with '$', and live forever.
213+
static IdString new_autoidx_with_prefix(const std::string *prefix) {
214+
int index = -(autoidx++);
215+
global_negative_id_prefix_storage_.insert({index, prefix});
216+
return from_index(index);
253217
}
254218

255219
// the actual IdString object is just is a single int
@@ -279,11 +243,31 @@ struct RTLIL::IdString
279243
constexpr inline const IdString &id_string() const { return *this; }
280244

281245
inline const char *c_str() const {
282-
return global_id_storage_.at(index_);
246+
if (index_ >= 0)
247+
return global_id_storage_.at(index_);
248+
auto it = global_negative_id_storage_.find(index_);
249+
if (it != global_negative_id_storage_.end())
250+
return it->second;
251+
std::string str = stringf("%s%d", *global_negative_id_prefix_storage_.at(index_), -index_);
252+
char *c = static_cast<char*>(malloc(str.size() + 1));
253+
memcpy(c, str.c_str(), str.size() + 1);
254+
global_negative_id_storage_.insert(it, {index_, c});
255+
return c;
283256
}
284257

285258
inline std::string str() const {
286-
return std::string(global_id_storage_.at(index_));
259+
std::string result;
260+
append_to(&result);
261+
return result;
262+
}
263+
264+
inline void append_to(std::string *out) const {
265+
if (index_ >= 0) {
266+
*out += global_id_storage_.at(index_);
267+
return;
268+
}
269+
*out += *global_negative_id_prefix_storage_.at(index_);
270+
*out += std::to_string(-index_);
287271
}
288272

289273
inline bool operator<(const IdString &rhs) const {
@@ -389,6 +373,14 @@ struct RTLIL::IdString
389373

390374
private:
391375
static void prepopulate();
376+
static int really_insert(std::string_view p, std::unordered_map<std::string_view, int>::iterator &it);
377+
378+
protected:
379+
static IdString from_index(int index) {
380+
IdString result;
381+
result.index_ = index;
382+
return result;
383+
}
392384

393385
public:
394386
static void ensure_prepopulated() {
@@ -458,7 +450,7 @@ struct RTLIL::OwningIdString : public RTLIL::IdString {
458450
#endif
459451
#ifdef YOSYS_XTRACE_GET_PUT
460452
if (yosys_xtrace && idx >= static_cast<short>(StaticId::STATIC_ID_END))
461-
log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, refcount(idx));
453+
log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", from_index(idx), idx, refcount(idx));
462454
#endif
463455
}
464456

@@ -471,7 +463,7 @@ struct RTLIL::OwningIdString : public RTLIL::IdString {
471463
return;
472464
#ifdef YOSYS_XTRACE_GET_PUT
473465
if (yosys_xtrace)
474-
log("#X# PUT '%s' (index %d, refcount %u)\n", global_id_storage_.at(index_), index_, refcount(index_));
466+
log("#X# PUT '%s' (index %d, refcount %u)\n", from_index(index_), index_, refcount(index_));
475467
#endif
476468
auto it = global_refcount_storage_.find(index_);
477469
log_assert(it != global_refcount_storage_.end() && it->second >= 1);

kernel/yosys.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ void yosys_shutdown()
295295
#endif
296296
}
297297

298-
RTLIL::IdString new_id(std::string_view file, int line, std::string_view func)
298+
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func)
299299
{
300300
#ifdef _WIN32
301301
size_t pos = file.find_last_of("/\\");
@@ -309,7 +309,7 @@ RTLIL::IdString new_id(std::string_view file, int line, std::string_view func)
309309
if (pos != std::string_view::npos)
310310
func = func.substr(pos+1);
311311

312-
return stringf("$auto$%s:%d:%s$%d", file, line, func, autoidx++);
312+
return new std::string(stringf("$auto$%s:%d:%s$", file, line, func));
313313
}
314314

315315
RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view func, std::string_view suffix)

kernel/yosys_common.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,14 @@ extern int autoidx;
271271
extern int yosys_xtrace;
272272
extern bool yosys_write_versions;
273273

274-
RTLIL::IdString new_id(std::string_view file, int line, std::string_view func);
274+
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func);
275275
RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view func, std::string_view suffix);
276276

277277
#define NEW_ID \
278-
YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
278+
YOSYS_NAMESPACE_PREFIX RTLIL::IdString::new_autoidx_with_prefix([](std::string_view func) -> const std::string * { \
279+
static const std::string *prefix = create_id_prefix(__FILE__, __LINE__, func); \
280+
return prefix; \
281+
}(__FUNCTION__))
279282
#define NEW_ID_SUFFIX(suffix) \
280283
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
281284

pyosys/generator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ def __post_init__(self):
163163
{
164164
"global_id_storage_",
165165
"global_id_index_",
166+
"global_negative_id_storage_",
167+
"global_negative_id_prefix_storage_",
166168
"global_refcount_storage_",
167169
"global_free_idx_list_",
168170
"last_created_idx_ptr_",

tests/unit/kernel/rtlilTest.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,12 @@ namespace RTLIL {
367367
EXPECT_EQ(own.str(), "\\figblortle");
368368
}
369369

370+
TEST_F(KernelRtlilTest, LookupAutoidxId) {
371+
IdString id = NEW_ID;
372+
IdString id2 = IdString(id.str());
373+
EXPECT_EQ(id, id2);
374+
}
375+
370376
class WireRtlVsHdlIndexConversionTest :
371377
public KernelRtlilTest,
372378
public testing::WithParamInterface<std::tuple<bool, int, int>>

0 commit comments

Comments
 (0)