Skip to content

Commit 045e7a8

Browse files
committed
Bump abc for multi-threading
2 parents 5894296 + 09742e2 commit 045e7a8

File tree

6 files changed

+705
-192
lines changed

6 files changed

+705
-192
lines changed

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ LINK_ABC := 0
4949
# Needed for environments that can't run executables (i.e. emscripten, wasm)
5050
DISABLE_SPAWN := 0
5151
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
52+
ENABLE_THREADS := 1
53+
ifeq ($(ENABLE_THREADS),1)
5254
DISABLE_ABC_THREADS := 0
55+
else
56+
DISABLE_ABC_THREADS := 1
57+
endif
5358

5459
# UPF requires TCL
5560
ifeq ($(ENABLE_VERIFIC_UPF),1)
@@ -316,6 +321,7 @@ DISABLE_SPAWN := 1
316321

317322
ifeq ($(ENABLE_ABC),1)
318323
LINK_ABC := 1
324+
ENABLE_THREADS := 0
319325
DISABLE_ABC_THREADS := 1
320326
endif
321327

@@ -486,6 +492,11 @@ CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS))
486492
STRIP :=
487493
endif
488494

495+
ifeq ($(ENABLE_THREADS),1)
496+
CXXFLAGS += -DYOSYS_ENABLE_THREADS
497+
LIBS += -lpthread
498+
endif
499+
489500
ifeq ($(ENABLE_ABC),1)
490501
CXXFLAGS += -DYOSYS_ENABLE_ABC
491502
ifeq ($(LINK_ABC),1)
@@ -649,6 +660,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
649660
$(eval $(call add_include_file,kernel/scopeinfo.h))
650661
$(eval $(call add_include_file,kernel/sexpr.h))
651662
$(eval $(call add_include_file,kernel/sigtools.h))
663+
$(eval $(call add_include_file,kernel/threading.h))
652664
$(eval $(call add_include_file,kernel/timinginfo.h))
653665
$(eval $(call add_include_file,kernel/utils.h))
654666
$(eval $(call add_include_file,kernel/yosys.h))
@@ -675,7 +687,7 @@ OBJS += kernel/log_compat.o
675687
endif
676688
OBJS += kernel/binding.o kernel/tclapi.o
677689
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
678-
OBJS += kernel/drivertools.o kernel/functional.o
690+
OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o
679691
ifeq ($(ENABLE_ZLIB),1)
680692
OBJS += kernel/fstdata.o
681693
endif

kernel/threading.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "kernel/yosys_common.h"
2+
#include "kernel/threading.h"
3+
4+
YOSYS_NAMESPACE_BEGIN
5+
6+
void DeferredLogs::flush()
7+
{
8+
for (auto &m : logs)
9+
if (m.error)
10+
YOSYS_NAMESPACE_PREFIX log_error("%s", m.text.c_str());
11+
else
12+
YOSYS_NAMESPACE_PREFIX log("%s", m.text.c_str());
13+
}
14+
15+
int ThreadPool::pool_size(int reserved_cores, int max_threads)
16+
{
17+
#ifdef YOSYS_ENABLE_THREADS
18+
int num_threads = std::min<int>(std::thread::hardware_concurrency() - reserved_cores, max_threads);
19+
return std::max(0, num_threads);
20+
#else
21+
return 0;
22+
#endif
23+
}
24+
25+
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
26+
: body(std::move(b))
27+
{
28+
#ifdef YOSYS_ENABLE_THREADS
29+
threads.reserve(pool_size);
30+
for (int i = 0; i < pool_size; i++)
31+
threads.emplace_back([i, this]{ body(i); });
32+
#else
33+
log_assert(pool_size == 0);
34+
#endif
35+
}
36+
37+
ThreadPool::~ThreadPool()
38+
{
39+
#ifdef YOSYS_ENABLE_THREADS
40+
for (auto &t : threads)
41+
t.join();
42+
#endif
43+
}
44+
45+
YOSYS_NAMESPACE_END

