From b43c282e4e0de53b9323a5d8d85b07bc73324a17 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 11 Nov 2019 09:23:06 +0000 Subject: [PATCH] Add WASI platform support. This includes the following significant changes: * Patching ezsat and minisat to disable resource limiting code on WASM/WASI, since the POSIX functions they use are unavailable. * Adding a new definition, YOSYS_DISABLE_SPAWN, present if platform does not support spawning subprocesses (i.e. Emscripten or WASI). This definition hides the definition of `run_command()`. * Adding a new Makefile flag, DISABLE_SPAWN, present in the same condition. This flag disables all passes that require spawning subprocesses for their function. --- Makefile | 50 ++++++++++++++++++++++++++++++-- frontends/rpc/Makefile.inc | 2 +- kernel/driver.cc | 17 ++++++++++- kernel/register.cc | 4 +++ kernel/yosys.cc | 27 +++++++++++------ kernel/yosys.h | 2 ++ libs/ezsat/ezminisat.cc | 11 +++---- libs/minisat/00_PATCH_wasm.patch | 34 ++++++++++++++++++++++ libs/minisat/00_UPDATE.sh | 2 +- libs/minisat/System.cc | 8 +++-- passes/cmds/Makefile.inc | 4 +++ passes/cmds/cover.cc | 4 +-- passes/cmds/show.cc | 12 ++++++-- passes/sat/Makefile.inc | 3 +- passes/techmap/Makefile.inc | 2 +- passes/techmap/abc.cc | 11 +++++-- passes/techmap/abc9_exe.cc | 4 +++ 17 files changed, 167 insertions(+), 30 deletions(-) create mode 100644 libs/minisat/00_PATCH_wasm.patch diff --git a/Makefile b/Makefile index da8701355..c719094b4 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ CONFIG := clang # CONFIG := gcc-4.8 # CONFIG := afl-gcc # CONFIG := emcc +# CONFIG := wasi # CONFIG := mxe # CONFIG := msys2 # CONFIG := msys2-64 @@ -32,7 +33,9 @@ ENABLE_NDEBUG := 0 LINK_CURSES := 0 LINK_TERMCAP := 0 LINK_ABC := 0 -# Needed for environments that don't have proper thread support (i.e. emscripten) +# Needed for environments that can't run executables (i.e. emscripten, wasm) +DISABLE_SPAWN := 0 +# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now) DISABLE_ABC_THREADS := 0 # clang sanitizers @@ -42,7 +45,7 @@ SANITIZER = # SANITIZER = undefined # SANITIZER = cfi -PROGRAM_PREFIX := +PROGRAM_PREFIX := OS := $(shell uname -s) PREFIX ?= /usr/local @@ -253,6 +256,8 @@ LDFLAGS += $(EMCCFLAGS) LDLIBS = EXE = .js +DISABLE_SPAWN := 1 + TARGETS := $(filter-out $(PROGRAM_PREFIX)yosys-config,$(TARGETS)) EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip @@ -274,6 +279,35 @@ yosysjs-$(YOSYS_VER).zip: yosys.js yosys.wasm viz.js misc/yosysjs/* yosys.html: misc/yosys.html $(P) cp misc/yosys.html yosys.html +else ifeq ($(CONFIG),wasi) +ifeq ($(WASI_SDK),) +CXX = clang++ +LD = clang++ +AR = llvm-ar +RANLIB = llvm-ranlib +WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) +else +CXX = $(WASI_SDK)/bin/clang++ +LD = $(WASI_SDK)/bin/clang++ +AR = $(WASI_SDK)/bin/ar +RANLIB = $(WASI_SDK)/bin/ranlib +WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) +endif +CXXFLAGS := $(WASIFLAGS) -std=c++11 -Os $(filter-out -fPIC,$(CXXFLAGS)) +LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS)) +LDLIBS := $(filter-out -lrt,$(LDLIBS)) +ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" +ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING" +ABCMKARGS += OPTFLAGS="-Os" +EXE = .wasm + +DISABLE_SPAWN := 1 + +ifeq ($(ENABLE_ABC),1) +LINK_ABC := 1 +DISABLE_ABC_THREADS := 1 +endif + else ifeq ($(CONFIG),mxe) PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ @@ -396,6 +430,10 @@ ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif +ifeq ($(DISABLE_SPAWN),1) +CXXFLAGS += -DYOSYS_DISABLE_SPAWN +endif + ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) @@ -911,6 +949,14 @@ config-emcc: clean echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_ZLIB := 0' >> Makefile.conf +config-wasi: clean + echo 'CONFIG := wasi' > Makefile.conf + echo 'ENABLE_TCL := 0' >> Makefile.conf + echo 'ENABLE_ABC := 0' >> Makefile.conf + echo 'ENABLE_PLUGINS := 0' >> Makefile.conf + echo 'ENABLE_READLINE := 0' >> Makefile.conf + echo 'ENABLE_ZLIB := 0' >> Makefile.conf + config-mxe: clean echo 'CONFIG := mxe' > Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf diff --git a/frontends/rpc/Makefile.inc b/frontends/rpc/Makefile.inc index 7b270b6fe..fa1d068f9 100644 --- a/frontends/rpc/Makefile.inc +++ b/frontends/rpc/Makefile.inc @@ -1,3 +1,3 @@ -ifneq ($(CONFIG),emcc) +ifeq ($(DISABLE_SPAWN),0) OBJS += frontends/rpc/rpc_frontend.o endif diff --git a/kernel/driver.cc b/kernel/driver.cc index 5f0959776..57ed7b8b4 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -155,6 +155,19 @@ int yosys_history_offset = 0; std::string yosys_history_file; #endif +#if defined(__wasm) +extern "C" { + // FIXME: WASI does not currently support exceptions. + void* __cxa_allocate_exception(size_t thrown_size) throw() { + return malloc(thrown_size); + } + bool __cxa_uncaught_exception() throw(); + void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) { + std::terminate(); + } +} +#endif + void yosys_atexit() { #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) @@ -587,9 +600,11 @@ int main(int argc, char **argv) ru_buffer.ru_utime.tv_usec += ru_buffer_children.ru_utime.tv_usec; ru_buffer.ru_stime.tv_sec += ru_buffer_children.ru_stime.tv_sec; ru_buffer.ru_stime.tv_usec += ru_buffer_children.ru_stime.tv_usec; +#if defined(__linux__) || defined(__FreeBSD__) ru_buffer.ru_maxrss = std::max(ru_buffer.ru_maxrss, ru_buffer_children.ru_maxrss); +#endif } -# if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) meminfo = stringf(", MEM: %.2f MB peak", ru_buffer.ru_maxrss / 1024.0); #endif diff --git a/kernel/register.cc b/kernel/register.cc index 925d0d776..02974e534 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -238,6 +238,7 @@ void Pass::call(RTLIL::Design *design, std::string command) return; if (tok[0] == '!') { +#if !defined(YOSYS_DISABLE_SPAWN) cmd_buf = command.substr(command.find('!') + 1); while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' || cmd_buf.back() == '\r' || cmd_buf.back() == '\n')) @@ -247,6 +248,9 @@ void Pass::call(RTLIL::Design *design, std::string command) if (retCode != 0) log_cmd_error("Shell command returned error code %d.\n", retCode); return; +#else + log_cmd_error("Shell is not available.\n"); +#endif } while (!tok.empty()) { diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 01131601f..2ec3dca0c 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -45,8 +45,10 @@ # include # include # include -# include # include +# if !defined(YOSYS_DISABLE_SPAWN) +# include +# endif #endif #if !defined(_WIN32) && defined(YOSYS_ENABLE_GLOB) @@ -336,16 +338,13 @@ bool patmatch(const char *pattern, const char *string) return false; } +#if !defined(YOSYS_DISABLE_SPAWN) int run_command(const std::string &command, std::function process_line) { if (!process_line) return system(command.c_str()); -#ifdef EMSCRIPTEN - FILE *f = nullptr; -#else FILE *f = popen(command.c_str(), "r"); -#endif if (f == nullptr) return -1; @@ -368,10 +367,16 @@ int run_command(const std::string &command, std::function split_tokens(const std::string &text, const char *sep = " \t\r\n"); bool patmatch(const char *pattern, const char *string); +#if !defined(YOSYS_DISABLE_SPAWN) int run_command(const std::string &command, std::function process_line = std::function()); +#endif std::string make_temp_file(std::string template_str = "/tmp/yosys_XXXXXX"); std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX"); bool check_file_exists(std::string filename, bool is_exec = false); diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc index 4be5fd493..ac4defac3 100644 --- a/libs/ezsat/ezminisat.cc +++ b/libs/ezsat/ezminisat.cc @@ -29,11 +29,12 @@ #include #include -#include #include -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__wasm) +# include # include +# define HAS_ALARM #endif #include "../minisat/Solver.h" @@ -84,7 +85,7 @@ bool ezMiniSAT::eliminated(int idx) } #endif -#ifndef _WIN32 +#if defined(HAS_ALARM) ezMiniSAT *ezMiniSAT::alarmHandlerThis = NULL; clock_t ezMiniSAT::alarmHandlerTimeout = 0; @@ -183,7 +184,7 @@ contradiction: #endif } -#ifndef _WIN32 +#if defined(HAS_ALARM) struct sigaction sig_action; struct sigaction old_sig_action; int old_alarm_timeout = 0; @@ -202,7 +203,7 @@ contradiction: bool foundSolution = minisatSolver->solve(assumps); -#ifndef _WIN32 +#if defined(HAS_ALARM) if (solverTimeout > 0) { if (alarmHandlerTimeout == 0) solverTimoutStatus = true; diff --git a/libs/minisat/00_PATCH_wasm.patch b/libs/minisat/00_PATCH_wasm.patch new file mode 100644 index 000000000..0bcff7d77 --- /dev/null +++ b/libs/minisat/00_PATCH_wasm.patch @@ -0,0 +1,34 @@ +--- System.cc ++++ System.cc +@@ -101,7 +101,7 @@ double Minisat::memUsedPeak(bool) { return 0; } + #endif + + +-#if !defined(_MSC_VER) && !defined(__MINGW32__) ++#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__wasm) + void Minisat::limitMemory(uint64_t max_mem_mb) + { + // FIXME: OpenBSD does not support RLIMIT_AS. Not sure how well RLIMIT_DATA works instead. +@@ -133,7 +133,7 @@ void Minisat::limitMemory(uint64_t /*max_mem_mb*/) + #endif + + +-#if !defined(_MSC_VER) && !defined(__MINGW32__) ++#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__wasm) + void Minisat::limitTime(uint32_t max_cpu_time) + { + if (max_cpu_time != 0){ +@@ -156,9 +156,13 @@ void Minisat::limitTime(uint32_t /*max_cpu_time*/) + + void Minisat::sigTerm(void handler(int)) + { ++#if defined(__wasm) ++ (void)handler; ++#else + signal(SIGINT, handler); + signal(SIGTERM,handler); + #ifdef SIGXCPU + signal(SIGXCPU,handler); + #endif ++#endif + } diff --git a/libs/minisat/00_UPDATE.sh b/libs/minisat/00_UPDATE.sh index ea26215ab..51107e450 100755 --- a/libs/minisat/00_UPDATE.sh +++ b/libs/minisat/00_UPDATE.sh @@ -16,4 +16,4 @@ patch -p0 < 00_PATCH_mkLit_default_arg.patch patch -p0 < 00_PATCH_remove_zlib.patch patch -p0 < 00_PATCH_no_fpu_control.patch patch -p0 < 00_PATCH_typofixes.patch - +patch -p0 < 00_PATCH_wasm.patch diff --git a/libs/minisat/System.cc b/libs/minisat/System.cc index 1921a1d71..345be8c4c 100644 --- a/libs/minisat/System.cc +++ b/libs/minisat/System.cc @@ -101,7 +101,7 @@ double Minisat::memUsedPeak(bool) { return 0; } #endif -#if !defined(_MSC_VER) && !defined(__MINGW32__) +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__wasm) void Minisat::limitMemory(uint64_t max_mem_mb) { // FIXME: OpenBSD does not support RLIMIT_AS. Not sure how well RLIMIT_DATA works instead. @@ -133,7 +133,7 @@ void Minisat::limitMemory(uint64_t /*max_mem_mb*/) #endif -#if !defined(_MSC_VER) && !defined(__MINGW32__) +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__wasm) void Minisat::limitTime(uint32_t max_cpu_time) { if (max_cpu_time != 0){ @@ -156,9 +156,13 @@ void Minisat::limitTime(uint32_t /*max_cpu_time*/) void Minisat::sigTerm(void handler(int)) { +#if defined(__wasm) + (void)handler; +#else signal(SIGINT, handler); signal(SIGTERM,handler); #ifdef SIGXCPU signal(SIGXCPU,handler); #endif +#endif } diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 60f20fa6d..a88980eaf 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -1,5 +1,7 @@ +ifeq ($(DISABLE_SPAWN),0) OBJS += passes/cmds/exec.o +endif OBJS += passes/cmds/add.o OBJS += passes/cmds/delete.o OBJS += passes/cmds/design.o @@ -32,6 +34,8 @@ OBJS += passes/cmds/chformal.o OBJS += passes/cmds/chtype.o OBJS += passes/cmds/blackbox.o OBJS += passes/cmds/ltp.o +ifeq ($(DISABLE_SPAWN),0) OBJS += passes/cmds/bugpoint.o +endif OBJS += passes/cmds/scratchpad.o OBJS += passes/cmds/logger.o diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 628ac4c5e..89d27c9aa 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -101,8 +101,8 @@ struct CoverPass : public Pass { const std::string &filename = args[++argidx]; FILE *f = nullptr; if (args[argidx-1] == "-d") { - #ifdef _WIN32 - log_cmd_error("The 'cover -d' option is not supported on win32.\n"); + #if defined(_WIN32) || defined(__wasm) + log_cmd_error("The 'cover -d' option is not supported on this platform.\n"); #else char filename_buffer[4096]; snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid()); diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 155ed0fcd..fa922454a 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -682,7 +682,7 @@ struct ShowPass : public Pass { std::vector> color_selections; std::vector> label_selections; -#if defined(EMSCRIPTEN) || defined(_WIN32) +#if defined(_WIN32) || defined(YOSYS_DISABLE_SPAWN) std::string format = "dot"; std::string prefix = "show"; #else @@ -849,10 +849,15 @@ struct ShowPass : public Pass { std::string cmd = stringf(DOT_CMD, format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str()); #undef DOT_CMD log("Exec: %s\n", cmd.c_str()); - if (run_command(cmd) != 0) - log_cmd_error("Shell command failed!\n"); + #if !defined(YOSYS_DISABLE_SPAWN) + if (run_command(cmd) != 0) + log_cmd_error("Shell command failed!\n"); + #endif } + #if defined(YOSYS_DISABLE_SPAWN) + log_assert(viewer_exe.empty() && !format.empty()); + #else if (!viewer_exe.empty()) { #ifdef _WIN32 // system()/cmd.exe does not understand single quotes nor @@ -876,6 +881,7 @@ struct ShowPass : public Pass { if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } + #endif if (flag_pause) { #ifdef YOSYS_ENABLE_READLINE diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index a928c57de..7118c1563 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -13,5 +13,6 @@ OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o OBJS += passes/sat/cutpoint.o OBJS += passes/sat/fminit.o +ifeq ($(DISABLE_SPAWN),0) OBJS += passes/sat/qbfsat.o - +endif diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 766b954df..1802ba0de 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -57,7 +57,7 @@ passes/techmap/techmap.inc: techlibs/common/techmap.v passes/techmap/techmap.o: passes/techmap/techmap.inc -ifneq ($(CONFIG),emcc) +ifeq ($(DISABLE_SPAWN),0) TARGETS += $(PROGRAM_PREFIX)yosys-filterlib$(EXE) EXTRA_OBJS += passes/techmap/filterlib.o diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index aff0baa44..fae8b2426 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -771,7 +771,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (abc_script[i] == ';' && abc_script[i+1] == ' ') abc_script[i+1] = '\n'; - FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + std::string buffer = stringf("%s/abc.script", tempdir_name.c_str()); + FILE *f = fopen(buffer.c_str(), "wt"); + if (f == nullptr) + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); fprintf(f, "%s\n", abc_script.c_str()); fclose(f); @@ -807,7 +810,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin handle_loops(); - std::string buffer = stringf("%s/input.blif", tempdir_name.c_str()); + buffer = stringf("%s/input.blif", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); @@ -1541,11 +1544,15 @@ struct AbcPass : public Pass { size_t argidx, g_argidx; bool g_arg_from_cmd = false; +#if defined(__wasm) + const char *pwd = "."; +#else char pwd [PATH_MAX]; if (!getcwd(pwd, sizeof(pwd))) { log_cmd_error("getcwd failed: %s\n", strerror(errno)); log_abort(); } +#endif for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exe" && argidx+1 < args.size()) { diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index bad91a224..0bf547921 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -420,11 +420,15 @@ struct Abc9ExePass : public Pass { } size_t argidx; +#if defined(__wasm) + const char *pwd = "."; +#else char pwd [PATH_MAX]; if (!getcwd(pwd, sizeof(pwd))) { log_cmd_error("getcwd failed: %s\n", strerror(errno)); log_abort(); } +#endif for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exe" && argidx+1 < args.size()) {