Skip to content

Commit 09742e2

Browse files
authored
Merge pull request #5266 from rocallahan/abc-parallel
Run ABC passes in parallel
2 parents 475d455 + ae0ca75 commit 09742e2

File tree

6 files changed

+684
-171
lines changed

6 files changed

+684
-171
lines changed

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ LINK_ABC := 0
4444
# Needed for environments that can't run executables (i.e. emscripten, wasm)
4545
DISABLE_SPAWN := 0
4646
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
47+
ENABLE_THREADS := 1
48+
ifeq ($(ENABLE_THREADS),1)
4749
DISABLE_ABC_THREADS := 0
50+
else
51+
DISABLE_ABC_THREADS := 1
52+
endif
4853

4954
# clang sanitizers
5055
SANITIZER =
@@ -300,6 +305,7 @@ DISABLE_SPAWN := 1
300305

301306
ifeq ($(ENABLE_ABC),1)
302307
LINK_ABC := 1
308+
ENABLE_THREADS := 0
303309
DISABLE_ABC_THREADS := 1
304310
endif
305311

@@ -457,6 +463,11 @@ CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS))
457463
STRIP :=
458464
endif
459465

466+
ifeq ($(ENABLE_THREADS),1)
467+
CXXFLAGS += -DYOSYS_ENABLE_THREADS
468+
LIBS += -lpthread
469+
endif
470+
460471
ifeq ($(ENABLE_ABC),1)
461472
CXXFLAGS += -DYOSYS_ENABLE_ABC
462473
ifeq ($(LINK_ABC),1)
@@ -612,6 +623,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
612623
$(eval $(call add_include_file,kernel/scopeinfo.h))
613624
$(eval $(call add_include_file,kernel/sexpr.h))
614625
$(eval $(call add_include_file,kernel/sigtools.h))
626+
$(eval $(call add_include_file,kernel/threading.h))
615627
$(eval $(call add_include_file,kernel/timinginfo.h))
616628
$(eval $(call add_include_file,kernel/utils.h))
617629
$(eval $(call add_include_file,kernel/yosys.h))
@@ -638,7 +650,7 @@ OBJS += kernel/log_compat.o
638650
endif
639651
OBJS += kernel/binding.o kernel/tclapi.o
640652
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
641-
OBJS += kernel/drivertools.o kernel/functional.o
653+
OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o
642654
ifeq ($(ENABLE_ZLIB),1)
643655
OBJS += kernel/fstdata.o
644656
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
@@ -177,7 +177,7 @@ int run_command(const std::string &command, std::function<void(const std::string
177177

178178
int ret = pclose(f);
179179
if (ret < 0)
180-
return -1;
180+
return -2;
181181
#ifdef _WIN32
182182
return ret;
183183
#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)