5959#include < memory>
6060#include < vector>
6161
62+ #ifdef __linux__
63+ # include < fcntl.h>
64+ # include < spawn.h>
65+ # include < sys/wait.h>
66+ #endif
6267#ifndef _WIN32
6368# include < unistd.h>
6469# include < dirent.h>
@@ -153,6 +158,121 @@ struct AbcSigVal {
153158 }
154159};
155160
161+ #if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
162+ struct AbcProcess
163+ {
164+ pid_t pid;
165+ int to_child_pipe;
166+ int from_child_pipe;
167+
168+ AbcProcess () : pid(0 ), to_child_pipe(-1 ), from_child_pipe(-1 ) {}
169+ AbcProcess (AbcProcess &&other) {
170+ pid = other.pid ;
171+ to_child_pipe = other.to_child_pipe ;
172+ from_child_pipe = other.from_child_pipe ;
173+ other.pid = 0 ;
174+ other.to_child_pipe = other.from_child_pipe = -1 ;
175+ }
176+ AbcProcess &operator =(AbcProcess &&other) {
177+ if (this != &other) {
178+ pid = other.pid ;
179+ to_child_pipe = other.to_child_pipe ;
180+ from_child_pipe = other.from_child_pipe ;
181+ other.pid = 0 ;
182+ other.to_child_pipe = other.from_child_pipe = -1 ;
183+ }
184+ return *this ;
185+ }
186+ ~AbcProcess () {
187+ if (pid == 0 )
188+ return ;
189+ if (to_child_pipe >= 0 )
190+ close (to_child_pipe);
191+ int status;
192+ int ret = waitpid (pid, &status, 0 );
193+ if (ret != pid) {
194+ log_error (" waitpid(%d) failed" , pid);
195+ }
196+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 ) {
197+ log_error (" ABC failed with status %X" , status);
198+ }
199+ if (from_child_pipe >= 0 )
200+ close (from_child_pipe);
201+ }
202+ };
203+
204+ std::optional<AbcProcess> spawn_abc (const char * abc_exe, DeferredLogs &logs) {
205+ // Open pipes O_CLOEXEC so we don't leak any of the fds into racing
206+ // fork()s.
207+ int to_child_pipe[2 ];
208+ if (pipe2 (to_child_pipe, O_CLOEXEC) != 0 ) {
209+ logs.log_error (" pipe failed" );
210+ return std::nullopt ;
211+ }
212+ int from_child_pipe[2 ];
213+ if (pipe2 (from_child_pipe, O_CLOEXEC) != 0 ) {
214+ logs.log_error (" pipe failed" );
215+ return std::nullopt ;
216+ }
217+
218+ AbcProcess result;
219+ result.to_child_pipe = to_child_pipe[1 ];
220+ result.from_child_pipe = from_child_pipe[0 ];
221+ // Allow the child side of the pipes to be inherited.
222+ fcntl (to_child_pipe[0 ], F_SETFD, 0 );
223+ fcntl (from_child_pipe[1 ], F_SETFD, 0 );
224+
225+ posix_spawn_file_actions_t file_actions;
226+ if (posix_spawn_file_actions_init (&file_actions) != 0 ) {
227+ logs.log_error (" posix_spawn_file_actions_init failed" );
228+ return std::nullopt ;
229+ }
230+
231+ if (posix_spawn_file_actions_addclose (&file_actions, to_child_pipe[1 ]) != 0 ) {
232+ logs.log_error (" posix_spawn_file_actions_addclose failed" );
233+ return std::nullopt ;
234+ }
235+ if (posix_spawn_file_actions_addclose (&file_actions, from_child_pipe[0 ]) != 0 ) {
236+ logs.log_error (" posix_spawn_file_actions_addclose failed" );
237+ return std::nullopt ;
238+ }
239+ if (posix_spawn_file_actions_adddup2 (&file_actions, to_child_pipe[0 ], STDIN_FILENO) != 0 ) {
240+ logs.log_error (" posix_spawn_file_actions_adddup2 failed" );
241+ return std::nullopt ;
242+ }
243+ if (posix_spawn_file_actions_adddup2 (&file_actions, from_child_pipe[1 ], STDOUT_FILENO) != 0 ) {
244+ logs.log_error (" posix_spawn_file_actions_adddup2 failed" );
245+ return std::nullopt ;
246+ }
247+ if (posix_spawn_file_actions_adddup2 (&file_actions, from_child_pipe[1 ], STDERR_FILENO) != 0 ) {
248+ logs.log_error (" posix_spawn_file_actions_adddup2 failed" );
249+ return std::nullopt ;
250+ }
251+ if (posix_spawn_file_actions_addclose (&file_actions, to_child_pipe[0 ]) != 0 ) {
252+ logs.log_error (" posix_spawn_file_actions_addclose failed" );
253+ return std::nullopt ;
254+ }
255+ if (posix_spawn_file_actions_addclose (&file_actions, from_child_pipe[1 ]) != 0 ) {
256+ logs.log_error (" posix_spawn_file_actions_addclose failed" );
257+ return std::nullopt ;
258+ }
259+
260+ char arg1[] = " -s" ;
261+ char * argv[] = { strdup (abc_exe), arg1, nullptr };
262+ if (0 != posix_spawn (&result.pid , abc_exe, &file_actions, nullptr , argv, environ)) {
263+ logs.log_error (" posix_spawn %s failed" , abc_exe);
264+ return std::nullopt ;
265+ }
266+ free (argv[0 ]);
267+ posix_spawn_file_actions_destroy (&file_actions);
268+ close (to_child_pipe[0 ]);
269+ close (from_child_pipe[1 ]);
270+ return result;
271+ }
272+ #else
273+ struct AbcProcess {};
274+ #endif
275+
156276using AbcSigMap = SigValMap<AbcSigVal>;
157277
158278// Used by off-main-threads. Contains no direct or indirect access to RTLIL.
@@ -167,7 +287,7 @@ struct RunAbcState {
167287 dict<int , std::string> pi_map, po_map;
168288
169289 RunAbcState (const AbcConfig &config) : config(config) {}
170- void run ();
290+ void run (ConcurrentStack<AbcProcess> &process_pool );
171291};
172292
173293struct AbcModuleState {
@@ -1010,7 +1130,42 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
10101130 handle_loops (assign_map, module );
10111131}
10121132
1013- void RunAbcState::run ()
1133+ bool read_until_abc_done (abc_output_filter &filt, int fd, DeferredLogs &logs) {
1134+ std::string line;
1135+ char buf[1024 ];
1136+ while (true ) {
1137+ int ret = read (fd, buf, sizeof (buf) - 1 );
1138+ if (ret < 0 ) {
1139+ logs.log_error (" Failed to read from ABC, errno=%d" , errno);
1140+ return false ;
1141+ }
1142+ if (ret == 0 ) {
1143+ logs.log_error (" ABC exited prematurely" );
1144+ return false ;
1145+ }
1146+ char *start = buf;
1147+ char *end = buf + ret;
1148+ while (start < end) {
1149+ char *p = static_cast <char *>(memchr (start, ' \n ' , end - start));
1150+ if (p == nullptr ) {
1151+ break ;
1152+ }
1153+ line.append (start, p + 1 - start);
1154+ // ABC seems to actually print "ABC_DONE \n", but we probably shouldn't
1155+ // rely on that extra space being output.
1156+ if (line.substr (0 , 8 ) == " ABC_DONE" ) {
1157+ // Ignore any leftover output, there should only be a prompt perhaps
1158+ return true ;
1159+ }
1160+ filt.next_line (line);
1161+ line.clear ();
1162+ start = p + 1 ;
1163+ }
1164+ line.append (start, end - start);
1165+ }
1166+ }
1167+
1168+ void RunAbcState::run (ConcurrentStack<AbcProcess> &process_pool)
10141169{
10151170 std::string buffer = stringf (" %s/input.blif" , tempdir_name);
10161171 FILE *f = fopen (buffer.c_str (), " wt" );
@@ -1137,14 +1292,12 @@ void RunAbcState::run()
11371292 count_gates, GetSize (signal_list), count_input, count_output);
11381293 if (count_output > 0 )
11391294 {
1140- buffer = stringf (" \" %s \" -s -f %s /abc.script 2>&1 " , config. exe_file , tempdir_name);
1141- logs.log (" Running ABC command : %s\n " , replace_tempdir (buffer , tempdir_name, config.show_tempdir ));
1295+ std::string tmp_script_name = stringf (" %s /abc.script" , tempdir_name);
1296+ logs.log (" Running ABC script : %s\n " , replace_tempdir (tmp_script_name , tempdir_name, config.show_tempdir ));
11421297
11431298 errno = 0 ;
1144- #ifndef YOSYS_LINK_ABC
11451299 abc_output_filter filt (*this , tempdir_name, config.show_tempdir );
1146- int ret = run_command (buffer, std::bind (&abc_output_filter::next_line, filt, std::placeholders::_1));
1147- #else
1300+ #ifdef YOSYS_LINK_ABC
11481301 string temp_stdouterr_name = stringf (" %s/stdouterr.txt" , tempdir_name);
11491302 FILE *temp_stdouterr_w = fopen (temp_stdouterr_name.c_str (), " w" );
11501303 if (temp_stdouterr_w == NULL )
@@ -1165,7 +1318,6 @@ void RunAbcState::run()
11651318 fclose (temp_stdouterr_w);
11661319 // These needs to be mutable, supposedly due to getopt
11671320 char *abc_argv[5 ];
1168- string tmp_script_name = stringf (" %s/abc.script" , tempdir_name);
11691321 abc_argv[0 ] = strdup (config.exe_file .c_str ());
11701322 abc_argv[1 ] = strdup (" -s" );
11711323 abc_argv[2 ] = strdup (" -f" );
@@ -1183,13 +1335,40 @@ void RunAbcState::run()
11831335 fclose (old_stdout);
11841336 fclose (old_stderr);
11851337 std::ifstream temp_stdouterr_r (temp_stdouterr_name);
1186- abc_output_filter filt (*this , tempdir_name, config.show_tempdir );
11871338 for (std::string line; std::getline (temp_stdouterr_r, line); )
11881339 filt.next_line (line + " \n " );
11891340 temp_stdouterr_r.close ();
1341+ #elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
1342+ AbcProcess process;
1343+ if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back ()) {
1344+ process = std::move (process_opt.value ());
1345+ } else if (std::optional<AbcProcess> process_opt = spawn_abc (config.exe_file .c_str (), logs)) {
1346+ process = std::move (process_opt.value ());
1347+ } else {
1348+ return ;
1349+ }
1350+ std::string cmd = stringf (
1351+ // This makes ABC switch stdout to line buffering, which we need
1352+ // to see our ABC_DONE message.
1353+ " set abcout /dev/stdout\n "
1354+ " empty\n "
1355+ " source %s\n "
1356+ " echo \" ABC_DONE\"\n " , tmp_script_name);
1357+ int ret = write (process.to_child_pipe , cmd.c_str (), cmd.size ());
1358+ if (ret != static_cast <int >(cmd.size ())) {
1359+ logs.log_error (" write failed" );
1360+ return ;
1361+ }
1362+ ret = read_until_abc_done (filt, process.from_child_pipe , logs) ? 0 : 1 ;
1363+ if (ret == 0 ) {
1364+ process_pool.push_back (std::move (process));
1365+ }
1366+ #else
1367+ std::string cmd = stringf (" \" %s\" -s -f %s/abc.script 2>&1" , config.exe_file .c_str (), tempdir_name.c_str ());
1368+ int ret = run_command (cmd, std::bind (&abc_output_filter::next_line, filt, std::placeholders::_1));
11901369#endif
11911370 if (ret != 0 ) {
1192- logs.log_error (" ABC: execution of command \" %s\" failed: return code %d (errno=%d).\n " , buffer , ret, errno);
1371+ logs.log_error (" ABC: execution of script \" %s\" failed: return code %d (errno=%d).\n " , tmp_script_name , ret, errno);
11931372 return ;
11941373 }
11951374 did_run = true ;
@@ -2205,7 +2384,8 @@ struct AbcPass : public Pass {
22052384
22062385 AbcModuleState state (config, initvals);
22072386 state.prepare_module (design, mod, assign_map, cells, dff_mode, clk_str);
2208- state.run_abc .run ();
2387+ ConcurrentStack<AbcProcess> process_pool;
2388+ state.run_abc .run (process_pool);
22092389 state.extract (assign_map, design, mod);
22102390 continue ;
22112391 }
@@ -2379,11 +2559,12 @@ struct AbcPass : public Pass {
23792559 ConcurrentQueue<std::unique_ptr<AbcModuleState>> work_queue (num_worker_threads);
23802560 ConcurrentQueue<std::unique_ptr<AbcModuleState>> work_finished_queue;
23812561 int work_finished_count = 0 ;
2562+ ConcurrentStack<AbcProcess> process_pool;
23822563 ThreadPool worker_threads (num_worker_threads, [&](int ){
23832564 while (std::optional<std::unique_ptr<AbcModuleState>> work =
23842565 work_queue.pop_front ()) {
23852566 // Only the `run_abc` component is safe to touch here!
2386- (*work)->run_abc .run ();
2567+ (*work)->run_abc .run (process_pool );
23872568 work_finished_queue.push_back (std::move (*work));
23882569 }
23892570 });
@@ -2409,7 +2590,7 @@ struct AbcPass : public Pass {
24092590 work_queue.push_back (std::move (state));
24102591 } else {
24112592 // Just run everything on the main thread.
2412- state->run_abc .run ();
2593+ state->run_abc .run (process_pool );
24132594 work_finished_queue.push_back (std::move (state));
24142595 }
24152596 }
0 commit comments