kernel/threading.h

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#include <deque>
2+
3+
#ifdef YOSYS_ENABLE_THREADS
4+
#include <condition_variable>
5+
#include <mutex>
6+
#include <thread>
7+
#endif
8+
9+
#include "kernel/yosys_common.h"
10+
#include "kernel/log.h"
11+
12+
#ifndef YOSYS_THREADING_H
13+
#define YOSYS_THREADING_H
14+
15+
YOSYS_NAMESPACE_BEGIN
16+
17+
// Concurrent queue implementation. Not fast, but simple.
18+
// Multi-producer, multi-consumer, optionally bounded.
19+
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
20+
template <typename T>
21+
class ConcurrentQueue
22+
{
23+
public:
24+
ConcurrentQueue(int capacity = INT_MAX)
25+
: capacity(capacity) {}
26+
// Push an element into the queue. If it's at capacity, block until there is room.
27+
void push_back(T t)
28+
{
29+
#ifdef YOSYS_ENABLE_THREADS
30+
std::unique_lock<std::mutex> lock(mutex);
31+
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
32+
if (contents.empty())
33+
not_empty_condition.notify_one();
34+
#endif
35+
log_assert(!closed);
36+
contents.push_back(std::move(t));
37+
#ifdef YOSYS_ENABLE_THREADS
38+
if (static_cast<int>(contents.size()) < capacity)
39+
not_full_condition.notify_one();
40+
#endif
41+
}
42+
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
43+
void close()
44+
{
45+
#ifdef YOSYS_ENABLE_THREADS
46+
std::unique_lock<std::mutex> lock(mutex);
47+
not_empty_condition.notify_all();
48+
#endif
49+
closed = true;
50+
}
51+
// Pop an element from the queue. Blocks until an element is available
52+
// or the queue is closed and empty.
53+
std::optional<T> pop_front()
54+
{
55+
return pop_front_internal(true);
56+
}
57+
// Pop an element from the queue. Does not block, just returns nullopt if the
58+
// queue is empty.
59+
std::optional<T> try_pop_front()
60+
{
61+
return pop_front_internal(false);
62+
}
63+
private:
64+
#ifdef YOSYS_ENABLE_THREADS
65+
std::optional<T> pop_front_internal(bool wait)
66+
{
67+
std::unique_lock<std::mutex> lock(mutex);
68+
if (wait) {
69+
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
70+
}
71+
#else
72+
std::optional<T> pop_front_internal(bool)
73+
{
74+
#endif
75+
if (contents.empty())
76+
return std::nullopt;
77+
#ifdef YOSYS_ENABLE_THREADS
78+
if (static_cast<int>(contents.size()) == capacity)
79+
not_full_condition.notify_one();
80+
#endif
81+
T result = std::move(contents.front());
82+
contents.pop_front();
83+
#ifdef YOSYS_ENABLE_THREADS
84+
if (!contents.empty())
85+
not_empty_condition.notify_one();
86+
#endif
87+
return std::move(result);
88+
}
89+
90+
#ifdef YOSYS_ENABLE_THREADS
91+
std::mutex mutex;
92+
// Signals one waiter thread when the queue changes and is not full.
93+
std::condition_variable not_full_condition;
94+
// Signals one waiter thread when the queue changes and is not empty.
95+
std::condition_variable not_empty_condition;
96+
#endif
97+
std::deque<T> contents;
98+
int capacity;
99+
bool closed = false;
100+
};
101+
102+
class DeferredLogs
103+
{
104+
public:
105+
template <typename... Args>
106+
void log(FmtString<TypeIdentity<Args>...> fmt, Args... args)
107+
{
108+
logs.push_back({fmt.format(args...), false});
109+
}
110+
template <typename... Args>
111+
void log_error(FmtString<TypeIdentity<Args>...> fmt, Args... args)
112+
{
113+
logs.push_back({fmt.format(args...), true});
114+
}
115+
void flush();
116+
private:
117+
struct Message
118+
{
119+
std::string text;
120+
bool error;
121+
};
122+
std::vector<Message> logs;
123+
};
124+
125+
class ThreadPool
126+
{
127+
public:
128+
// Computes the number of worker threads to use.
129+
// `reserved_cores` cores are set aside for other threads (e.g. work on the main thread).
130+
// `max_threads` --- don't return more workers than this.
131+
// The result may be 0.
132+
static int pool_size(int reserved_cores, int max_threads);
133+
134+
// Create a pool of threads running the given closure (parameterized by thread number).
135+
// `pool_size` must be the result of a `pool_size()` call.
136+
ThreadPool(int pool_size, std::function<void(int)> b);
137+
ThreadPool(ThreadPool &&other) = delete;
138+
// Waits for all threads to terminate. Make sure those closures return!
139+
~ThreadPool();
140+
141+
// Return the number of threads in the pool.
142+
int num_threads() const
143+
{
144+
#ifdef YOSYS_ENABLE_THREADS
145+
return threads.size();
146+
#else
147+
return 0;
148+
#endif
149+
}
150+
private:
151+
std::function<void(int)> body;
152+
#ifdef YOSYS_ENABLE_THREADS
153+
std::vector<std::thread> threads;
154+
#endif
155+
};
156+
157+
template <class T>
158+
class ConcurrentStack
159+
{
160+
public:
161+
void push_back(T &&t) {
162+
#ifdef YOSYS_ENABLE_THREADS
163+
std::lock_guard<std::mutex> lock(mutex);
164+
#endif
165+
contents.push_back(std::move(t));
166+
}
167+
std::optional<T> try_pop_back() {
168+
#ifdef YOSYS_ENABLE_THREADS
169+
std::lock_guard<std::mutex> lock(mutex);
170+
#endif
171+
if (contents.empty())
172+
return std::nullopt;
173+
T result = std::move(contents.back());
174+
contents.pop_back();
175+
return result;
176+
}
177+
private:
178+
#ifdef YOSYS_ENABLE_THREADS
179+
std::mutex mutex;
180+
#endif
181+
std::vector<T> contents;
182+
};
183+
184+
YOSYS_NAMESPACE_END
185+
186+
#endif // YOSYS_THREADING_H

kernel/yosys.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ int run_command(const std::string &command, std::function<void(const std::string
179179

180180
int ret = pclose(f);
181181
if (ret < 0)
182-
return -1;
182+
return -2;
183183
#ifdef _WIN32
184184
return ret;
185185
#else

misc/create_vcxsrc.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ popd
3838
} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
3939

4040
sed -i 's,</AdditionalIncludeDirectories>,</AdditionalIncludeDirectories>\n <LanguageStandard>stdcpp17</LanguageStandard>\n <AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
41+
sed -i 's,<PreprocessorDefinitions>,<PreprocessorDefinitions>YOSYS_ENABLE_THREADS;,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
4142
if [ -f "/usr/include/FlexLexer.h" ] ; then
4243
sed -i 's,</AdditionalIncludeDirectories>,;..\\yosys\\libs\\flex</AdditionalIncludeDirectories>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
4344
fi

0 commit comments

Comments
 (0)