From 7c3becfbcd0c2e3ae820f6fa5129200761a7b04e Mon Sep 17 00:00:00 2001 From: Mike Inouye Date: Mon, 17 Aug 2026 21:18:34 +0000 Subject: [PATCH 1/3] add file lock on liberty scl cache Signed-off-by: Mike Inouye --- passes/techmap/liberty_cache.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/passes/techmap/liberty_cache.h b/passes/techmap/liberty_cache.h index e2b0900aa..f5fe7f750 100644 --- a/passes/techmap/liberty_cache.h +++ b/passes/techmap/liberty_cache.h @@ -9,8 +9,25 @@ namespace abc { } #endif +#if !defined(_WIN32) +#include +#endif + YOSYS_NAMESPACE_BEGIN +#if !defined(_WIN32) +struct ScopedFlock { + int fd; + ScopedFlock(const std::string &path) { + fd = open(path.c_str(), O_CREAT | O_RDWR, 0666); + if (fd >= 0) flock(fd, LOCK_EX); + } + ~ScopedFlock() { + if (fd >= 0) { flock(fd, LOCK_UN); close(fd); } + } +}; +#endif + // Controlled by the libcache pass (-scl option), enabled by default extern bool scl_cache_enabled; @@ -81,6 +98,13 @@ inline std::string convert_liberty_files_to_merged_scl(const std::vector= newest_mtime) { + log("ABC: using cached merged SCL: %s (%zu files)\n", merged_scl.c_str(), liberty_files.size()); + return merged_scl; + } +#endif // read_lib -X cell1 -X cell2 file1 ; read_lib -X cell1 -X cell2 -m file2 ; ... ; write_scl merged.scl std::string temp_scl = merged_scl + ".tmp"; From 977e55315d5294b490168a0b60b49999759cf036 Mon Sep 17 00:00:00 2001 From: Mike Inouye Date: Mon, 17 Aug 2026 22:05:09 +0000 Subject: [PATCH 2/3] fix macOS build Signed-off-by: Mike Inouye --- passes/techmap/liberty_cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/passes/techmap/liberty_cache.h b/passes/techmap/liberty_cache.h index f5fe7f750..05a36d99b 100644 --- a/passes/techmap/liberty_cache.h +++ b/passes/techmap/liberty_cache.h @@ -11,6 +11,8 @@ namespace abc { #if !defined(_WIN32) #include +#include +#include #endif YOSYS_NAMESPACE_BEGIN From 38b6e5e76c82b4c6159e7390a291752a799523c1 Mon Sep 17 00:00:00 2001 From: nella Date: Tue, 18 Aug 2026 09:46:33 +0200 Subject: [PATCH 3/3] fix WASI, harden scl cache locking. Co-authored-by: Mike Inouye --- kernel/io.cc | 70 ++++++++++++++++++++++++++++++++++ kernel/io.h | 18 +++++++++ passes/techmap/liberty_cache.h | 36 ++++++----------- 3 files changed, 99 insertions(+), 25 deletions(-) diff --git a/kernel/io.cc b/kernel/io.cc index 1d0566930..be3c244db 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -11,6 +11,12 @@ #include #endif +#if !defined(_WIN32) && !defined(__wasi__) +#include +#include +#include +#endif + YOSYS_NAMESPACE_BEGIN // Set of utilities for handling files @@ -615,4 +621,68 @@ void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynam format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); } +#if defined(_WIN32) +ScopedFileLock::ScopedFileLock(const std::string &path) +{ + HANDLE h = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + log_warning("Cannot open lock file %s, proceeding without lock\n", path.c_str()); + return; + } + OVERLAPPED ov = {}; + if (!LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &ov)) { + log_warning("Cannot lock %s, proceeding without lock\n", path.c_str()); + CloseHandle(h); + return; + } + handle = h; +} + +ScopedFileLock::~ScopedFileLock() +{ + if (handle != nullptr) { + OVERLAPPED ov = {}; + UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &ov); + CloseHandle(handle); + } +} +#elif !defined(__wasi__) +ScopedFileLock::ScopedFileLock(const std::string &path) +{ + fd = open(path.c_str(), O_CREAT | O_RDWR | O_CLOEXEC, 0666); + if (fd < 0) + fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + if (fd < 0) { + log_warning("Cannot open lock file %s, proceeding without lock\n", path.c_str()); + return; + } + int ret; + while ((ret = flock(fd, LOCK_EX)) != 0 && errno == EINTR); + if (ret != 0) + log_warning("Cannot lock %s, proceeding without lock\n", path.c_str()); +} + +ScopedFileLock::~ScopedFileLock() +{ + if (fd >= 0) + close(fd); // releases the flock +} +#else +ScopedFileLock::ScopedFileLock(const std::string &) {} +ScopedFileLock::~ScopedFileLock() {} +#endif + +unsigned get_process_id() +{ +#if defined(_WIN32) + return GetCurrentProcessId(); +#elif defined(__wasi__) + return 0; +#else + return getpid(); +#endif +} + YOSYS_NAMESPACE_END diff --git a/kernel/io.h b/kernel/io.h index f80b9e908..1212a8f39 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -508,6 +508,24 @@ void append_globbed(std::vector& paths, std::string pattern); std::string name_from_file_path(std::string path); std::string parent_from_file_path(std::string path); +// Exclusive inter-process file lock (flock/LockFileEx, no-op on WASI), +// released on destruction. Never unlinks the lock file: that would reopen the race. +struct ScopedFileLock { + ScopedFileLock(const std::string &path); + ~ScopedFileLock(); + ScopedFileLock(const ScopedFileLock &) = delete; + ScopedFileLock &operator=(const ScopedFileLock &) = delete; +private: +#if defined(_WIN32) + void *handle = nullptr; +#elif !defined(__wasi__) + int fd = -1; +#endif +}; + +// Current process id (0 on WASI). +unsigned get_process_id(); + YOSYS_NAMESPACE_END #endif // YOSYS_IO_H diff --git a/passes/techmap/liberty_cache.h b/passes/techmap/liberty_cache.h index 05a36d99b..22c70dd17 100644 --- a/passes/techmap/liberty_cache.h +++ b/passes/techmap/liberty_cache.h @@ -3,33 +3,17 @@ #include "kernel/yosys.h" +#include +#include + #ifdef YOSYS_LINK_ABC namespace abc { int Abc_RealMain(int argc, char *argv[]); } #endif -#if !defined(_WIN32) -#include -#include -#include -#endif - YOSYS_NAMESPACE_BEGIN -#if !defined(_WIN32) -struct ScopedFlock { - int fd; - ScopedFlock(const std::string &path) { - fd = open(path.c_str(), O_CREAT | O_RDWR, 0666); - if (fd >= 0) flock(fd, LOCK_EX); - } - ~ScopedFlock() { - if (fd >= 0) { flock(fd, LOCK_UN); close(fd); } - } -}; -#endif - // Controlled by the libcache pass (-scl option), enabled by default extern bool scl_cache_enabled; @@ -100,18 +84,18 @@ inline std::string convert_liberty_files_to_merged_scl(const std::vector= newest_mtime) { log("ABC: using cached merged SCL: %s (%zu files)\n", merged_scl.c_str(), liberty_files.size()); return merged_scl; } -#endif // read_lib -X cell1 -X cell2 file1 ; read_lib -X cell1 -X cell2 -m file2 ; ... ; write_scl merged.scl - std::string temp_scl = merged_scl + ".tmp"; + // Concurrent writers cannot corrupt each other even when running without the lock. + std::string temp_scl = stringf("%s.%u.tmp", merged_scl.c_str(), get_process_id()); #ifdef YOSYS_LINK_ABC - std::string script_path = stringf("%s/yosys_merged_scl_convert_%08x.script", cache_dir.c_str(), hash); + std::string script_path = stringf("%s/yosys_merged_scl_convert_%08x_%u.script", cache_dir.c_str(), hash, get_process_id()); FILE *f = fopen(script_path.c_str(), "w"); if (f == NULL) { @@ -170,7 +154,9 @@ inline std::string convert_liberty_files_to_merged_scl(const std::vector