diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index a2ffb8d3e..dfdcd88c0 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -8,7 +8,7 @@ runs: shell: bash run: | sudo apt-get update - sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev + sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev libbz2-dev - name: Install macOS Dependencies if: runner.os == 'macOS' diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml index 74c3e2639..203c20bb4 100644 --- a/.github/workflows/test-compile.yml +++ b/.github/workflows/test-compile.yml @@ -30,18 +30,15 @@ jobs: - ubuntu-latest compiler: # oldest supported - - 'clang-14' + - 'clang-10' - 'gcc-10' # newest, make sure to update maximum standard step to match - - 'clang-18' + - 'clang-19' - 'gcc-13' include: # macOS - os: macos-13 compiler: 'clang' - # oldest clang not available on ubuntu-latest - - os: ubuntu-20.04 - compiler: 'clang-10' fail-fast: false steps: - name: Checkout Yosys @@ -72,7 +69,7 @@ jobs: # maximum standard, only on newest compilers - name: Build C++20 - if: ${{ matrix.compiler == 'clang-18' || matrix.compiler == 'gcc-13' }} + if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-13' }} shell: bash run: | make config-$CC_SHORT diff --git a/CHANGELOG b/CHANGELOG index 1ebaeb24a..d1c1f560a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,21 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.48 .. Yosys 0.49-dev +Yosys 0.49 .. Yosys 0.50-dev -------------------------- +Yosys 0.48 .. Yosys 0.49 +-------------------------- + * Various + - "$scopeinfo" cells are now part of JSON export by default. + - Added option to specify hierarchical separator for "flatten". + - Improved "wreduce" to handle more cases of operator size reduction. + - Updated hashing interface, see docs/source/yosys_internals/hashing.rst + for breaking API changes. + + * New commands and options + - Added "-noscopeinfo" option to "json" and "write_json" pass. + Yosys 0.47 .. Yosys 0.48 -------------------------- * Various diff --git a/COPYING b/COPYING index e8b123be2..2d962dddc 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ ISC License -Copyright (C) 2012 - 2020 Claire Xenia Wolf +Copyright (C) 2012 - 2025 Claire Xenia Wolf Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/Makefile b/Makefile index 1ef5bd160..896d44203 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,7 @@ CONFIG := none # CONFIG := clang # CONFIG := gcc -# CONFIG := afl-gcc # CONFIG := wasi -# CONFIG := mxe # CONFIG := msys2-32 # CONFIG := msys2-64 @@ -155,7 +153,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.48+0 +YOSYS_VER := 0.49+1 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -171,7 +169,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline aaa5347.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 427b5a2.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -265,16 +263,6 @@ ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif -else ifeq ($(CONFIG),afl-gcc) -CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc -CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" - -else ifeq ($(CONFIG),cygwin) -CXX = g++ -CXXFLAGS += -std=gnu++11 $(OPT_LEVEL) -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" - else ifeq ($(CONFIG),wasi) ifeq ($(WASI_SDK),) CXX = clang++ @@ -302,18 +290,6 @@ 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++ -CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -Wno-attributes -CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s -LIBS := $(filter-out -lrt,$(LIBS)) -ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" -# TODO: Try to solve pthread linking issue in more appropriate way -ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LINKFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" -EXE = .exe - else ifeq ($(CONFIG),msys2-32) CXX = i686-w64-mingw32-g++ CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR @@ -340,7 +316,7 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" LTOFLAGS = else -$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, mxe, msys2-32, msys2-64, none) +$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, msys2-32, msys2-64, none) endif @@ -392,9 +368,6 @@ ifeq ($(LINK_TERMCAP),1) LIBS += -ltermcap ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" endif -ifeq ($(CONFIG),mxe) -LIBS += -ltermcap -endif else ifeq ($(ENABLE_EDITLINE),1) CXXFLAGS += -DYOSYS_ENABLE_EDITLINE @@ -443,17 +416,12 @@ TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) TCL_LIBS ?= -l$(TCL_VERSION) endif -ifeq ($(CONFIG),mxe) -CXXFLAGS += -DYOSYS_ENABLE_TCL -LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv -else CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) ifneq (,$(findstring TCL_WITH_EXTERNAL_TOMMATH,$(CXXFLAGS))) LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libtommath || echo) endif endif -endif ifeq ($(ENABLE_GCOV),1) CXXFLAGS += --coverage @@ -842,71 +810,101 @@ else ABCOPT="" endif -# When YOSYS_NOVERIFIC is set as a make variable, also export it to the -# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_ -# `make test YOSYS_NOVERIFIC=1` will run with verific disabled. -ifeq ($(YOSYS_NOVERIFIC),1) -export YOSYS_NOVERIFIC +# Tests that generate .mk with tests/gen-tests-makefile.sh +MK_TEST_DIRS = +MK_TEST_DIRS += tests/arch/anlogic +MK_TEST_DIRS += tests/arch/ecp5 +MK_TEST_DIRS += tests/arch/efinix +MK_TEST_DIRS += tests/arch/gatemate +MK_TEST_DIRS += tests/arch/gowin +MK_TEST_DIRS += tests/arch/ice40 +MK_TEST_DIRS += tests/arch/intel_alm +MK_TEST_DIRS += tests/arch/machxo2 +MK_TEST_DIRS += tests/arch/microchip +MK_TEST_DIRS += tests/arch/nanoxplore +MK_TEST_DIRS += tests/arch/nexus +MK_TEST_DIRS += tests/arch/quicklogic/pp3 +MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f +MK_TEST_DIRS += tests/arch/xilinx +MK_TEST_DIRS += tests/opt +MK_TEST_DIRS += tests/sat +MK_TEST_DIRS += tests/sim +MK_TEST_DIRS += tests/svtypes +MK_TEST_DIRS += tests/techmap +MK_TEST_DIRS += tests/various +ifeq ($(ENABLE_VERIFIC),1) +ifneq ($(YOSYS_NOVERIFIC),1) +MK_TEST_DIRS += tests/verific +endif +endif +MK_TEST_DIRS += tests/verilog + +# Tests that don't generate .mk +SH_TEST_DIRS = +SH_TEST_DIRS += tests/simple +SH_TEST_DIRS += tests/simple_abc9 +SH_TEST_DIRS += tests/hana +SH_TEST_DIRS += tests/asicworld +# SH_TEST_DIRS += tests/realmath +SH_TEST_DIRS += tests/share +SH_TEST_DIRS += tests/opt_share +SH_TEST_DIRS += tests/fsm +SH_TEST_DIRS += tests/memlib +SH_TEST_DIRS += tests/bram +SH_TEST_DIRS += tests/svinterfaces +SH_TEST_DIRS += tests/xprop +SH_TEST_DIRS += tests/select +SH_TEST_DIRS += tests/proc +SH_TEST_DIRS += tests/blif +SH_TEST_DIRS += tests/arch +SH_TEST_DIRS += tests/rpc +SH_TEST_DIRS += tests/memfile +SH_TEST_DIRS += tests/fmt +SH_TEST_DIRS += tests/cxxrtl +ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) +SH_TEST_DIRS += tests/functional endif -test: $(TARGETS) $(EXTRA_TARGETS) -ifeq ($(ENABLE_VERIFIC),1) -ifeq ($(YOSYS_NOVERIFIC),1) - @echo - @echo "Running tests without verific support due to YOSYS_NOVERIFIC=1" - @echo -else - +cd tests/verific && bash run-test.sh $(SEEDOPT) -endif -endif - +cd tests/simple && bash run-test.sh $(SEEDOPT) - +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) - +cd tests/hana && bash run-test.sh $(SEEDOPT) - +cd tests/asicworld && bash run-test.sh $(SEEDOPT) - # +cd tests/realmath && bash run-test.sh $(SEEDOPT) - +cd tests/share && bash run-test.sh $(SEEDOPT) - +cd tests/opt_share && bash run-test.sh $(SEEDOPT) - +cd tests/fsm && bash run-test.sh $(SEEDOPT) - +cd tests/techmap && bash run-test.sh - +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT) - +cd tests/memlib && bash run-test.sh $(SEEDOPT) - +cd tests/bram && bash run-test.sh $(SEEDOPT) - +cd tests/various && bash run-test.sh - +cd tests/select && bash run-test.sh - +cd tests/sat && bash run-test.sh - +cd tests/sim && bash run-test.sh - +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) - +cd tests/svtypes && bash run-test.sh $(SEEDOPT) - +cd tests/proc && bash run-test.sh - +cd tests/blif && bash run-test.sh - +cd tests/opt && bash run-test.sh - +cd tests/aiger && bash run-test.sh $(ABCOPT) - +cd tests/arch && bash run-test.sh - +cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT) - +cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT) - +cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT) - +cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT) - +cd tests/arch/efinix && bash run-test.sh $(SEEDOPT) - +cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) - +cd tests/arch/gowin && bash run-test.sh $(SEEDOPT) - +cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT) - +cd tests/arch/nanoxplore && bash run-test.sh $(SEEDOPT) - +cd tests/arch/nexus && bash run-test.sh $(SEEDOPT) - +cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT) - +cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT) - +cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT) - +cd tests/arch/microchip && bash run-test.sh $(SEEDOPT) - +cd tests/rpc && bash run-test.sh - +cd tests/memfile && bash run-test.sh - +cd tests/verilog && bash run-test.sh - +cd tests/xprop && bash run-test.sh $(SEEDOPT) - +cd tests/fmt && bash run-test.sh - +cd tests/cxxrtl && bash run-test.sh -ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) - +cd tests/functional && bash run-test.sh -endif +# Tests that don't generate .mk and need special args +SH_ABC_TEST_DIRS = +SH_ABC_TEST_DIRS += tests/memories +SH_ABC_TEST_DIRS += tests/aiger +SH_ABC_TEST_DIRS += tests/alumacc + +# seed-tests/ is a dummy string, not a directory +.PHONY: seed-tests +seed-tests: $(SH_TEST_DIRS:%=seed-tests/%) +.PHONY: seed-tests/% +seed-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS) + +cd $* && bash run-test.sh $(SEEDOPT) + +@echo "...passed tests in $*" + +# abcopt-tests/ is a dummy string, not a directory +.PHONY: abcopt-tests +abcopt-tests: $(SH_ABC_TEST_DIRS:%=abcopt-tests/%) +abcopt-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS) + +cd $* && bash run-test.sh $(ABCOPT) $(SEEDOPT) + +@echo "...passed tests in $*" + +# makefile-tests/ is a dummy string, not a directory +.PHONY: makefile-tests +makefile-tests: $(MK_TEST_DIRS:%=makefile-tests/%) +# this target actually emits .mk files +%.mk: + +cd $(dir $*) && bash run-test.sh +# this one spawns submake on each +makefile-tests/%: %/run-test.mk $(TARGETS) $(EXTRA_TARGETS) + $(MAKE) -C $* -f run-test.mk + +@echo "...passed tests in $*" + +test: makefile-tests abcopt-tests seed-tests @echo "" @echo " Passed \"make test\"." +ifeq ($(ENABLE_VERIFIC),1) +ifeq ($(YOSYS_NOVERIFIC),1) + @echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1." +endif +endif @echo "" VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all @@ -1101,19 +1099,6 @@ vcxsrc: $(GENFILES) $(EXTRA_TARGETS) zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/ rm -f srcfiles.txt kernel/version.cc -ifeq ($(CONFIG),mxe) -mxebin: $(TARGETS) $(EXTRA_TARGETS) - rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip} - mkdir -p yosys-win32-mxebin-$(YOSYS_VER) - cp -r $(PROGRAM_PREFIX)yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/ -ifeq ($(ENABLE_ABC),1) - cp -r $(PROGRAM_PREFIX)yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/ -endif - echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt - echo -en 'Documentation at https://yosyshq.net/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt - zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/ -endif - config-clean: clean rm -f Makefile.conf @@ -1129,9 +1114,6 @@ config-gcc-static: clean echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf -config-afl-gcc: clean - echo 'CONFIG := afl-gcc' > Makefile.conf - config-wasi: clean echo 'CONFIG := wasi' > Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf @@ -1140,10 +1122,6 @@ config-wasi: clean 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 - config-msys2-32: clean echo 'CONFIG := msys2-32' > Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf @@ -1152,9 +1130,6 @@ config-msys2-64: clean echo 'CONFIG := msys2-64' > Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf -config-cygwin: clean - echo 'CONFIG := cygwin' > Makefile.conf - config-gcov: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GCOV := 1' >> Makefile.conf @@ -1183,5 +1158,5 @@ echo-cxx: -include kernel/*.d -include techlibs/*/*.d -.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc mxebin -.PHONY: config-clean config-clang config-gcc config-gcc-static config-afl-gcc config-gprof config-sudo +.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc +.PHONY: config-clean config-clang config-gcc config-gcc-static config-gprof config-sudo diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b72caf119..931454ada 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -47,7 +47,7 @@ struct Scheduler { struct Vertex { T *data; Vertex *prev, *next; - pool preds, succs; + pool preds, succs; Vertex() : data(NULL), prev(this), next(this) {} Vertex(T *data) : data(data), prev(NULL), next(NULL) {} @@ -300,10 +300,10 @@ struct FlowGraph { }; std::vector nodes; - dict> wire_comb_defs, wire_sync_defs, wire_uses; - dict, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses; + dict> wire_comb_defs, wire_sync_defs, wire_uses; + dict> node_comb_defs, node_sync_defs, node_uses; dict wire_def_inlinable; - dict> wire_use_inlinable; + dict> wire_use_inlinable; dict bit_has_state; ~FlowGraph() @@ -365,7 +365,7 @@ struct FlowGraph { return false; } - bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const + bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const { // Can the wire be inlined, knowing that the given nodes are reachable? if (nodes.size() != 1) @@ -3080,7 +3080,7 @@ struct CxxrtlWorker { // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only // a single delta cycle. Scheduler scheduler; - dict::Vertex*, hash_ptr_ops> node_vertex_map; + dict::Vertex*> node_vertex_map; for (auto node : flow.nodes) node_vertex_map[node] = scheduler.add(node); for (auto node_comb_def : flow.node_comb_defs) { @@ -3095,7 +3095,7 @@ struct CxxrtlWorker { // Find out whether the order includes any feedback arcs. std::vector node_order; - pool evaluated_nodes; + pool evaluated_nodes; pool feedback_wires; for (auto vertex : scheduler.schedule()) { auto node = vertex->data; @@ -3139,7 +3139,7 @@ struct CxxrtlWorker { } // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. - pool worklist; + pool worklist; for (auto node : flow.nodes) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type)) worklist.insert(node); // node evaluates a submodule @@ -3159,8 +3159,8 @@ struct CxxrtlWorker { worklist.insert(node); // node drives public wires } } - dict> live_wires; - pool live_nodes; + dict> live_wires; + pool live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); live_nodes.insert(node); @@ -3290,15 +3290,15 @@ struct CxxrtlWorker { // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members) // and collect reachable wire users. - pool worklist; + pool worklist; for (auto node : flow.nodes) { if (flow.node_comb_defs.count(node)) for (auto wire : flow.node_comb_defs[node]) if (debug_wire_types[wire].is_outline()) worklist.insert(node); // node drives outline } - dict> debug_live_wires; - pool debug_live_nodes; + dict> debug_live_wires; + pool debug_live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); debug_live_nodes.insert(node); diff --git a/backends/json/json.cc b/backends/json/json.cc index 287c01ead..20d42f626 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -34,6 +34,7 @@ struct JsonWriter bool use_selection; bool aig_mode; bool compat_int_mode; + bool scopeinfo_mode; Design *design; Module *module; @@ -43,9 +44,9 @@ struct JsonWriter dict sigids; pool aig_models; - JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) : + JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode, bool scopeinfo_mode) : f(f), use_selection(use_selection), aig_mode(aig_mode), - compat_int_mode(compat_int_mode) { } + compat_int_mode(compat_int_mode), scopeinfo_mode(scopeinfo_mode) { } string get_string(string str) { @@ -192,9 +193,7 @@ struct JsonWriter for (auto c : module->cells()) { if (use_selection && !module->selected(c)) continue; - // Eventually we will want to emit $scopeinfo, but currently this - // will break JSON netlist consumers like nextpnr - if (c->type == ID($scopeinfo)) + if (!scopeinfo_mode && c->type == ID($scopeinfo)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(c->name).c_str()); @@ -356,6 +355,9 @@ struct JsonBackend : public Backend { log(" -selected\n"); log(" output only select module\n"); log("\n"); + log(" -noscopeinfo\n"); + log(" don't include $scopeinfo cells in the output\n"); + log("\n"); log("\n"); log("The general syntax of the JSON output created by this command is as follows:\n"); log("\n"); @@ -601,6 +603,7 @@ struct JsonBackend : public Backend { bool aig_mode = false; bool compat_int_mode = false; bool use_selection = false; + bool scopeinfo_mode = true; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -617,13 +620,17 @@ struct JsonBackend : public Backend { use_selection = true; continue; } + if (args[argidx] == "-noscopeinfo") { + scopeinfo_mode = false; + continue; + } break; } extra_args(f, filename, args, argidx); log_header(design, "Executing JSON backend.\n"); - JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode); + JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode, scopeinfo_mode); json_writer.write_design(design); } } JsonBackend; @@ -648,6 +655,9 @@ struct JsonPass : public Pass { log(" emit 32-bit or smaller fully-defined parameter values directly\n"); log(" as JSON numbers (for compatibility with old parsers)\n"); log("\n"); + log(" -noscopeinfo\n"); + log(" don't include $scopeinfo cells in the output\n"); + log("\n"); log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } @@ -656,6 +666,7 @@ struct JsonPass : public Pass { std::string filename; bool aig_mode = false; bool compat_int_mode = false; + bool scopeinfo_mode = true; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -672,6 +683,10 @@ struct JsonPass : public Pass { compat_int_mode = true; continue; } + if (args[argidx] == "-noscopeinfo") { + scopeinfo_mode = false; + continue; + } break; } extra_args(args, argidx, design); @@ -693,7 +708,7 @@ struct JsonPass : public Pass { f = &buf; } - JsonWriter json_writer(*f, true, aig_mode, compat_int_mode); + JsonWriter json_writer(*f, true, aig_mode, compat_int_mode, scopeinfo_mode); json_writer.write_design(design); if (!empty) { diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 5fc3ab5a4..2bc7daddc 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -1208,7 +1208,7 @@ class SmtOpts: def helpmsg(self): return """ -s - set SMT solver: z3, yices, boolector, bitwuzla, cvc4, mathsat, dummy + set SMT solver: z3, yices, boolector, bitwuzla, cvc4, cvc5, mathsat, dummy default: yices -S diff --git a/docs/source/conf.py b/docs/source/conf.py index 8c8555b71..a27b4dc64 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -5,12 +5,35 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' -copyright ='2024 YosysHQ GmbH' -yosys_ver = "0.48" +copyright ='2025 YosysHQ GmbH' +yosys_ver = "0.49" # select HTML theme html_theme = 'furo-ys' html_css_files = ['custom.css'] +html_theme_options: dict[str] = { + "source_repository": "https://github.com/YosysHQ/yosys/", + "source_branch": "main", + "source_directory": "docs/source/", +} + +# try to fix the readthedocs detection +html_context: dict[str] = { + "READTHEDOCS": True, + "display_github": True, + "github_user": "YosysHQ", + "github_repo": "yosys", + "slug": "yosys", +} + +# override source_branch if not main +git_slug = os.getenv("READTHEDOCS_VERSION_NAME") +if git_slug not in [None, "latest", "stable"]: + html_theme_options["source_branch"] = git_slug + +# edit only works on branches, not tags +if os.getenv("READTHEDOCS_VERSION_TYPE", "branch") != "branch": + html_theme_options["top_of_page_buttons"] = ["view"] # These folders are copied to the documentation's HTML output html_static_path = ['_static', "_images"] diff --git a/docs/source/index.rst b/docs/source/index.rst index ab1742424..61dc114ef 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,17 +7,6 @@ see :doc:`/introduction`. For a quick guide on how to get started using Yosys, check out :doc:`/getting_started/index`. For the complete list of commands available, go to :ref:`commandindex`. -.. note:: - - This documentation recently went through a major restructure. If you're - looking for something from the previous version and can't find it here, - please `let us know`_. Documentation from before the restructure can still - be found by switching to `version 0.36`_ or earlier. Note that the previous - theme does not include a version switcher. - -.. _let us know: https://github.com/YosysHQ/yosys/issues/new/choose -.. _version 0.36: https://yosyshq.readthedocs.io/projects/yosys/en/0.36/ - .. todo:: look into command ref improvements - Search bar with live drop down suggestions for matching on title / diff --git a/docs/source/yosys_internals/hashing.rst b/docs/source/yosys_internals/hashing.rst new file mode 100644 index 000000000..c6e22c0cf --- /dev/null +++ b/docs/source/yosys_internals/hashing.rst @@ -0,0 +1,166 @@ +Hashing and associative data structures in Yosys +------------------------------------------------ + +Container classes based on hashing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yosys uses ``dict`` and ``pool`` as main container classes. +``dict`` is essentially a replacement for ``std::unordered_map`` +and ``pool`` is a replacement for ``std::unordered_set``. +The main characteristics are: + +* ``dict`` and ``pool`` are about 2x faster than the std containers + (though this claim hasn't been verified for over 10 years) + +* references to elements in a ``dict`` or ``pool`` are invalidated by + insert and remove operations (similar to ``std::vector`` on ``push_back()``). + +* some iterators are invalidated by ``erase()``. specifically, iterators + that have not passed the erased element yet are invalidated. (``erase()`` + itself returns valid iterator to the next element.) + +* no iterators are invalidated by ``insert()``. elements are inserted at + ``begin()``. i.e. only a new iterator that starts at ``begin()`` will see the + inserted elements. + +* the method ``.count(key, iterator)`` is like ``.count(key)`` but only + considers elements that can be reached via the iterator. + +* iterators can be compared. ``it1 < it2`` means that the position of ``t2`` + can be reached via ``t1`` but not vice versa. + +* the method ``.sort()`` can be used to sort the elements in the container + the container stays sorted until elements are added or removed. + +* ``dict`` and ``pool`` will have the same order of iteration across + all compilers, standard libraries and architectures. + +In addition to ``dict`` and ``pool`` there is also an ``idict`` that +creates a bijective map from ``K`` to the integers. For example: + +:: + + idict si; + log("%d\n", si("hello")); // will print 42 + log("%d\n", si("world")); // will print 43 + log("%d\n", si.at("world")); // will print 43 + log("%d\n", si.at("dummy")); // will throw exception + log("%s\n", si[42].c_str())); // will print hello + log("%s\n", si[43].c_str())); // will print world + log("%s\n", si[44].c_str())); // will throw exception + +It is not possible to remove elements from an idict. + +Finally ``mfp`` implements a merge-find set data structure (aka. disjoint-set +or union-find) over the type ``K`` ("mfp" = merge-find-promote). + +The hash function +~~~~~~~~~~~~~~~~~ + +The hash function generally used in Yosys is the XOR version of DJB2: + +:: + + state = ((state << 5) + state) ^ value + +This is an old-school hash designed to hash ASCII characters. Yosys doesn't hash +a lot of ASCII text, but it still happens to be a local optimum due to factors +described later. + +Hash function quality is multi-faceted and highly dependent on what is being +hashed. Yosys isn't concerned by any cryptographic qualities, instead the goal +is minimizing total hashing collision risk given the data patterns within Yosys. +In general, a good hash function typically folds values into a state accumulator +with a mathematical function that is fast to compute and has some beneficial +properties. One of these is the avalanche property, which demands that a small +change such as flipping a bit or incrementing by one in the input produces a +large, unpredictable change in the output. Additionally, the bit independence +criterion states that any pair of output bits should change independently when +any single input bit is inverted. These properties are important for avoiding +hash collision on data patterns like the hash of a sequence not colliding with +its permutation, not losing from the state the information added by hashing +preceding elements, etc. + +DJB2 lacks these properties. Instead, since Yosys hashes large numbers of data +structures composed of incrementing integer IDs, Yosys abuses the predictability +of DJB2 to get lower hash collisions, with regular nature of the hashes +surviving through the interaction with the "modulo prime" operations in the +associative data structures. For example, some most common objects in Yosys are +interned ``IdString``\ s of incrementing indices or ``SigBit``\ s with bit +offsets into wire (represented by its unique ``IdString`` name) as the typical +case. This is what makes DJB2 a local optimum. Additionally, the ADD version of +DJB2 (like above but with addition instead of XOR) is used to this end for some +types, abandoning the general pattern of folding values into a state value. + +Making a type hashable +~~~~~~~~~~~~~~~~~~~~~~ + +Let's first take a look at the external interface on a simplified level. +Generally, to get the hash for ``T obj``, you would call the utility function +``run_hash(const T& obj)``, corresponding to ``hash_ops::hash(obj)``, +the default implementation of which uses ``hash_ops::hash_into(Hasher(), obj)``. +``Hasher`` is the class actually implementing the hash function, hiding its +initialized internal state, and passing it out on ``hash_t yield()`` with +perhaps some finalization steps. + +``hash_ops`` is the star of the show. By default it pulls the ``Hasher h`` +through a ``Hasher T::hash_into(Hasher h)`` method. That's the method you have to +implement to make a record (class or struct) type easily hashable with Yosys +hashlib associative data structures. + +``hash_ops`` is specialized for built-in types like ``int`` or ``bool`` and +treats pointers the same as integers, so it doesn't dereference pointers. Since +many RTLIL data structures like ``RTLIL::Wire`` carry their own unique index +``Hasher::hash_t hashidx_;``, there are specializations for ``hash_ops`` +and others in ``kernel/hashlib.h`` that actually dereference the pointers and +call ``hash_into`` on the instances pointed to. + +``hash_ops`` is also specialized for simple compound types like +``std::pair`` by calling hash_into in sequence on its members. For flexible +size containers like ``std::vector`` the size of the container is hashed +first. That is also how implementing hashing for a custom record data type +should be - unless there is strong reason to do otherwise, call ``h.eat(m)`` on +the ``Hasher h`` you have received for each member in sequence and ``return +h;``. + +The ``hash_ops::hash(obj)`` method is not indended to be called when +context of implementing the hashing for a record or other compound type. +When writing it, you should connect it to ``hash_ops::hash_into(Hasher h)`` +as shown below. If you have a strong reason to do so, and you have +to create a special implementation for top-level hashing, look at how +``hash_ops::hash(...)`` is implemented in ``kernel/rtlil.h``. + +Porting plugins from the legacy interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, the interface to implement hashing on custom types was just +``unsigned int T::hash() const``. This meant hashes for members were computed +independently and then ad-hoc combined with the hash function with some xorshift +operations thrown in to mix bits together somewhat. A plugin can stay compatible +with both versions prior and after the break by implementing both interfaces +based on the existance and value of `YS_HASHING_VERSION`. + +.. code-block:: cpp + :caption: Example hash compatibility wrapper + :name: hash_plugin_compat + + #ifndef YS_HASHING_VERSION + unsigned int T::hash() const { + return mkhash(a, b); + } + #elif YS_HASHING_VERSION == 1 + Hasher T::hash_into(Hasher h) const { + h.eat(a); + h.eat(b); + return h; + } + Hasher T::hash() const { + Hasher h; + h.eat(*this); + return h; + } + #else + #error "Unsupported hashing interface" + #endif + +Feel free to contact Yosys maintainers with related issues. diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst index 9631e8653..3dd4224fa 100644 --- a/docs/source/yosys_internals/index.rst +++ b/docs/source/yosys_internals/index.rst @@ -39,3 +39,4 @@ as reference to implement a similar system in any language. extending_yosys/index techmap verilog + hashing diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc index f163dff9e..0882ba804 100644 --- a/examples/cxx-api/scopeinfo_example.cc +++ b/examples/cxx-api/scopeinfo_example.cc @@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass { // Shuffle wires so this example produces more interesting outputs std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) { - return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5); + return mkhash_xorshift(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 0x2c9277b5); }); ModuleHdlnameIndex index(module); diff --git a/flake.nix b/flake.nix index 90fa5328c..19ba59f17 100644 --- a/flake.nix +++ b/flake.nix @@ -14,15 +14,15 @@ }; # TODO: don't override src when ./abc is empty # which happens when the command used is `nix build` and not `nix build ?submodules=1` - abc-verifier = pkgs.abc-verifier.overrideAttrs(x: y: {src = ./abc;}); + abc-verifier = pkgs.abc-verifier; yosys = pkgs.clangStdenv.mkDerivation { name = "yosys"; src = ./. ; - buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git pkg-configUpstream llvmPackages.bintools ]; + buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 zlib git pkg-configUpstream llvmPackages.bintools ]; checkInputs = with pkgs; [ gtest ]; propagatedBuildInputs = [ abc-verifier ]; preConfigure = "make config-clang"; - checkTarget = "test"; + checkTarget = "unit-test"; installPhase = '' make install PREFIX=$out ABCEXTERNAL=yosys-abc ln -s ${abc-verifier}/bin/abc $out/bin/yosys-abc @@ -41,7 +41,7 @@ packages.default = yosys; defaultPackage = yosys; devShell = pkgs.mkShell { - buildInputs = with pkgs; [ clang llvmPackages.bintools bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git gtest abc-verifier ]; + buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog boost python3Packages.boost ]; }; } ); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index bdff015e3..2c2d408ce 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -177,7 +177,7 @@ namespace AST { // for dict<> and pool<> unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } // this nodes type AstNodeType type; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 08b8c505d..cdd0ed802 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -407,7 +407,7 @@ static const std::string verific_unescape(const char *value) } #endif -void VerificImporter::import_attributes(dict &attributes, DesignObj *obj, Netlist *nl) +void VerificImporter::import_attributes(dict &attributes, DesignObj *obj, Netlist *nl, int wire_width_hint) { if (!obj) return; @@ -433,10 +433,18 @@ void VerificImporter::import_attributes(dict &att auto type_range = nl->GetTypeRange(obj->Name()); if (!type_range) return; - if (type_range->IsTypeScalar()) { + if (nl->IsFromVhdl() && type_range->IsTypeScalar()) { const long long bottom_bound = type_range->GetScalarRangeLeftBound(); const long long top_bound = type_range->GetScalarRangeRightBound(); - const unsigned bit_width = type_range->NumElements(); + int bit_width = type_range->LeftRangeBound()+1; + if (bit_width <= 0) { // VHDL null range + if (wire_width_hint >= 0) + bit_width = wire_width_hint; + else + bit_width = 64; //fallback, currently largest integer width that verific will allow (in vhdl2019 mode) + } else { + if (wire_width_hint >= 0) log_assert(bit_width == wire_width_hint); + } RTLIL::Const bottom_const(bottom_bound, bit_width); RTLIL::Const top_const(top_bound, bit_width); if (bottom_bound < 0 || top_bound < 0) { @@ -611,7 +619,7 @@ RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *p } } -RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) +RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) { RTLIL::SigSpec sig; RTLIL::Wire *dummy_wire = NULL; @@ -1456,7 +1464,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma log("Importing module %s.\n", RTLIL::id2cstr(module->name)); } import_attributes(module->attributes, nl, nl); - module->set_string_attribute(ID::hdlname, nl->CellBaseName()); + if (module->name.isPublic()) + module->set_string_attribute(ID::hdlname, nl->CellBaseName()); module->set_string_attribute(ID(library), nl->Owner()->Owner()->Name()); #ifdef VERIFIC_VHDL_SUPPORT if (nl->IsFromVhdl()) { @@ -1499,7 +1508,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma log(" importing port %s.\n", port->Name()); RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name())); - import_attributes(wire->attributes, port, nl); + import_attributes(wire->attributes, port, nl, 1); wire->port_id = nl->IndexOf(port) + 1; @@ -1527,11 +1536,11 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); wire->upto = portbus->IsUp(); - import_attributes(wire->attributes, portbus, nl); + import_attributes(wire->attributes, portbus, nl, portbus->Size()); SetIter si ; Port *port ; FOREACH_PORT_OF_PORTBUS(portbus, si, port) { - import_attributes(wire->attributes, port->GetNet(), nl); + import_attributes(wire->attributes, port->GetNet(), nl, portbus->Size()); break; } bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN; @@ -1568,9 +1577,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma module->fixup_ports(); - dict init_nets; - pool anyconst_nets, anyseq_nets; - pool allconst_nets, allseq_nets; + dict init_nets; + pool anyconst_nets, anyseq_nets; + pool allconst_nets, allseq_nets; any_all_nets.clear(); FOREACH_NET_OF_NETLIST(nl, mi, net) @@ -1693,7 +1702,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma log(" importing net %s as %s.\n", net->Name(), log_id(wire_name)); RTLIL::Wire *wire = module->addWire(wire_name); - import_attributes(wire->attributes, net, nl); + import_attributes(wire->attributes, net, nl, 1); net_map[net] = wire; } @@ -1722,10 +1731,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma MapIter mibus; FOREACH_NET_OF_NETBUS(netbus, mibus, net) { if (net) - import_attributes(wire->attributes, net, nl); + import_attributes(wire->attributes, net, nl, netbus->Size()); break; } - import_attributes(wire->attributes, netbus, nl); + import_attributes(wire->attributes, netbus, nl, netbus->Size()); RTLIL::Const initval = Const(State::Sx, GetSize(wire)); bool initval_valid = false; @@ -1833,10 +1842,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT - pool sva_asserts; - pool sva_assumes; - pool sva_covers; - pool sva_triggers; + pool sva_asserts; + pool sva_assumes; + pool sva_covers; + pool sva_triggers; #endif pool past_ffs; diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index 0b9616e19..4e9c7a305 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -71,7 +71,7 @@ struct VerificImporter std::map net_map; std::map sva_posedge_map; - pool any_all_nets; + pool any_all_nets; bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; bool mode_autocover, mode_fullinit; @@ -81,7 +81,7 @@ struct VerificImporter RTLIL::SigBit net_map_at(Verific::Net *net); RTLIL::IdString new_verific_id(Verific::DesignObj *obj); - void import_attributes(dict &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr); + void import_attributes(dict &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr, int wire_width_hint = -1); RTLIL::SigBit netToSigBit(Verific::Net *net); RTLIL::SigSpec operatorInput(Verific::Instance *inst); @@ -89,7 +89,7 @@ struct VerificImporter RTLIL::SigSpec operatorInput2(Verific::Instance *inst); RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname); RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname); - RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); + RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name); bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index ef8247e83..860d3c166 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1051,7 +1051,7 @@ struct VerificSvaImporter msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } - dict check_expression_cache; + dict check_expression_cache; bool check_expression(Net *net, bool raise_error = false) { diff --git a/guidelines/GettingStarted b/guidelines/GettingStarted index 110f63185..17fe32523 100644 --- a/guidelines/GettingStarted +++ b/guidelines/GettingStarted @@ -37,57 +37,15 @@ And then executed using the following command: Yosys Data Structures --------------------- -Here is a short list of data structures that you should make yourself familiar -with before you write C++ code for Yosys. The following data structures are all -defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used. + 1. Container classes based on hashing - 1. Yosys Container Classes +Yosys heavily relies on custom container data structures such as dict or pool +defined in kernel/hashlib.h. +dict is essentially a replacement for std::unordered_map +and pool is a replacement for std::unordered_set. Please refer to +docs/source/yosys_internals/hashing.rst for more information on those. -Yosys uses dict and pool as main container classes. dict is -essentially a replacement for std::unordered_map and pool is a -replacement for std::unordered_set. The main characteristics are: - - - dict and pool are about 2x faster than the std containers - - - references to elements in a dict or pool are invalidated by - insert and remove operations (similar to std::vector on push_back()). - - - some iterators are invalidated by erase(). specifically, iterators - that have not passed the erased element yet are invalidated. (erase() - itself returns valid iterator to the next element.) - - - no iterators are invalidated by insert(). elements are inserted at - begin(). i.e. only a new iterator that starts at begin() will see the - inserted elements. - - - the method .count(key, iterator) is like .count(key) but only - considers elements that can be reached via the iterator. - - - iterators can be compared. it1 < it2 means that the position of t2 - can be reached via t1 but not vice versa. - - - the method .sort() can be used to sort the elements in the container - the container stays sorted until elements are added or removed. - - - dict and pool will have the same order of iteration across - all compilers, standard libraries and architectures. - -In addition to dict and pool there is also an idict that -creates a bijective map from K to the integers. For example: - - idict si; - log("%d\n", si("hello")); // will print 42 - log("%d\n", si("world")); // will print 43 - log("%d\n", si.at("world")); // will print 43 - log("%d\n", si.at("dummy")); // will throw exception - log("%s\n", si[42].c_str())); // will print hello - log("%s\n", si[43].c_str())); // will print world - log("%s\n", si[44].c_str())); // will throw exception - -It is not possible to remove elements from an idict. - -Finally mfp implements a merge-find set data structure (aka. disjoint-set or -union-find) over the type K ("mfp" = merge-find-promote). +Otherwise, Yosys makes use of the following: 2. Standard STL data types diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index c1ceac14c..821490dca 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -30,7 +30,7 @@ struct BitPatternPool int width; struct bits_t { std::vector bitdata; - mutable unsigned int cached_hash; + mutable Hasher::hash_t cached_hash; bits_t(int width = 0) : bitdata(width), cached_hash(0) { } RTLIL::State &operator[](int index) { return bitdata[index]; @@ -39,14 +39,15 @@ struct BitPatternPool return bitdata[index]; } bool operator==(const bits_t &other) const { - if (hash() != other.hash()) + if (run_hash(*this) != run_hash(other)) return false; return bitdata == other.bitdata; } - unsigned int hash() const { + [[nodiscard]] Hasher hash_into(Hasher h) const { if (!cached_hash) - cached_hash = hash_ops>::hash(bitdata); - return cached_hash; + cached_hash = run_hash(bitdata); + h.eat(cached_hash); + return h; } }; pool database; diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index de0a49394..fd3c7bb67 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -39,13 +39,13 @@ bool AigNode::operator==(const AigNode &other) const return true; } -unsigned int AigNode::hash() const +Hasher AigNode::hash_into(Hasher h) const { - unsigned int h = mkhash_init; - h = mkhash(portname.hash(), portbit); - h = mkhash(h, inverter); - h = mkhash(h, left_parent); - h = mkhash(h, right_parent); + h.eat(portname); + h.eat(portbit); + h.eat(inverter); + h.eat(left_parent); + h.eat(right_parent); return h; } @@ -54,9 +54,10 @@ bool Aig::operator==(const Aig &other) const return name == other.name; } -unsigned int Aig::hash() const +Hasher Aig::hash_into(Hasher h) const { - return hash_ops::hash(name); + h.eat(name); + return h; } struct AigMaker diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h index 8f6d69ba6..f6afcde5e 100644 --- a/kernel/cellaigs.h +++ b/kernel/cellaigs.h @@ -34,7 +34,7 @@ struct AigNode AigNode(); bool operator==(const AigNode &other) const; - unsigned int hash() const; + [[nodiscard]] Hasher hash_into(Hasher h) const; }; struct Aig @@ -44,7 +44,7 @@ struct Aig Aig(Cell *cell); bool operator==(const Aig &other) const; - unsigned int hash() const; + [[nodiscard]] Hasher hash_into(Hasher h) const; }; YOSYS_NAMESPACE_END diff --git a/kernel/consteval.h b/kernel/consteval.h index 73d05f0b3..331d8f128 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -315,9 +315,6 @@ struct ConstEval Macc macc; macc.from_cell(cell); - if (!eval(macc.bit_ports, undef, cell)) - return false; - for (auto &port : macc.ports) { if (!eval(port.in_a, undef, cell)) return false; diff --git a/kernel/driver.cc b/kernel/driver.cc index 6565c472c..a3d85bd90 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/hashlib.h" #include "libs/sha1/sha1.h" #include "libs/cxxopts/include/cxxopts.hpp" #include @@ -282,6 +283,8 @@ int main(int argc, char **argv) ("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging") ("autoidx", "start counting autoidx up from , similar effect to --hash-seed", cxxopts::value(), "") + ("hash-seed", "mix up hashing values with , for extreme optimization and testing", + cxxopts::value(), "") ("A,abort", "will call abort() at the end of the script. for debugging") ("x,experimental", "do not print warnings for the experimental ", cxxopts::value>(), "") @@ -437,6 +440,10 @@ int main(int argc, char **argv) int idx = result["autoidx"].as(); autoidx = idx; } + if (result.count("hash-seed")) { + int seed = result["hash-seed"].as(); + Hasher::set_fudge((Hasher::hash_t)seed); + } if (log_errfile == NULL) { log_files.push_back(stdout); diff --git a/kernel/drivertools.h b/kernel/drivertools.h index 8929c3426..d46217da5 100644 --- a/kernel/drivertools.h +++ b/kernel/drivertools.h @@ -74,10 +74,8 @@ struct DriveBitWire return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(wire->name.hash(), offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; + operator SigBit() const { @@ -107,10 +105,8 @@ struct DriveBitPort return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; + }; @@ -133,10 +129,7 @@ struct DriveBitMarker return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(marker, offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; @@ -171,10 +164,7 @@ public: return multiple_ == other.multiple_; } - unsigned int hash() const - { - return multiple_.hash(); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; struct DriveBit @@ -362,35 +352,7 @@ public: return *this; } - unsigned int hash() const - { - unsigned int inner = 0; - switch (type_) - { - case DriveType::NONE: - inner = 0; - break; - case DriveType::CONSTANT: - inner = constant_; - break; - case DriveType::WIRE: - inner = wire_.hash(); - break; - case DriveType::PORT: - inner = port_.hash(); - break; - case DriveType::MARKER: - inner = marker_.hash(); - break; - case DriveType::MULTIPLE: - inner = multiple_.hash(); - break; - default: - log_abort(); - break; - } - return mkhash((unsigned int)type_, inner); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; bool operator==(const DriveBit &other) const { @@ -511,10 +473,7 @@ struct DriveChunkWire return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(wire->name.hash(), width), offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; explicit operator SigChunk() const { @@ -572,10 +531,7 @@ struct DriveChunkPort return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(mkhash(cell->name.hash(), port.hash()), width), offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; @@ -616,10 +572,7 @@ struct DriveChunkMarker return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(marker, width), offset); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; struct DriveChunkMultiple @@ -659,10 +612,7 @@ public: return false; // TODO implement, canonicalize order } - unsigned int hash() const - { - return mkhash(width_, multiple_.hash()); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; struct DriveChunk @@ -913,35 +863,7 @@ public: bool try_append(DriveBit const &bit); bool try_append(DriveChunk const &chunk); - unsigned int hash() const - { - unsigned int inner = 0; - switch (type_) - { - case DriveType::NONE: - inner = 0; - break; - case DriveType::CONSTANT: - inner = constant_.hash(); - break; - case DriveType::WIRE: - inner = wire_.hash(); - break; - case DriveType::PORT: - inner = port_.hash(); - break; - case DriveType::MARKER: - inner = marker_.hash(); - break; - case DriveType::MULTIPLE: - inner = multiple_.hash(); - break; - default: - log_abort(); - break; - } - return mkhash((unsigned int)type_, inner); - } + [[nodiscard]] Hasher hash_into(Hasher h) const; bool operator==(const DriveChunk &other) const { @@ -1144,17 +1066,20 @@ public: DriveSpec &operator=(DriveBitMarker const &bit) { return *this = DriveBit(bit); } DriveSpec &operator=(DriveBitMultiple const &bit) { return *this = DriveBit(bit); } - unsigned int hash() const { - if (hash_ != 0) return hash_; - + void updhash() const { + if (hash_ != 0) + return; pack(); - hash_ = hash_ops>().hash(chunks_); + hash_ = run_hash(chunks_); hash_ |= (hash_ == 0); - return hash_; } + [[nodiscard]] Hasher hash_into(Hasher h) const; + bool operator==(DriveSpec const &other) const { - if (size() != other.size() || hash() != other.hash()) + updhash(); + other.updhash(); + if (size() != other.size() || hash_ != other.hash_) return false; return chunks() == other.chunks(); } @@ -1187,7 +1112,7 @@ private: bool operator==(const DriveBitId &other) const { return id == other.id; } bool operator!=(const DriveBitId &other) const { return id != other.id; } bool operator<(const DriveBitId &other) const { return id < other.id; } - unsigned int hash() const { return id; } + [[nodiscard]] Hasher hash_into(Hasher h) const; }; // Essentially a dict> but using less memory // and fewer allocations @@ -1333,6 +1258,131 @@ private: } }; +inline Hasher DriveBitWire::hash_into(Hasher h) const +{ + h.eat(wire->name); + h.eat(offset); + return h; +} + +inline Hasher DriveBitPort::hash_into(Hasher h) const +{ + h.eat(cell->name); + h.eat(port); + h.eat(offset); + return h; +} + +inline Hasher DriveBitMarker::hash_into(Hasher h) const +{ + h.eat(marker); + h.eat(offset); + return h; +} + +inline Hasher DriveBitMultiple::hash_into(Hasher h) const +{ + h.eat(multiple_); + return h; +} + +inline Hasher DriveBit::hash_into(Hasher h) const +{ + switch (type_) { + case DriveType::NONE: + h.eat(0); + break; + case DriveType::CONSTANT: + h.eat(constant_); + break; + case DriveType::WIRE: + h.eat(wire_); + break; + case DriveType::PORT: + h.eat(port_); + break; + case DriveType::MARKER: + h.eat(marker_); + break; + case DriveType::MULTIPLE: + h.eat(multiple_); + break; + } + h.eat(type_); + return h; +} + +inline Hasher DriveChunkWire::hash_into(Hasher h) const +{ + h.eat(wire->name); + h.eat(width); + h.eat(offset); + return h; +} + +inline Hasher DriveChunkPort::hash_into(Hasher h) const +{ + h.eat(cell->name); + h.eat(port); + h.eat(width); + h.eat(offset); + return h; +} + +inline Hasher DriveChunkMarker::hash_into(Hasher h) const +{ + h.eat(marker); + h.eat(width); + h.eat(offset); + return h; +} + +inline Hasher DriveChunkMultiple::hash_into(Hasher h) const +{ + h.eat(width_); + h.eat(multiple_); + return h; +} + +inline Hasher DriveChunk::hash_into(Hasher h) const +{ + switch (type_) { + case DriveType::NONE: + h.eat(0); + break; + case DriveType::CONSTANT: + h.eat(constant_); + break; + case DriveType::WIRE: + h.eat(wire_); + break; + case DriveType::PORT: + h.eat(port_); + break; + case DriveType::MARKER: + h.eat(marker_); + break; + case DriveType::MULTIPLE: + h.eat(multiple_); + break; + } + h.eat(type_); + return h; +} + +inline Hasher DriveSpec::hash_into(Hasher h) const +{ + updhash(); + h.eat(hash_); + return h; +} + +inline Hasher DriverMap::DriveBitId::hash_into(Hasher h) const +{ + h.eat(id); + return h; +} + YOSYS_NAMESPACE_END #endif diff --git a/kernel/functional.h b/kernel/functional.h index 61b303e0b..7e5becef4 100644 --- a/kernel/functional.h +++ b/kernel/functional.h @@ -151,7 +151,7 @@ namespace Functional { // returns the data width of a bitvector sort, errors out for other sorts int data_width() const { return std::get<1>(_v).second; } bool operator==(Sort const& other) const { return _v == other._v; } - unsigned int hash() const { return mkhash(_v); } + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(_v); return h; } }; class IR; class Factory; @@ -225,8 +225,10 @@ namespace Functional { const RTLIL::Const &as_const() const { return std::get(_extra); } std::pair as_idstring_pair() const { return std::get>(_extra); } int as_int() const { return std::get(_extra); } - int hash() const { - return mkhash((unsigned int) _fn, mkhash(_extra)); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat((unsigned int) _fn); + h.eat(_extra); + return h; } bool operator==(NodeData const &other) const { return _fn == other._fn && _extra == other._extra; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 859115829..6e3eb32a4 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -17,27 +17,52 @@ #include #include #include - +#include #include +#define YS_HASHING_VERSION 1 + namespace hashlib { +/** + * HASHING + * + * Also refer to docs/source/yosys_internals/hashing.rst + * + * The Hasher knows how to hash 32 and 64-bit integers. That's it. + * In the future, it could be expanded to do vectors with SIMD. + * + * The Hasher doesn't know how to hash common standard containers + * and compositions. However, hashlib provides centralized wrappers. + * + * Hashlib doesn't know how to hash silly Yosys-specific types. + * Hashlib doesn't depend on Yosys and can be used standalone. + * Please don't use hashlib standalone for new projects. + * Never directly include kernel/hashlib.h in Yosys code. + * Instead include kernel/yosys_common.h + * + * The hash_ops type is now always left to its default value, derived + * from templated functions through SFINAE. Providing custom ops is + * still supported. + * + * HASH TABLES + * + * We implement associative data structures with separate chaining. + * Linked lists use integers into the indirection hashtable array + * instead of pointers. + */ + const int hashtable_size_trigger = 2; const int hashtable_size_factor = 3; -// The XOR version of DJB2 -inline unsigned int mkhash(unsigned int a, unsigned int b) { - return ((a << 5) + a) ^ b; -} +namespace legacy { + inline uint32_t djb2_add(uint32_t a, uint32_t b) { + return ((a << 5) + a) + b; + } +}; -// traditionally 5381 is used as starting value for the djb2 hash -const unsigned int mkhash_init = 5381; - -// The ADD version of DJB2 -// (use this version for cache locality in b) -inline unsigned int mkhash_add(unsigned int a, unsigned int b) { - return ((a << 5) + a) + b; -} +template +struct hash_ops; inline unsigned int mkhash_xorshift(unsigned int a) { if (sizeof(a) == 4) { @@ -53,72 +78,113 @@ inline unsigned int mkhash_xorshift(unsigned int a) { return a; } -template struct hash_ops { +class HasherDJB32 { +public: + using hash_t = uint32_t; + + HasherDJB32() { + // traditionally 5381 is used as starting value for the djb2 hash + state = 5381; + } + static void set_fudge(hash_t f) { + fudge = f; + } + +private: + uint32_t state; + static uint32_t fudge; + // The XOR version of DJB2 + [[nodiscard]] + static uint32_t djb2_xor(uint32_t a, uint32_t b) { + uint32_t hash = ((a << 5) + a) ^ b; + return hash; + } + public: + void hash32(uint32_t i) { + state = djb2_xor(i, state); + state = mkhash_xorshift(fudge ^ state); + return; + } + void hash64(uint64_t i) { + state = djb2_xor((uint32_t)(i & 0xFFFFFFFFULL), state); + state = djb2_xor((uint32_t)(i >> 32ULL), state); + state = mkhash_xorshift(fudge ^ state); + return; + } + [[nodiscard]] + hash_t yield() { + return (hash_t)state; + } + + template + void eat(T&& t) { + *this = hash_ops>>::hash_into(std::forward(t), *this); + } + + template + void eat(const T& t) { + *this = hash_ops::hash_into(t, *this); + } + + void commutative_eat(hash_t t) { + state ^= t; + } + + void force(hash_t new_state) { + state = new_state; + } +}; + +using Hasher = HasherDJB32; + +// Boilerplate compressor for trivially implementing +// top-level hash method with hash_into +#define HASH_TOP_LOOP_FST [[nodiscard]] static inline Hasher hash +#define HASH_TOP_LOOP_SND { \ + Hasher h; \ + h = hash_into(a, h); \ + return h; \ +} + +template +struct hash_ops { static inline bool cmp(const T &a, const T &b) { return a == b; } - static inline unsigned int hash(const T &a) { - return a.hash(); - } -}; - -struct hash_int_ops { - template - static inline bool cmp(T a, T b) { - return a == b; - } -}; - -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(bool a) { - return a ? 1 : 0; - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int32_t a) { - return a; - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint32_t a) { - return a; - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); - } -}; - -template<> struct hash_ops { - static inline bool cmp(const std::string &a, const std::string &b) { - return a == b; - } - static inline unsigned int hash(const std::string &a) { - unsigned int v = 0; - for (auto c : a) - v = mkhash(v, c); - return v; + [[nodiscard]] static inline Hasher hash_into(const T &a, Hasher h) { + if constexpr (std::is_integral_v) { + static_assert(sizeof(T) <= sizeof(uint64_t)); + if (sizeof(T) == sizeof(uint64_t)) + h.hash64(a); + else + h.hash32(a); + return h; + } else if constexpr (std::is_enum_v) { + using u_type = std::underlying_type_t; + return hash_ops::hash_into((u_type) a, h); + } else if constexpr (std::is_pointer_v) { + return hash_ops::hash_into((uintptr_t) a, h); + } else if constexpr (std::is_same_v) { + for (auto c : a) + h.hash32(c); + return h; + } else { + return a.hash_into(h); + } } + HASH_TOP_LOOP_FST (const T &a) HASH_TOP_LOOP_SND }; template struct hash_ops> { static inline bool cmp(std::pair a, std::pair b) { return a == b; } - static inline unsigned int hash(std::pair a) { - return mkhash(hash_ops

::hash(a.first), hash_ops::hash(a.second)); + [[nodiscard]] static inline Hasher hash_into(std::pair a, Hasher h) { + h = hash_ops

::hash_into(a.first, h); + h = hash_ops::hash_into(a.second, h); + return h; } + HASH_TOP_LOOP_FST (std::pair a) HASH_TOP_LOOP_SND }; template struct hash_ops> { @@ -126,50 +192,66 @@ template struct hash_ops> { return a == b; } template - static inline typename std::enable_if::type hash(std::tuple) { - return mkhash_init; + static inline typename std::enable_if::type hash_into(std::tuple, Hasher h) { + return h; } template - static inline typename std::enable_if::type hash(std::tuple a) { + static inline typename std::enable_if::type hash_into(std::tuple a, Hasher h) { typedef hash_ops>::type> element_ops_t; - return mkhash(hash(a), element_ops_t::hash(std::get(a))); + h = hash_into(a, h); + h = element_ops_t::hash_into(std::get(a), h); + return h; } + HASH_TOP_LOOP_FST (std::tuple a) HASH_TOP_LOOP_SND }; template struct hash_ops> { static inline bool cmp(std::vector a, std::vector b) { return a == b; } - static inline unsigned int hash(std::vector a) { - unsigned int h = mkhash_init; + [[nodiscard]] static inline Hasher hash_into(std::vector a, Hasher h) { + h.eat((uint32_t)a.size()); for (auto k : a) - h = mkhash(h, hash_ops::hash(k)); + h.eat(k); return h; } + HASH_TOP_LOOP_FST (std::vector a) HASH_TOP_LOOP_SND +}; + +template struct hash_ops> { + static inline bool cmp(std::array a, std::array b) { + return a == b; + } + [[nodiscard]] static inline Hasher hash_into(std::array a, Hasher h) { + for (const auto& k : a) + h = hash_ops::hash_into(k, h); + return h; + } + HASH_TOP_LOOP_FST (std::array a) HASH_TOP_LOOP_SND }; struct hash_cstr_ops { static inline bool cmp(const char *a, const char *b) { - for (int i = 0; a[i] || b[i]; i++) - if (a[i] != b[i]) - return false; - return true; + return strcmp(a, b) == 0; } - static inline unsigned int hash(const char *a) { - unsigned int hash = mkhash_init; + [[nodiscard]] static inline Hasher hash_into(const char *a, Hasher h) { while (*a) - hash = mkhash(hash, *(a++)); - return hash; + h.hash32(*(a++)); + return h; } + HASH_TOP_LOOP_FST (const char *a) HASH_TOP_LOOP_SND }; +template <> struct hash_ops : hash_cstr_ops {}; + struct hash_ptr_ops { static inline bool cmp(const void *a, const void *b) { return a == b; } - static inline unsigned int hash(const void *a) { - return (uintptr_t)a; + [[nodiscard]] static inline Hasher hash_into(const void *a, Hasher h) { + return hash_ops::hash_into((uintptr_t)a, h); } + HASH_TOP_LOOP_FST (const void *a) HASH_TOP_LOOP_SND }; struct hash_obj_ops { @@ -177,22 +259,42 @@ struct hash_obj_ops { return a == b; } template - static inline unsigned int hash(const T *a) { - return a ? a->hash() : 0; + [[nodiscard]] static inline Hasher hash_into(const T *a, Hasher h) { + if (a) + h = a->hash_into(h); + else + h.eat(0); + return h; } + template + HASH_TOP_LOOP_FST (const T *a) HASH_TOP_LOOP_SND }; - +/** + * If you find yourself using this function, think hard + * about if it's the right thing to do. Mixing finalized + * hashes together with XORs or worse can destroy + * desirable qualities of the hash function + */ template +[[nodiscard]] +Hasher::hash_t run_hash(const T& obj) { + return hash_ops::hash(obj).yield(); +} + +/** Refer to docs/source/yosys_internals/hashing.rst */ +template +[[nodiscard]] +[[deprecated]] inline unsigned int mkhash(const T &v) { - return hash_ops().hash(v); + return (unsigned int) run_hash(v); } template<> struct hash_ops { static inline bool cmp(std::monostate a, std::monostate b) { return a == b; } - static inline unsigned int hash(std::monostate) { - return mkhash_init; + [[nodiscard]] static inline Hasher hash_into(std::monostate, Hasher h) { + return h; } }; @@ -200,9 +302,10 @@ template struct hash_ops> { static inline bool cmp(std::variant a, std::variant b) { return a == b; } - static inline unsigned int hash(std::variant a) { - unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a); - return mkhash(a.index(), h); + [[nodiscard]] static inline Hasher hash_into(std::variant a, Hasher h) { + std::visit([& h](const auto &v) { h.eat(v); }, a); + h.eat(a.index()); + return h; } }; @@ -210,18 +313,19 @@ template struct hash_ops> { static inline bool cmp(std::optional a, std::optional b) { return a == b; } - static inline unsigned int hash(std::optional a) { + [[nodiscard]] static inline Hasher hash_into(std::optional a, Hasher h) { if(a.has_value()) - return mkhash(*a); + h.eat(*a); else - return 0; + h.eat(0); + return h; } }; -inline int hashtable_size(int min_size) +inline unsigned int hashtable_size(unsigned int min_size) { // Primes as generated by https://oeis.org/A175953 - static std::vector zero_and_some_primes = { + static std::vector zero_and_some_primes = { 0, 23, 29, 37, 47, 59, 79, 101, 127, 163, 211, 269, 337, 431, 541, 677, 853, 1069, 1361, 1709, 2137, 2677, 3347, 4201, 5261, 6577, 8231, 10289, 12889, 16127, 20161, 25219, 31531, 39419, 49277, 61603, 77017, 96281, @@ -231,13 +335,13 @@ inline int hashtable_size(int min_size) 25499291, 31874149, 39842687, 49803361, 62254207, 77817767, 97272239, 121590311, 151987889, 189984863, 237481091, 296851369, 371064217, 463830313, 579787991, 724735009, 905918777, 1132398479, 1415498113, - 1769372713 + 1769372713, 2211715897, 2764644887, 3455806139 }; for (auto p : zero_and_some_primes) if (p >= min_size) return p; - if (sizeof(int) == 4) + if (sizeof(unsigned int) == 4) throw std::length_error("hash table exceeded maximum size.\nDesign is likely too large for yosys to handle, if possible try not to flatten the design."); for (auto p : zero_and_some_primes) @@ -252,8 +356,7 @@ template> class pool; template> class mfp; template -class dict -{ +class dict { struct entry_t { std::pair udata; @@ -277,11 +380,11 @@ class dict } #endif - int do_hash(const K &key) const + Hasher::hash_t do_hash(const K &key) const { - unsigned int hash = 0; + Hasher::hash_t hash = 0; if (!hashtable.empty()) - hash = ops.hash(key) % (unsigned int)(hashtable.size()); + hash = ops.hash(key).yield() % (unsigned int)(hashtable.size()); return hash; } @@ -292,13 +395,13 @@ class dict for (int i = 0; i < int(entries.size()); i++) { do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size())); - int hash = do_hash(entries[i].udata.first); + Hasher::hash_t hash = do_hash(entries[i].udata.first); entries[i].next = hashtable[hash]; hashtable[hash] = i; } } - int do_erase(int index, int hash) + int do_erase(int index, Hasher::hash_t hash) { do_assert(index < int(entries.size())); if (hashtable.empty() || index < 0) @@ -321,7 +424,7 @@ class dict if (index != back_idx) { - int back_hash = do_hash(entries[back_idx].udata.first); + Hasher::hash_t back_hash = do_hash(entries[back_idx].udata.first); k = hashtable[back_hash]; do_assert(0 <= k && k < int(entries.size())); @@ -347,7 +450,7 @@ class dict return 1; } - int do_lookup(const K &key, int &hash) const + int do_lookup(const K &key, Hasher::hash_t &hash) const { if (hashtable.empty()) return -1; @@ -367,7 +470,7 @@ class dict return index; } - int do_insert(const K &key, int &hash) + int do_insert(const K &key, Hasher::hash_t &hash) { if (hashtable.empty()) { entries.emplace_back(std::pair(key, T()), -1); @@ -380,7 +483,7 @@ class dict return entries.size() - 1; } - int do_insert(const std::pair &value, int &hash) + int do_insert(const std::pair &value, Hasher::hash_t &hash) { if (hashtable.empty()) { entries.emplace_back(value, -1); @@ -393,7 +496,7 @@ class dict return entries.size() - 1; } - int do_insert(std::pair &&rvalue, int &hash) + int do_insert(std::pair &&rvalue, Hasher::hash_t &hash) { if (hashtable.empty()) { auto key = rvalue.first; @@ -505,7 +608,7 @@ public: std::pair insert(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -515,7 +618,7 @@ public: std::pair insert(const std::pair &value) { - int hash = do_hash(value.first); + Hasher::hash_t hash = do_hash(value.first); int i = do_lookup(value.first, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -525,7 +628,7 @@ public: std::pair insert(std::pair &&rvalue) { - int hash = do_hash(rvalue.first); + Hasher::hash_t hash = do_hash(rvalue.first); int i = do_lookup(rvalue.first, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -535,7 +638,7 @@ public: std::pair emplace(K const &key, T const &value) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -545,7 +648,7 @@ public: std::pair emplace(K const &key, T &&rvalue) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -555,7 +658,7 @@ public: std::pair emplace(K &&rkey, T const &value) { - int hash = do_hash(rkey); + Hasher::hash_t hash = do_hash(rkey); int i = do_lookup(rkey, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -565,7 +668,7 @@ public: std::pair emplace(K &&rkey, T &&rvalue) { - int hash = do_hash(rkey); + Hasher::hash_t hash = do_hash(rkey); int i = do_lookup(rkey, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -575,35 +678,35 @@ public: int erase(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int index = do_lookup(key, hash); return do_erase(index, hash); } iterator erase(iterator it) { - int hash = do_hash(it->first); + Hasher::hash_t hash = do_hash(it->first); do_erase(it.index, hash); return ++it; } int count(const K &key) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); return i < 0 ? 0 : 1; } int count(const K &key, const_iterator it) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); return i < 0 || i > it.index ? 0 : 1; } iterator find(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) return end(); @@ -612,7 +715,7 @@ public: const_iterator find(const K &key) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) return end(); @@ -621,7 +724,7 @@ public: T& at(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) throw std::out_of_range("dict::at()"); @@ -630,7 +733,7 @@ public: const T& at(const K &key) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) throw std::out_of_range("dict::at()"); @@ -639,7 +742,7 @@ public: const T& at(const K &key, const T &defval) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) return defval; @@ -648,7 +751,7 @@ public: T& operator[](const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) i = do_insert(std::pair(key, T()), hash); @@ -683,12 +786,14 @@ public: return !operator==(other); } - unsigned int hash() const { - unsigned int h = mkhash_init; - for (auto &entry : entries) { - h ^= hash_ops::hash(entry.udata.first); - h ^= hash_ops::hash(entry.udata.second); + [[nodiscard]] Hasher hash_into(Hasher h) const { + for (auto &it : entries) { + Hasher entry_hash; + entry_hash.eat(it.udata.first); + entry_hash.eat(it.udata.second); + h.commutative_eat(entry_hash.yield()); } + h.eat(entries.size()); return h; } @@ -734,11 +839,11 @@ protected: } #endif - int do_hash(const K &key) const + Hasher::hash_t do_hash(const K &key) const { - unsigned int hash = 0; + Hasher::hash_t hash = 0; if (!hashtable.empty()) - hash = ops.hash(key) % (unsigned int)(hashtable.size()); + hash = ops.hash(key).yield() % (unsigned int)(hashtable.size()); return hash; } @@ -749,13 +854,13 @@ protected: for (int i = 0; i < int(entries.size()); i++) { do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size())); - int hash = do_hash(entries[i].udata); + Hasher::hash_t hash = do_hash(entries[i].udata); entries[i].next = hashtable[hash]; hashtable[hash] = i; } } - int do_erase(int index, int hash) + int do_erase(int index, Hasher::hash_t hash) { do_assert(index < int(entries.size())); if (hashtable.empty() || index < 0) @@ -776,7 +881,7 @@ protected: if (index != back_idx) { - int back_hash = do_hash(entries[back_idx].udata); + Hasher::hash_t back_hash = do_hash(entries[back_idx].udata); k = hashtable[back_hash]; if (k == back_idx) { @@ -800,7 +905,7 @@ protected: return 1; } - int do_lookup(const K &key, int &hash) const + int do_lookup(const K &key, Hasher::hash_t &hash) const { if (hashtable.empty()) return -1; @@ -820,7 +925,7 @@ protected: return index; } - int do_insert(const K &value, int &hash) + int do_insert(const K &value, Hasher::hash_t &hash) { if (hashtable.empty()) { entries.emplace_back(value, -1); @@ -833,7 +938,7 @@ protected: return entries.size() - 1; } - int do_insert(K &&rvalue, int &hash) + int do_insert(K &&rvalue, Hasher::hash_t &hash) { if (hashtable.empty()) { entries.emplace_back(std::forward(rvalue), -1); @@ -940,7 +1045,7 @@ public: std::pair insert(const K &value) { - int hash = do_hash(value); + Hasher::hash_t hash = do_hash(value); int i = do_lookup(value, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -950,7 +1055,7 @@ public: std::pair insert(K &&rvalue) { - int hash = do_hash(rvalue); + Hasher::hash_t hash = do_hash(rvalue); int i = do_lookup(rvalue, hash); if (i >= 0) return std::pair(iterator(this, i), false); @@ -966,35 +1071,35 @@ public: int erase(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int index = do_lookup(key, hash); return do_erase(index, hash); } iterator erase(iterator it) { - int hash = do_hash(*it); + Hasher::hash_t hash = do_hash(*it); do_erase(it.index, hash); return ++it; } int count(const K &key) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); return i < 0 ? 0 : 1; } int count(const K &key, const_iterator it) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); return i < 0 || i > it.index ? 0 : 1; } iterator find(const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) return end(); @@ -1003,7 +1108,7 @@ public: const_iterator find(const K &key) const { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); if (i < 0) return end(); @@ -1012,7 +1117,7 @@ public: bool operator[](const K &key) { - int hash = do_hash(key); + Hasher::hash_t hash = do_hash(key); int i = do_lookup(key, hash); return i >= 0; } @@ -1051,11 +1156,12 @@ public: return !operator==(other); } - unsigned int hash() const { - unsigned int hashval = mkhash_init; - for (auto &it : entries) - hashval ^= ops.hash(it.udata); - return hashval; + [[nodiscard]] Hasher hash_into(Hasher h) const { + for (auto &it : entries) { + h.commutative_eat(ops.hash(it.udata).yield()); + } + h.eat(entries.size()); + return h; } void reserve(size_t n) { entries.reserve(n); } @@ -1105,7 +1211,7 @@ public: int operator()(const K &key) { - int hash = database.do_hash(key); + Hasher::hash_t hash = database.do_hash(key); int i = database.do_lookup(key, hash); if (i < 0) i = database.do_insert(key, hash); @@ -1114,7 +1220,7 @@ public: int at(const K &key) const { - int hash = database.do_hash(key); + Hasher::hash_t hash = database.do_hash(key); int i = database.do_lookup(key, hash); if (i < 0) throw std::out_of_range("idict::at()"); @@ -1123,7 +1229,7 @@ public: int at(const K &key, int defval) const { - int hash = database.do_hash(key); + Hasher::hash_t hash = database.do_hash(key); int i = database.do_lookup(key, hash); if (i < 0) return defval; @@ -1132,7 +1238,7 @@ public: int count(const K &key) const { - int hash = database.do_hash(key); + Hasher::hash_t hash = database.do_hash(key); int i = database.do_lookup(key, hash); return i < 0 ? 0 : 1; } @@ -1176,7 +1282,7 @@ class mfp mutable std::vector parents; public: - typedef typename idict::const_iterator const_iterator; + typedef typename idict::const_iterator const_iterator; constexpr mfp() { diff --git a/kernel/log.h b/kernel/log.h index 4b90cf9dc..e26ef072c 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -363,13 +363,13 @@ void log_dump_val_worker(RTLIL::IdString v); void log_dump_val_worker(RTLIL::SigSpec v); void log_dump_val_worker(RTLIL::State v); -template static inline void log_dump_val_worker(dict &v); -template static inline void log_dump_val_worker(pool &v); +template static inline void log_dump_val_worker(dict &v); +template static inline void log_dump_val_worker(pool &v); template static inline void log_dump_val_worker(std::vector &v); template static inline void log_dump_val_worker(T *ptr); -template -static inline void log_dump_val_worker(dict &v) { +template +static inline void log_dump_val_worker(dict &v) { log("{"); bool first = true; for (auto &it : v) { @@ -382,8 +382,8 @@ static inline void log_dump_val_worker(dict &v) { log(" }"); } -template -static inline void log_dump_val_worker(pool &v) { +template +static inline void log_dump_val_worker(pool &v) { log("{"); bool first = true; for (auto &it : v) { diff --git a/kernel/macc.h b/kernel/macc.h index 4af08cfd8..55940769d 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -30,14 +30,11 @@ struct Macc RTLIL::SigSpec in_a, in_b; bool is_signed, do_subtract; }; - std::vector ports; - RTLIL::SigSpec bit_ports; void optimize(int width) { std::vector new_ports; - RTLIL::SigSpec new_bit_ports; RTLIL::Const off(0, width); for (auto &port : ports) @@ -48,11 +45,6 @@ struct Macc if (GetSize(port.in_a) < GetSize(port.in_b)) std::swap(port.in_a, port.in_b); - if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { - bit_ports.append(port.in_a); - continue; - } - if (port.in_a.is_fully_const() && port.in_b.is_fully_const()) { RTLIL::Const v = port.in_a.as_const(); if (GetSize(port.in_b)) @@ -79,12 +71,6 @@ struct Macc new_ports.push_back(port); } - for (auto &bit : bit_ports) - if (bit == State::S1) - off = const_add(off, RTLIL::Const(1, width), false, false, width); - else if (bit != State::S0) - new_bit_ports.append(bit); - if (off.as_bool()) { port_t port; port.in_a = off; @@ -94,7 +80,6 @@ struct Macc } new_ports.swap(ports); - bit_ports = new_bit_ports; } void from_cell(RTLIL::Cell *cell) @@ -102,7 +87,6 @@ struct Macc RTLIL::SigSpec port_a = cell->getPort(ID::A); ports.clear(); - bit_ports = cell->getPort(ID::B); auto config_bits = cell->getParam(ID::CONFIG); int config_cursor = 0; @@ -145,6 +129,9 @@ struct Macc ports.push_back(this_port); } + for (auto bit : cell->getPort(ID::B)) + ports.push_back(port_t{{bit}, {}, false, false}); + log_assert(config_cursor == config_width); log_assert(port_a_cursor == GetSize(port_a)); } @@ -190,11 +177,11 @@ struct Macc } cell->setPort(ID::A, port_a); - cell->setPort(ID::B, bit_ports); + cell->setPort(ID::B, {}); cell->setParam(ID::CONFIG, config_bits); cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits)); cell->setParam(ID::A_WIDTH, GetSize(port_a)); - cell->setParam(ID::B_WIDTH, GetSize(bit_ports)); + cell->setParam(ID::B_WIDTH, 0); } bool eval(RTLIL::Const &result) const @@ -219,19 +206,12 @@ struct Macc result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result)); } - for (auto bit : bit_ports) { - if (bit.wire) - return false; - result = const_add(result, bit.data, false, false, GetSize(result)); - } - return true; } bool is_simple_product() { - return bit_ports.empty() && - ports.size() == 1 && + return ports.size() == 1 && !ports[0].in_b.empty() && !ports[0].do_subtract; } diff --git a/kernel/modtools.h b/kernel/modtools.h index 34a23b379..27ba98d7d 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -48,8 +48,11 @@ struct ModIndex : public RTLIL::Monitor return cell == other.cell && port == other.port && offset == other.offset; } - unsigned int hash() const { - return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(cell->name); + h.eat(port); + h.eat(offset); + return h; } }; @@ -304,6 +307,7 @@ struct ModWalker RTLIL::Cell *cell; RTLIL::IdString port; int offset; + PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {} bool operator<(const PortBit &other) const { if (cell != other.cell) @@ -317,8 +321,11 @@ struct ModWalker return cell == other.cell && port == other.port && offset == other.offset; } - unsigned int hash() const { - return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(cell->name); + h.eat(port); + h.eat(offset); + return h; } }; @@ -355,7 +362,7 @@ struct ModWalker { for (int i = 0; i < int(bits.size()); i++) if (bits[i].wire != NULL) { - PortBit pbit = { cell, port, i }; + PortBit pbit {cell, port, i}; if (is_output) { signal_drivers[bits[i]].insert(pbit); cell_outputs[cell].insert(bits[i]); diff --git a/kernel/register.cc b/kernel/register.cc index d6e765ce4..11cf5b0e4 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -1221,7 +1221,7 @@ struct LicensePass : public Pass { log(" | |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); log(" | |\n"); - log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf |\n"); + log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf |\n"); log(" | |\n"); log(" | Permission to use, copy, modify, and/or distribute this software for any |\n"); log(" | purpose with or without fee is hereby granted, provided that the above |\n"); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index b279cce6e..cb0f7da78 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN bool RTLIL::IdString::destruct_guard_ok = false; RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; -dict RTLIL::IdString::global_id_index_; +dict RTLIL::IdString::global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT std::vector RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_free_idx_list_; @@ -4476,17 +4476,17 @@ void RTLIL::SigSpec::updhash() const cover("kernel.rtlil.sigspec.hash"); that->pack(); - that->hash_ = mkhash_init; + Hasher h; for (auto &c : that->chunks_) if (c.wire == NULL) { for (auto &v : c.data) - that->hash_ = mkhash(that->hash_, v); + h.eat(v); } else { - that->hash_ = mkhash(that->hash_, c.wire->name.index_); - that->hash_ = mkhash(that->hash_, c.offset); - that->hash_ = mkhash(that->hash_, c.width); + h.eat(c.wire->name.index_); + h.eat(c.offset); + h.eat(c.width); } - + that->hash_ = h.yield(); if (that->hash_ == 0) that->hash_ = 1; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6d3558621..f9cacd151 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -76,329 +76,357 @@ namespace RTLIL struct SyncRule; struct Process; struct Binding; + struct IdString; typedef std::pair SigSig; +}; - struct IdString +struct RTLIL::IdString +{ + #undef YOSYS_XTRACE_GET_PUT + #undef YOSYS_SORT_ID_FREE_LIST + #undef YOSYS_USE_STICKY_IDS + #undef YOSYS_NO_IDS_REFCNT + + // the global id string cache + + static bool destruct_guard_ok; // POD, will be initialized to zero + static struct destruct_guard_t { + destruct_guard_t() { destruct_guard_ok = true; } + ~destruct_guard_t() { destruct_guard_ok = false; } + } destruct_guard; + + static std::vector global_id_storage_; + static dict global_id_index_; +#ifndef YOSYS_NO_IDS_REFCNT + static std::vector global_refcount_storage_; + static std::vector global_free_idx_list_; +#endif + +#ifdef YOSYS_USE_STICKY_IDS + static int last_created_idx_ptr_; + static int last_created_idx_[8]; +#endif + + static inline void xtrace_db_dump() { - #undef YOSYS_XTRACE_GET_PUT - #undef YOSYS_SORT_ID_FREE_LIST - #undef YOSYS_USE_STICKY_IDS - #undef YOSYS_NO_IDS_REFCNT - - // the global id string cache - - static bool destruct_guard_ok; // POD, will be initialized to zero - static struct destruct_guard_t { - destruct_guard_t() { destruct_guard_ok = true; } - ~destruct_guard_t() { destruct_guard_ok = false; } - } destruct_guard; - - static std::vector global_id_storage_; - static dict global_id_index_; - #ifndef YOSYS_NO_IDS_REFCNT - static std::vector global_refcount_storage_; - static std::vector global_free_idx_list_; + #ifdef YOSYS_XTRACE_GET_PUT + for (int idx = 0; idx < GetSize(global_id_storage_); idx++) + { + if (global_id_storage_.at(idx) == nullptr) + log("#X# DB-DUMP index %d: FREE\n", idx); + else + log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx)); + } #endif + } + static inline void checkpoint() + { #ifdef YOSYS_USE_STICKY_IDS - static int last_created_idx_ptr_; - static int last_created_idx_[8]; + last_created_idx_ptr_ = 0; + for (int i = 0; i < 8; i++) { + if (last_created_idx_[i]) + put_reference(last_created_idx_[i]); + last_created_idx_[i] = 0; + } #endif + #ifdef YOSYS_SORT_ID_FREE_LIST + std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater()); + #endif + } - static inline void xtrace_db_dump() - { - #ifdef YOSYS_XTRACE_GET_PUT - for (int idx = 0; idx < GetSize(global_id_storage_); idx++) - { - if (global_id_storage_.at(idx) == nullptr) - log("#X# DB-DUMP index %d: FREE\n", idx); - else - log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx)); - } - #endif + static inline int get_reference(int idx) + { + if (idx) { + #ifndef YOSYS_NO_IDS_REFCNT + global_refcount_storage_[idx]++; + #endif + #ifdef YOSYS_XTRACE_GET_PUT + if (yosys_xtrace) + log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); + #endif + } + return idx; + } + + static int get_reference(const char *p) + { + log_assert(destruct_guard_ok); + + if (!p[0]) + return 0; + + auto it = global_id_index_.find((char*)p); + if (it != global_id_index_.end()) { + #ifndef YOSYS_NO_IDS_REFCNT + global_refcount_storage_.at(it->second)++; + #endif + #ifdef YOSYS_XTRACE_GET_PUT + if (yosys_xtrace) + log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second)); + #endif + return it->second; } - static inline void checkpoint() - { - #ifdef YOSYS_USE_STICKY_IDS - last_created_idx_ptr_ = 0; - for (int i = 0; i < 8; i++) { - if (last_created_idx_[i]) - put_reference(last_created_idx_[i]); - last_created_idx_[i] = 0; - } - #endif - #ifdef YOSYS_SORT_ID_FREE_LIST - std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater()); - #endif - } + log_assert(p[0] == '$' || p[0] == '\\'); + log_assert(p[1] != 0); + for (const char *c = p; *c; c++) + if ((unsigned)*c <= (unsigned)' ') + log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); - static inline int get_reference(int idx) - { - if (idx) { - #ifndef YOSYS_NO_IDS_REFCNT - global_refcount_storage_[idx]++; - #endif - #ifdef YOSYS_XTRACE_GET_PUT - if (yosys_xtrace) - log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); - #endif - } - return idx; - } - - static int get_reference(const char *p) - { - log_assert(destruct_guard_ok); - - if (!p[0]) - return 0; - - auto it = global_id_index_.find((char*)p); - if (it != global_id_index_.end()) { - #ifndef YOSYS_NO_IDS_REFCNT - global_refcount_storage_.at(it->second)++; - #endif - #ifdef YOSYS_XTRACE_GET_PUT - if (yosys_xtrace) - log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second)); - #endif - return it->second; - } - - log_assert(p[0] == '$' || p[0] == '\\'); - log_assert(p[1] != 0); - for (const char *c = p; *c; c++) - if ((unsigned)*c <= (unsigned)' ') - log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); - - #ifndef YOSYS_NO_IDS_REFCNT - if (global_free_idx_list_.empty()) { - if (global_id_storage_.empty()) { - global_refcount_storage_.push_back(0); - global_id_storage_.push_back((char*)""); - global_id_index_[global_id_storage_.back()] = 0; - } - log_assert(global_id_storage_.size() < 0x40000000); - global_free_idx_list_.push_back(global_id_storage_.size()); - global_id_storage_.push_back(nullptr); - global_refcount_storage_.push_back(0); - } - - int idx = global_free_idx_list_.back(); - global_free_idx_list_.pop_back(); - global_id_storage_.at(idx) = strdup(p); - global_id_index_[global_id_storage_.at(idx)] = idx; - global_refcount_storage_.at(idx)++; - #else + #ifndef YOSYS_NO_IDS_REFCNT + if (global_free_idx_list_.empty()) { if (global_id_storage_.empty()) { + global_refcount_storage_.push_back(0); global_id_storage_.push_back((char*)""); global_id_index_[global_id_storage_.back()] = 0; } - int idx = global_id_storage_.size(); - global_id_storage_.push_back(strdup(p)); - global_id_index_[global_id_storage_.back()] = idx; - #endif - - if (yosys_xtrace) { - log("#X# New IdString '%s' with index %d.\n", p, idx); - log_backtrace("-X- ", yosys_xtrace-1); - } - - #ifdef YOSYS_XTRACE_GET_PUT - if (yosys_xtrace) - log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); - #endif - - #ifdef YOSYS_USE_STICKY_IDS - // Avoid Create->Delete->Create pattern - if (last_created_idx_[last_created_idx_ptr_]) - put_reference(last_created_idx_[last_created_idx_ptr_]); - last_created_idx_[last_created_idx_ptr_] = idx; - get_reference(last_created_idx_[last_created_idx_ptr_]); - last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7; - #endif - - return idx; + log_assert(global_id_storage_.size() < 0x40000000); + global_free_idx_list_.push_back(global_id_storage_.size()); + global_id_storage_.push_back(nullptr); + global_refcount_storage_.push_back(0); } - #ifndef YOSYS_NO_IDS_REFCNT - static inline void put_reference(int idx) - { - // put_reference() may be called from destructors after the destructor of - // global_refcount_storage_ has been run. in this case we simply do nothing. - if (!destruct_guard_ok || !idx) - return; - - #ifdef YOSYS_XTRACE_GET_PUT - if (yosys_xtrace) { - log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); - } - #endif - - int &refcount = global_refcount_storage_[idx]; - - if (--refcount > 0) - return; - - log_assert(refcount == 0); - free_reference(idx); - } - static inline void free_reference(int idx) - { - if (yosys_xtrace) { - log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx); - log_backtrace("-X- ", yosys_xtrace-1); - } - - global_id_index_.erase(global_id_storage_.at(idx)); - free(global_id_storage_.at(idx)); - global_id_storage_.at(idx) = nullptr; - global_free_idx_list_.push_back(idx); - } + int idx = global_free_idx_list_.back(); + global_free_idx_list_.pop_back(); + global_id_storage_.at(idx) = strdup(p); + global_id_index_[global_id_storage_.at(idx)] = idx; + global_refcount_storage_.at(idx)++; #else - static inline void put_reference(int) { } + if (global_id_storage_.empty()) { + global_id_storage_.push_back((char*)""); + global_id_index_[global_id_storage_.back()] = 0; + } + int idx = global_id_storage_.size(); + global_id_storage_.push_back(strdup(p)); + global_id_index_[global_id_storage_.back()] = idx; #endif - // the actual IdString object is just is a single int - - int index_; - - inline IdString() : index_(0) { } - inline IdString(const char *str) : index_(get_reference(str)) { } - inline IdString(const IdString &str) : index_(get_reference(str.index_)) { } - inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; } - inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { } - inline ~IdString() { put_reference(index_); } - - inline void operator=(const IdString &rhs) { - put_reference(index_); - index_ = get_reference(rhs.index_); + if (yosys_xtrace) { + log("#X# New IdString '%s' with index %d.\n", p, idx); + log_backtrace("-X- ", yosys_xtrace-1); } - inline void operator=(const char *rhs) { - IdString id(rhs); - *this = id; + #ifdef YOSYS_XTRACE_GET_PUT + if (yosys_xtrace) + log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); + #endif + + #ifdef YOSYS_USE_STICKY_IDS + // Avoid Create->Delete->Create pattern + if (last_created_idx_[last_created_idx_ptr_]) + put_reference(last_created_idx_[last_created_idx_ptr_]); + last_created_idx_[last_created_idx_ptr_] = idx; + get_reference(last_created_idx_[last_created_idx_ptr_]); + last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7; + #endif + + return idx; + } + +#ifndef YOSYS_NO_IDS_REFCNT + static inline void put_reference(int idx) + { + // put_reference() may be called from destructors after the destructor of + // global_refcount_storage_ has been run. in this case we simply do nothing. + if (!destruct_guard_ok || !idx) + return; + + #ifdef YOSYS_XTRACE_GET_PUT + if (yosys_xtrace) { + log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx)); + } + #endif + + int &refcount = global_refcount_storage_[idx]; + + if (--refcount > 0) + return; + + log_assert(refcount == 0); + free_reference(idx); + } + static inline void free_reference(int idx) + { + if (yosys_xtrace) { + log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx); + log_backtrace("-X- ", yosys_xtrace-1); } - inline void operator=(const std::string &rhs) { - IdString id(rhs); - *this = id; - } - - inline const char *c_str() const { - return global_id_storage_.at(index_); - } - - inline std::string str() const { - return std::string(global_id_storage_.at(index_)); - } - - inline bool operator<(const IdString &rhs) const { - return index_ < rhs.index_; - } - - inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; } - inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; } - - // The methods below are just convenience functions for better compatibility with std::string. - - bool operator==(const std::string &rhs) const { return c_str() == rhs; } - bool operator!=(const std::string &rhs) const { return c_str() != rhs; } - - bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; } - bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; } - - char operator[](size_t i) const { - const char *p = c_str(); -#ifndef NDEBUG - for (; i != 0; i--, p++) - log_assert(*p != 0); - return *p; + global_id_index_.erase(global_id_storage_.at(idx)); + free(global_id_storage_.at(idx)); + global_id_storage_.at(idx) = nullptr; + global_free_idx_list_.push_back(idx); + } #else - return *(p + i); + static inline void put_reference(int) { } #endif + + // the actual IdString object is just is a single int + + int index_; + + inline IdString() : index_(0) { } + inline IdString(const char *str) : index_(get_reference(str)) { } + inline IdString(const IdString &str) : index_(get_reference(str.index_)) { } + inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; } + inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { } + inline ~IdString() { put_reference(index_); } + + inline void operator=(const IdString &rhs) { + put_reference(index_); + index_ = get_reference(rhs.index_); + } + + inline void operator=(const char *rhs) { + IdString id(rhs); + *this = id; + } + + inline void operator=(const std::string &rhs) { + IdString id(rhs); + *this = id; + } + + inline const char *c_str() const { + return global_id_storage_.at(index_); + } + + inline std::string str() const { + return std::string(global_id_storage_.at(index_)); + } + + inline bool operator<(const IdString &rhs) const { + return index_ < rhs.index_; + } + + inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; } + inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; } + + // The methods below are just convenience functions for better compatibility with std::string. + + bool operator==(const std::string &rhs) const { return c_str() == rhs; } + bool operator!=(const std::string &rhs) const { return c_str() != rhs; } + + bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; } + bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; } + + char operator[](size_t i) const { + const char *p = c_str(); +#ifndef NDEBUG + for (; i != 0; i--, p++) + log_assert(*p != 0); + return *p; +#else + return *(p + i); +#endif + } + + std::string substr(size_t pos = 0, size_t len = std::string::npos) const { + if (len == std::string::npos || len >= strlen(c_str() + pos)) + return std::string(c_str() + pos); + else + return std::string(c_str() + pos, len); + } + + int compare(size_t pos, size_t len, const char* s) const { + return strncmp(c_str()+pos, s, len); + } + + bool begins_with(const char* prefix) const { + size_t len = strlen(prefix); + if (size() < len) return false; + return compare(0, len, prefix) == 0; + } + + bool ends_with(const char* suffix) const { + size_t len = strlen(suffix); + if (size() < len) return false; + return compare(size()-len, len, suffix) == 0; + } + + bool contains(const char* str) const { + return strstr(c_str(), str); + } + + size_t size() const { + return strlen(c_str()); + } + + bool empty() const { + return c_str()[0] == 0; + } + + void clear() { + *this = IdString(); + } + + [[nodiscard]] Hasher hash_into(Hasher h) const { return hash_ops::hash_into(index_, h); } + + [[nodiscard]] Hasher hash_top() const { + Hasher h; + h.force((Hasher::hash_t) index_); + return h; + } + + // The following is a helper key_compare class. Instead of for example std::set + // use std::set> if the order of cells in the + // set has an influence on the algorithm. + + template struct compare_ptr_by_name { + bool operator()(const T *a, const T *b) const { + return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name); } - - std::string substr(size_t pos = 0, size_t len = std::string::npos) const { - if (len == std::string::npos || len >= strlen(c_str() + pos)) - return std::string(c_str() + pos); - else - return std::string(c_str() + pos, len); - } - - int compare(size_t pos, size_t len, const char* s) const { - return strncmp(c_str()+pos, s, len); - } - - bool begins_with(const char* prefix) const { - size_t len = strlen(prefix); - if (size() < len) return false; - return compare(0, len, prefix) == 0; - } - - bool ends_with(const char* suffix) const { - size_t len = strlen(suffix); - if (size() < len) return false; - return compare(size()-len, len, suffix) == 0; - } - - bool contains(const char* str) const { - return strstr(c_str(), str); - } - - size_t size() const { - return strlen(c_str()); - } - - bool empty() const { - return c_str()[0] == 0; - } - - void clear() { - *this = IdString(); - } - - unsigned int hash() const { - return index_; - } - - // The following is a helper key_compare class. Instead of for example std::set - // use std::set> if the order of cells in the - // set has an influence on the algorithm. - - template struct compare_ptr_by_name { - bool operator()(const T *a, const T *b) const { - return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name); - } - }; - - // often one needs to check if a given IdString is part of a list (for example a list - // of cell types). the following functions helps with that. - - template - bool in(Args... args) const { - // Credit: https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html - bool result = false; - (void) std::initializer_list{ (result = result || in(args), 0)... }; - return result; - } - - bool in(const IdString &rhs) const { return *this == rhs; } - bool in(const char *rhs) const { return *this == rhs; } - bool in(const std::string &rhs) const { return *this == rhs; } - bool in(const pool &rhs) const { return rhs.count(*this) != 0; } - - bool isPublic() const { return begins_with("\\"); } }; + // often one needs to check if a given IdString is part of a list (for example a list + // of cell types). the following functions helps with that. + template + bool in(Args... args) const { + return (... || in(args)); + } + + bool in(const IdString &rhs) const { return *this == rhs; } + bool in(const char *rhs) const { return *this == rhs; } + bool in(const std::string &rhs) const { return *this == rhs; } + inline bool in(const pool &rhs) const; + inline bool in(const pool &&rhs) const; + + bool isPublic() const { return begins_with("\\"); } +}; + +namespace hashlib { + template <> + struct hash_ops { + static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) { + return a == b; + } + [[nodiscard]] static inline Hasher hash(const RTLIL::IdString id) { + return id.hash_top(); + } + [[nodiscard]] static inline Hasher hash_into(const RTLIL::IdString id, Hasher h) { + return id.hash_into(h); + } + }; +}; + +/** + * How to not use these methods: + * 1. if(celltype.in({...})) -> if(celltype.in(...)) + * 2. pool p; ... a.in(p) -> (bool)p.count(a) + */ +[[deprecated]] +inline bool RTLIL::IdString::in(const pool &rhs) const { return rhs.count(*this) != 0; } +[[deprecated]] +inline bool RTLIL::IdString::in(const pool &&rhs) const { return rhs.count(*this) != 0; } + +namespace RTLIL { namespace ID { #define X(_id) extern IdString _id; #include "kernel/constids.inc" #undef X }; - extern dict constpad; const pool &builtin_ff_cell_types(); @@ -796,11 +824,10 @@ public: bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back()); } - inline unsigned int hash() const { - unsigned int h = mkhash_init; - - for (State b : *this) - h = mkhash(h, b); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(size()); + for (auto b : *this) + h.eat(b); return h; } }; @@ -890,7 +917,23 @@ struct RTLIL::SigBit bool operator <(const RTLIL::SigBit &other) const; bool operator ==(const RTLIL::SigBit &other) const; bool operator !=(const RTLIL::SigBit &other) const; - unsigned int hash() const; + [[nodiscard]] Hasher hash_into(Hasher h) const; + [[nodiscard]] Hasher hash_top() const; +}; + +namespace hashlib { + template <> + struct hash_ops { + static inline bool cmp(const RTLIL::SigBit &a, const RTLIL::SigBit &b) { + return a == b; + } + [[nodiscard]] static inline Hasher hash(const RTLIL::SigBit sb) { + return sb.hash_top(); + } + [[nodiscard]] static inline Hasher hash_into(const RTLIL::SigBit sb, Hasher h) { + return sb.hash_into(h); + } + }; }; struct RTLIL::SigSpecIterator @@ -931,7 +974,7 @@ struct RTLIL::SigSpec { private: int width_; - unsigned long hash_; + Hasher::hash_t hash_; std::vector chunks_; // LSB at index 0 std::vector bits_; // LSB at index 0 @@ -972,11 +1015,6 @@ public: SigSpec(const std::set &bits); explicit SigSpec(bool bit); - size_t get_hash() const { - if (!hash_) hash(); - return hash_; - } - inline const std::vector &chunks() const { pack(); return chunks_; } inline const std::vector &bits() const { inline_unpack(); return bits_; } @@ -1083,7 +1121,7 @@ public: operator std::vector() const { return bits(); } const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; } - unsigned int hash() const { if (!hash_) updhash(); return hash_; }; + [[nodiscard]] Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; } #ifndef NDEBUG void check(Module *mod = nullptr) const; @@ -1124,8 +1162,8 @@ struct RTLIL::Selection struct RTLIL::Monitor { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } Monitor() { static unsigned int hashidx_count = 123456789; @@ -1147,8 +1185,8 @@ struct define_map_t; struct RTLIL::Design { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } pool monitors; dict scratchpad; @@ -1252,8 +1290,8 @@ struct RTLIL::Design struct RTLIL::Module : public RTLIL::AttrObject { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } protected: void add(RTLIL::Wire *wire); @@ -1607,8 +1645,8 @@ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); struct RTLIL::Wire : public RTLIL::AttrObject { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } protected: // use module->addWire() and module->remove() to create or destroy wires @@ -1646,8 +1684,8 @@ inline int GetSize(RTLIL::Wire *wire) { struct RTLIL::Memory : public RTLIL::AttrObject { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } Memory(); @@ -1661,8 +1699,8 @@ struct RTLIL::Memory : public RTLIL::AttrObject struct RTLIL::Cell : public RTLIL::AttrObject { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } protected: // use module->addCell() and module->remove() to create or destroy cells @@ -1771,8 +1809,8 @@ struct RTLIL::SyncRule struct RTLIL::Process : public RTLIL::AttrObject { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } protected: // use module->addProcess() and module->remove() to create or destroy processes @@ -1816,10 +1854,25 @@ inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const { return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data)); } -inline unsigned int RTLIL::SigBit::hash() const { - if (wire) - return mkhash_add(wire->name.hash(), offset); - return data; +inline Hasher RTLIL::SigBit::hash_into(Hasher h) const { + if (wire) { + h.eat(offset); + h.eat(wire->name); + return h; + } + h.eat(data); + return h; +} + + +inline Hasher RTLIL::SigBit::hash_top() const { + Hasher h; + if (wire) { + h.force(hashlib::legacy::djb2_add(wire->name.index_, offset)); + return h; + } + h.force(data); + return h; } inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const { diff --git a/kernel/satgen.cc b/kernel/satgen.cc index ba6d664db..dd15b51f3 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -743,7 +743,6 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) if (cell->type == ID($macc)) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); - std::vector b = importDefSigSpec(cell->getPort(ID::B), timestep); std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); Macc macc; @@ -785,12 +784,6 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) } } - for (int i = 0; i < GetSize(b); i++) { - std::vector val(GetSize(y), ez->CONST_FALSE); - val.at(0) = b.at(i); - tmp = ez->vec_add(tmp, val); - } - if (model_undef) { std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h index 8c6e99fc0..3bc1a8162 100644 --- a/kernel/scopeinfo.h +++ b/kernel/scopeinfo.h @@ -169,8 +169,11 @@ public: return !(*this == other); } - int hash() const { - return mkhash(scope_name.hash(), hash_ptr_ops::hash(target)); + [[nodiscard]] Hasher hash_into(Hasher h) const + { + h.eat(scope_name); + h.eat(target); + return h; } bool valid() const { @@ -322,7 +325,7 @@ struct ModuleItem { Cell *cell() const { return type == Type::Cell ? static_cast(ptr) : nullptr; } bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; } - unsigned int hash() const { return (uintptr_t)ptr; } + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(ptr); return h; } }; static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); } @@ -334,12 +337,14 @@ template std::vector parse_hdlname(const O* object) { std::vector path; - if (!object->name.isPublic()) - return path; for (auto const &item : object->get_hdlname_attribute()) path.push_back("\\" + item); - if (path.empty()) + if (path.empty() && object->name.isPublic()) path.push_back(object->name); + if (!path.empty() && !(object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract"))) { + path.pop_back(); + path.push_back(object->name); + } return path; } @@ -348,17 +353,22 @@ std::pair, IdString> parse_scopename(const O* object) { std::vector path; IdString trailing = object->name; - if (object->name.isPublic()) { + if (object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract")) { for (auto const &item : object->get_hdlname_attribute()) path.push_back("\\" + item); if (!path.empty()) { trailing = path.back(); path.pop_back(); } + } else if (object->has_attribute(ID::hdlname)) { + for (auto const &item : object->get_hdlname_attribute()) + path.push_back("\\" + item); + if (!path.empty()) { + path.pop_back(); + } } else { for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " ")) path.push_back("\\" + item); - } return {path, trailing}; } diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 63f1b51ec..3b0d7b40d 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -29,7 +29,11 @@ struct SigPool struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(first->name); + h.eat(second); + return h; + } }; pool bits; @@ -143,7 +147,11 @@ struct SigSet struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(first->name); + h.eat(second); + return h; + } }; dict> bits; diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index 8eb7eb738..ff60415bd 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -36,7 +36,6 @@ struct TimingInfo explicit NameBit(const RTLIL::SigBit &b) : name(b.wire->name), offset(b.offset) {} bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; } bool operator!=(const NameBit& nb) const { return !operator==(nb); } - unsigned int hash() const { return mkhash_add(name.hash(), offset); } std::optional get_connection(RTLIL::Cell *cell) { if (!cell->hasPort(name)) return {}; @@ -45,6 +44,11 @@ struct TimingInfo return {}; return port[offset]; } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(name); + h.eat(offset); + return h; + } }; struct BitBit { @@ -52,7 +56,11 @@ struct TimingInfo BitBit(const NameBit &first, const NameBit &second) : first(first), second(second) {} BitBit(const SigBit &first, const SigBit &second) : first(first), second(second) {} bool operator==(const BitBit& bb) const { return bb.first == first && bb.second == second; } - unsigned int hash() const { return mkhash_add(first.hash(), second.hash()); } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(first); + h.eat(second); + return h; + } }; struct ModuleTiming diff --git a/kernel/utils.h b/kernel/utils.h index 99f327db4..07edad074 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -31,17 +31,17 @@ YOSYS_NAMESPACE_BEGIN // A map-like container, but you can save and restore the state // ------------------------------------------------ -template> +template struct stackmap { private: - std::vector> backup_state; - dict current_state; + std::vector> backup_state; + dict current_state; static T empty_tuple; public: stackmap() { } - stackmap(const dict &other) : current_state(other) { } + stackmap(const dict &other) : current_state(other) { } template void operator=(const Other &other) @@ -94,7 +94,7 @@ public: current_state.erase(k); } - const dict &stdmap() + const dict &stdmap() { return current_state; } @@ -128,7 +128,7 @@ public: // A simple class for topological sorting // ------------------------------------------------ -template , typename OPS = hash_ops> class TopoSort +template > class TopoSort { public: // We use this ordering of the edges in the adjacency matrix for diff --git a/kernel/yosys.cc b/kernel/yosys.cc index bdd7303aa..de25d20e2 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -93,6 +93,7 @@ std::set yosys_input_files, yosys_output_files; bool memhasher_active = false; uint32_t memhasher_rng = 123456; std::vector memhasher_store; +uint32_t Hasher::fudge = 0; std::string yosys_share_dirname; std::string yosys_abc_executable; @@ -140,7 +141,7 @@ void yosys_banner() log("\n"); log(" /----------------------------------------------------------------------------\\\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); - log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf |\n"); + log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf |\n"); log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n"); log(" \\----------------------------------------------------------------------------/\n"); log(" %s\n", yosys_version_str); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 29dcd58c5..b1d9ce1e6 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -134,8 +134,7 @@ YOSYS_NAMESPACE_BEGIN // Note: All headers included in hashlib.h must be included // outside of YOSYS_NAMESPACE before this or bad things will happen. #ifdef HASHLIB_H -# undef HASHLIB_H -# include "kernel/hashlib.h" +# error "You've probably included hashlib.h under two namespace paths. Bad idea." #else # include "kernel/hashlib.h" # undef HASHLIB_H @@ -153,6 +152,15 @@ using std::get; using std::min; using std::max; +using hashlib::Hasher; +using hashlib::run_hash; +using hashlib::hash_ops; +using hashlib::mkhash_xorshift; +using hashlib::dict; +using hashlib::idict; +using hashlib::pool; +using hashlib::mfp; + // A primitive shared string implementation that does not // move its .c_str() when the object is copied or moved. struct shared_str { @@ -163,22 +171,12 @@ struct shared_str { const char *c_str() const { return content->c_str(); } const string &str() const { return *content; } bool operator==(const shared_str &other) const { return *content == *other.content; } - unsigned int hash() const { return hashlib::hash_ops::hash(*content); } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(*content); + return h; + } }; -using hashlib::mkhash; -using hashlib::mkhash_init; -using hashlib::mkhash_add; -using hashlib::mkhash_xorshift; -using hashlib::hash_ops; -using hashlib::hash_cstr_ops; -using hashlib::hash_ptr_ops; -using hashlib::hash_obj_ops; -using hashlib::dict; -using hashlib::idict; -using hashlib::pool; -using hashlib::mfp; - namespace RTLIL { struct IdString; struct Const; @@ -347,10 +345,6 @@ RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std: static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })() namespace ID = RTLIL::ID; -namespace hashlib { - template<> struct hash_ops : hash_ops {}; -} - YOSYS_NAMESPACE_END diff --git a/kernel/yw.h b/kernel/yw.h index c2f5921b1..34c688a09 100644 --- a/kernel/yw.h +++ b/kernel/yw.h @@ -35,7 +35,10 @@ struct IdPath : public std::vector bool has_address() const { int tmp; return get_address(tmp); }; bool get_address(int &addr) const; - int hash() const { return hashlib::hash_ops>::hash(*this); } + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(static_cast&&>(*this)); + return h; + } }; struct WitnessHierarchyItem { diff --git a/libs/json11/json11.cpp b/libs/json11/json11.cpp index 189e63881..cdd81c6a9 100644 --- a/libs/json11/json11.cpp +++ b/libs/json11/json11.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace json11 { diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index c62e624d2..03374c610 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -855,7 +855,11 @@ class WClass: if self.hash_id != None: text += "\n\t\tunsigned int get_hash_py()" text += "\n\t\t{" - text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";" + suffix = f"->{self.hash_id}" if self.hash_id else f"->{self.hash_id}" + if self.hash_id == "": + text += f"\n\t\t\treturn run_hash(*(get_cpp_obj()));" + else: + text += f"\n\t\t\treturn run_hash(get_cpp_obj()->{self.hash_id});" text += "\n\t\t}" text += "\n\t};\n" @@ -956,7 +960,7 @@ class WClass: sources = [ Source("kernel/celltypes",[ - WClass("CellType", link_types.pointer, None, None, "type.hash()", True), + WClass("CellType", link_types.pointer, None, None, "type", True), WClass("CellTypes", link_types.pointer, None, None, None, True) ] ), @@ -970,23 +974,23 @@ sources = [ ] ), Source("kernel/rtlil",[ - WClass("IdString", link_types.ref_copy, None, "str()", "hash()"), - WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"), + WClass("IdString", link_types.ref_copy, None, "str()", ""), + WClass("Const", link_types.ref_copy, None, "as_string()", ""), WClass("AttrObject", link_types.ref_copy, None, None, None), WClass("Selection", link_types.ref_copy, None, None, None), WClass("Monitor", link_types.derive, None, None, None), WClass("CaseRule",link_types.ref_copy, None, None, None, True), WClass("SwitchRule",link_types.ref_copy, None, None, None, True), WClass("SyncRule", link_types.ref_copy, None, None, None, True), - WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"), + WClass("Process", link_types.ref_copy, None, "name.c_str()", "name"), WClass("SigChunk", link_types.ref_copy, None, None, None), - WClass("SigBit", link_types.ref_copy, None, None, "hash()"), - WClass("SigSpec", link_types.ref_copy, None, None, "hash()"), - WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()") + WClass("SigBit", link_types.ref_copy, None, None, ""), + WClass("SigSpec", link_types.ref_copy, None, None, ""), + WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "") ] ), #Source("kernel/satgen",[ diff --git a/passes/cmds/dft_tag.cc b/passes/cmds/dft_tag.cc index 8947570e7..ba7d14323 100644 --- a/passes/cmds/dft_tag.cc +++ b/passes/cmds/dft_tag.cc @@ -47,7 +47,7 @@ struct DftTagWorker { bool operator<(const tag_set &other) const { return index < other.index; } bool operator==(const tag_set &other) const { return index == other.index; } - unsigned int hash() const { return hash_ops::hash(index); } + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(index); return h; } bool empty() const { return index == 0; } }; diff --git a/passes/cmds/example_dt.cc b/passes/cmds/example_dt.cc index 1912d7da1..2870e062b 100644 --- a/passes/cmds/example_dt.cc +++ b/passes/cmds/example_dt.cc @@ -52,8 +52,10 @@ struct ExampleDtPass : public Pass return name == other.name && parameters == other.parameters; } - unsigned int hash() const { - return mkhash(name.hash(), parameters.hash()); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(name); + h.eat(parameters); + return h; } }; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 3f8d807b3..fe8b4a444 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -20,7 +20,6 @@ #include "kernel/register.h" #include "kernel/rtlil.h" #include "kernel/log.h" -#include "kernel/hashlib.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 3655f3f49..9dd68bd00 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -70,13 +70,13 @@ struct GraphNode { pool names_; dict tags_; - pool upstream_; - pool downstream_; + pool upstream_; + pool downstream_; pool &names() { return get()->names_; } dict &tags() { return get()->tags_; } - pool &upstream() { return get()->upstream_; } - pool &downstream() { return get()->downstream_; } + pool &upstream() { return get()->upstream_; } + pool &downstream() { return get()->downstream_; } uint8_t tag(int index) { return tags().at(index, 0); @@ -154,8 +154,8 @@ struct Graph { nodes.push_back(n); n->index = GetSize(nodes); - pool new_upstream; - pool new_downstream; + pool new_upstream; + pool new_downstream; for (auto g : n->upstream()) { if (n != (g = g->get())) @@ -302,7 +302,7 @@ struct Graph { } } - pool excluded; + pool excluded; for (auto grp : config.groups) { @@ -348,7 +348,7 @@ struct Graph { excluded.insert(g->get()); dict cell_nodes; - dict> sig_users; + dict> sig_users; for (auto cell : module->selected_cells()) { auto g = new GraphNode; @@ -483,8 +483,8 @@ struct Graph { { header("Any nodes with identical connections"); - typedef pair, pool> node_conn_t; - dict> nodes_by_conn; + typedef pair, pool> node_conn_t; + dict> nodes_by_conn; for (auto g : term ? term_nodes : nonterm_nodes) { auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())]; for (auto n : entry) @@ -506,8 +506,8 @@ struct Graph { header("Sibblings with identical tags"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { - dict, pool> nodes_by_tags; + auto process_conns = [&](const pool &stream) { + dict, pool> nodes_by_tags; for (auto n : stream) { if (n->terminal) continue; std::vector key; @@ -556,7 +556,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (strict)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -585,7 +585,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (non-strict)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -603,7 +603,7 @@ struct Graph { { header("Any nodes with identical fan-in or fan-out"); - dict, pool> nodes_by_conn[2]; + dict, pool> nodes_by_conn[2]; for (auto g : term ? term_nodes : nonterm_nodes) { auto &up_entry = nodes_by_conn[0][g->upstream()]; auto &down_entry = nodes_by_conn[1][g->downstream()]; @@ -629,7 +629,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (lax)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -720,9 +720,9 @@ struct VizWorker fprintf(f, "digraph \"%s\" {\n", log_id(module)); fprintf(f, " rankdir = LR;\n"); - dict>, hash_ptr_ops> extra_lines; - dict bypass_nodes; - pool bypass_candidates; + dict>> extra_lines; + dict bypass_nodes; + pool bypass_candidates; auto bypass = [&](GraphNode *g, GraphNode *n) { log_assert(g->terminal); diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc index 39604994a..411f0dd5c 100644 --- a/passes/equiv/equiv_struct.cc +++ b/passes/equiv/equiv_struct.cc @@ -46,11 +46,11 @@ struct EquivStructWorker parameters == other.parameters && port_sizes == other.port_sizes; } - unsigned int hash() const { - unsigned int h = mkhash_init; - h = mkhash(h, mkhash(type)); - h = mkhash(h, mkhash(parameters)); - h = mkhash(h, mkhash(connections)); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(type); + h.eat(parameters); + h.eat(port_sizes); + h.eat(connections); return h; } }; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 6114dd34b..143ae7b54 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -377,6 +377,13 @@ static void extract_fsm(RTLIL::Wire *wire) fsm_cell->setPort(ID::CTRL_OUT, ctrl_out); fsm_cell->parameters[ID::NAME] = RTLIL::Const(wire->name.str()); fsm_cell->attributes = wire->attributes; + if(fsm_cell->attributes.count(ID::hdlname)) { + auto hdlname = fsm_cell->get_hdlname_attribute(); + hdlname.pop_back(); + fsm_cell->set_hdlname_attribute(hdlname); + fsm_cell->set_string_attribute(ID(scopename), fsm_cell->get_string_attribute(ID::hdlname)); + fsm_cell->attributes.erase(ID::hdlname); + } fsm_data.copy_to_cell(fsm_cell); // rename original state wire @@ -385,6 +392,13 @@ static void extract_fsm(RTLIL::Wire *wire) wire->attributes.erase(ID::fsm_encoding); wire->name = stringf("$fsm$oldstate%s", wire->name.c_str()); module->wires_[wire->name] = wire; + if(wire->attributes.count(ID::hdlname)) { + auto hdlname = wire->get_hdlname_attribute(); + hdlname.pop_back(); + wire->set_hdlname_attribute(hdlname); + wire->set_string_attribute(ID(scopename), wire->get_string_attribute(ID::hdlname)); + wire->attributes.erase(ID::hdlname); + } // unconnect control outputs from old drivers diff --git a/passes/hierarchy/keep_hierarchy.cc b/passes/hierarchy/keep_hierarchy.cc index bfae9fa38..9d77b5239 100644 --- a/passes/hierarchy/keep_hierarchy.cc +++ b/passes/hierarchy/keep_hierarchy.cc @@ -17,6 +17,7 @@ * */ +#include #include "kernel/yosys.h" #include "kernel/cost.h" @@ -66,7 +67,7 @@ struct ThresholdHierarchyKeeping { } if (size > threshold) { - log("Keeping %s (estimated size above threshold: %llu > %llu).\n", log_id(module), size, threshold); + log("Keeping %s (estimated size above threshold: %" PRIu64 " > %" PRIu64 ").\n", log_id(module), size, threshold); module->set_bool_attribute(ID::keep_hierarchy); size = 0; } diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 1408c512a..6081d140e 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -117,7 +117,7 @@ struct ShareWorker static int bits_macc(const Macc &m, int width) { - int bits = GetSize(m.bit_ports); + int bits = 0; for (auto &p : m.ports) bits += bits_macc_port(p, width); return bits; @@ -1255,7 +1255,6 @@ struct ShareWorker qcsat.max_cell_count = 100; } - pool sat_cells; std::set bits_queue; std::vector cell_active, other_cell_active; @@ -1298,8 +1297,8 @@ struct ShareWorker qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); - log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", - GetSize(sat_cells), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); + log(" Size of SAT problem: %d variables, %d clauses\n", + qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); if (qcsat.ez->solve(sat_model, sat_model_values)) { log(" According to the SAT solver this pair of cells can not be shared.\n"); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 02678d676..5b3b1edc8 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -263,6 +263,19 @@ struct WreduceWorker } } + int reduced_opsize(const SigSpec &inp, bool signed_) + { + int size = GetSize(inp); + if (signed_) { + while (size >= 2 && inp[size - 1] == inp[size - 2]) + size--; + } else { + while (size >= 1 && inp[size - 1] == State::S0) + size--; + } + return size; + } + void run_cell(Cell *cell) { bool did_something = false; @@ -295,6 +308,45 @@ struct WreduceWorker bool port_a_signed = false; bool port_b_signed = false; + // For some operations if the output is no wider than either of the inputs + // we are free to choose the signedness of the operands + if (cell->type.in(ID($mul), ID($add), ID($sub)) && + max_port_a_size == GetSize(sig) && + max_port_b_size == GetSize(sig)) { + SigSpec sig_a = mi.sigmap(cell->getPort(ID::A)), sig_b = mi.sigmap(cell->getPort(ID::B)); + + // Remove top bits from sig_a and sig_b which are not visible on the output + sig_a.extend_u0(max_port_a_size); + sig_b.extend_u0(max_port_b_size); + + int signed_cost, unsigned_cost; + if (cell->type == ID($mul)) { + signed_cost = reduced_opsize(sig_a, true) * reduced_opsize(sig_b, true); + unsigned_cost = reduced_opsize(sig_a, false) * reduced_opsize(sig_b, false); + } else { + signed_cost = max(reduced_opsize(sig_a, true), reduced_opsize(sig_b, true)); + unsigned_cost = max(reduced_opsize(sig_a, false), reduced_opsize(sig_b, false)); + } + + if (!port_a_signed && !port_b_signed && signed_cost < unsigned_cost) { + log("Converting cell %s.%s (%s) from unsigned to signed.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam(ID::A_SIGNED, 1); + cell->setParam(ID::B_SIGNED, 1); + port_a_signed = true; + port_b_signed = true; + did_something = true; + } else if (port_a_signed && port_b_signed && unsigned_cost < signed_cost) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam(ID::A_SIGNED, 0); + cell->setParam(ID::B_SIGNED, 0); + port_a_signed = false; + port_b_signed = false; + did_something = true; + } + } + if (max_port_a_size >= 0 && cell->type != ID($shiftx)) run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something); diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 5b392ce51..e038a202c 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -127,11 +127,10 @@ struct proc_dlatch_db_t return signal == other.signal && match == other.match && children == other.children; } - unsigned int hash() const { - unsigned int h = mkhash_init; - mkhash(h, signal.hash()); - mkhash(h, match.hash()); - for (auto i : children) mkhash(h, i); + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(signal); + h.eat(match); + h.eat(children); return h; } }; diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index b209057fe..2f539c960 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -108,8 +108,8 @@ struct SigSnippets struct SnippetSwCache { - dict, hash_ptr_ops> full_case_bits_cache; - dict, hash_ptr_ops> cache; + dict> full_case_bits_cache; + dict> cache; const SigSnippets *snippets; int current_snippet; @@ -318,7 +318,7 @@ const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul return swcache.full_case_bits_cache.at(sw); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict &swpara, +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict &swpara, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) swcache.snippets = &sigsnip; swcache.insert(&proc->root_case); - dict swpara; + dict swpara; int cnt = 0; for (int idx : sigsnip.snippets) diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 02174be53..3075ef3f0 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -176,7 +176,7 @@ struct coverdb_t struct mutate_queue_t { - pool db; + pool db; mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { mutate_t *m = nullptr; diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index 4870e2cac..cddd8771c 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -46,9 +46,11 @@ struct IdBit { bool operator==(const IdBit &other) const { return name == other.name && bit == other.bit; }; bool operator!=(const IdBit &other) const { return name != other.name || bit != other.bit; }; - unsigned hash() const + [[nodiscard]] Hasher hash_into(Hasher h) const { - return mkhash_add(name.hash(), bit); + h.eat(name); + h.eat(bit); + return h; } IdString name; @@ -62,9 +64,11 @@ struct InvBit { bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; }; bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; }; - unsigned hash() const + [[nodiscard]] Hasher hash_into(Hasher h) const { - return mkhash(bit.hash(), inverted); + h.eat(bit); + h.eat(inverted); + return h; } IdBit bit; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index c3fa213f6..8fac93b98 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -161,7 +161,7 @@ struct SimInstance pool dirty_bits; pool dirty_cells; pool dirty_memories; - pool dirty_children; + pool dirty_children; struct ff_state_t { diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 07f9ee45d..cc37677ce 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1411,6 +1411,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin module->connect(conn); } + cell_stats.sort(); for (auto &it : cell_stats) log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); int in_wires = 0, out_wires = 0; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index e4e70004c..d89a6fbec 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -111,7 +111,7 @@ struct AlumaccWorker dict bit_users; dict sig_macc; - dict> sig_alu; + dict> sig_alu; int macc_counter, alu_counter; AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) @@ -226,7 +226,7 @@ struct AlumaccWorker { while (1) { - pool delete_nodes; + pool delete_nodes; for (auto &it : sig_macc) { @@ -278,12 +278,12 @@ struct AlumaccWorker void macc_to_alu() { - pool delete_nodes; + pool delete_nodes; for (auto &it : sig_macc) { auto n = it.second; - RTLIL::SigSpec A, B, C = n->macc.bit_ports; + RTLIL::SigSpec A, B, C; bool a_signed = false, b_signed = false; bool subtract_b = false; alunode_t *alunode; diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 44fdc7e47..52851d922 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -233,10 +233,9 @@ struct ClockgatePass : public Pass { SigBit ce_bit; bool pol_clk; bool pol_ce; - unsigned int hash() const { + [[nodiscard]] Hasher hash_into(Hasher h) const { auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce); - unsigned int h = mkhash_init; - h = mkhash(h, hash_ops::hash(t)); + h.eat(t); return h; } bool operator==(const ClkNetInfo& other) const { diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index ea5855a09..3425509b1 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -28,22 +28,22 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -IdString concat_name(RTLIL::Cell *cell, IdString object_name) +IdString concat_name(RTLIL::Cell *cell, IdString object_name, const std::string &separator = ".") { if (object_name[0] == '\\') - return stringf("%s.%s", cell->name.c_str(), object_name.c_str() + 1); + return stringf("%s%s%s", cell->name.c_str(), separator.c_str(), object_name.c_str() + 1); else { std::string object_name_str = object_name.str(); if (object_name_str.substr(0, 8) == "$flatten") object_name_str.erase(0, 8); - return stringf("$flatten%s.%s", cell->name.c_str(), object_name_str.c_str()); + return stringf("$flatten%s%s%s", cell->name.c_str(), separator.c_str(), object_name_str.c_str()); } } template -IdString map_name(RTLIL::Cell *cell, T *object) +IdString map_name(RTLIL::Cell *cell, T *object, const std::string &separator = ".") { - return cell->module->uniquify(concat_name(cell, object->name)); + return cell->module->uniquify(concat_name(cell, object->name, separator)); } void map_sigspec(const dict &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr) @@ -60,6 +60,7 @@ struct FlattenWorker bool ignore_wb = false; bool create_scopeinfo = true; bool create_scopename = false; + std::string separator = "."; template void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name) @@ -107,13 +108,13 @@ struct FlattenWorker } } - void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector &new_cells) + void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector &new_cells, const std::string &separator) { // Copy the contents of the flattened cell dict memory_map; for (auto &tpl_memory_it : tpl->memories) { - RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second), tpl_memory_it.second); + RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second, separator), tpl_memory_it.second); map_attributes(cell, new_memory, tpl_memory_it.second->name); memory_map[tpl_memory_it.first] = new_memory->name; design->select(module, new_memory); @@ -127,7 +128,7 @@ struct FlattenWorker RTLIL::Wire *new_wire = nullptr; if (tpl_wire->name[0] == '\\') { - RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name)); + RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name, separator)); if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) { hier_wire->attributes.erase(ID::hierconn); if (GetSize(hier_wire) < GetSize(tpl_wire)) { @@ -139,7 +140,7 @@ struct FlattenWorker } } if (new_wire == nullptr) { - new_wire = module->addWire(map_name(cell, tpl_wire), tpl_wire); + new_wire = module->addWire(map_name(cell, tpl_wire, separator), tpl_wire); new_wire->port_input = new_wire->port_output = false; new_wire->port_id = false; } @@ -150,7 +151,7 @@ struct FlattenWorker } for (auto &tpl_proc_it : tpl->processes) { - RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second); + RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second, separator), tpl_proc_it.second); map_attributes(cell, new_proc, tpl_proc_it.second->name); for (auto new_proc_sync : new_proc->syncs) for (auto &memwr_action : new_proc_sync->mem_write_actions) @@ -161,14 +162,14 @@ struct FlattenWorker } for (auto tpl_cell : tpl->cells()) { - RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell); + RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell, separator), tpl_cell); map_attributes(cell, new_cell, tpl_cell->name); if (new_cell->has_memid()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str())); } else if (new_cell->is_mem_cell()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); - new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str())); + new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid, separator).str())); } auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_cell->rewrite_sigspecs(rewriter); @@ -279,7 +280,7 @@ struct FlattenWorker module->rename(scopeinfo, cell_name); } - void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool &used_modules) + void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool &used_modules, const std::string &separator) { if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb)) return; @@ -308,7 +309,7 @@ struct FlattenWorker // If a design is fully selected and has a top module defined, topological sorting ensures that all cells // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening // individual modules, this isn't the case, and the newly added cells might have to be flattened further. - flatten_cell(design, module, cell, tpl, sigmap, worklist); + flatten_cell(design, module, cell, tpl, sigmap, worklist, separator); } } }; @@ -345,6 +346,9 @@ struct FlattenPass : public Pass { log(" with a public name the enclosing scope can be found via their\n"); log(" 'hdlname' attribute.\n"); log("\n"); + log(" -separator \n"); + log(" Use this separator char instead of '.' when concatenating design levels.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -353,6 +357,9 @@ struct FlattenPass : public Pass { FlattenWorker worker; + if (design->scratchpad.count("flatten.separator")) + worker.separator = design->scratchpad_get_string("flatten.separator"); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-wb") { @@ -367,6 +374,10 @@ struct FlattenPass : public Pass { worker.create_scopename = true; continue; } + if (args[argidx] == "-separator" && argidx + 1 < args.size()) { + worker.separator = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); @@ -401,7 +412,7 @@ struct FlattenPass : public Pass { log_error("Cannot flatten a design containing recursive instantiations.\n"); for (auto module : topo_modules.sorted) - worker.flatten_module(design, module, used_modules); + worker.flatten_module(design, module, used_modules, worker.separator); if (top != nullptr) for (auto module : design->modules().to_vector()) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index b5b951323..61966195c 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -250,9 +250,11 @@ struct FlowGraph { return !(*this == other); } - unsigned int hash() const + [[nodiscard]] Hasher hash_into(Hasher h) const { - return hash_ops>::hash({node, is_bottom}); + std::pair p = {node, is_bottom}; + h.eat(p); + return h; } static NodePrime top(RTLIL::SigBit node) diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 2235bdef9..911d66cfa 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -286,19 +286,23 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b), GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned"); - if (GetSize(macc.bit_ports) != 0) - log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), GetSize(macc.bit_ports)); - if (unmap) { typedef std::pair summand_t; std::vector summands; + RTLIL::SigSpec bit_ports; + for (auto &port : macc.ports) { summand_t this_summand; if (GetSize(port.in_b)) { this_summand.first = module->addWire(NEW_ID, width); module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed); + } else if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { + // Mimic old 'bit_ports' treatment in case it's relevant for performance, + // i.e. defer single-bit summands to be the last ones + bit_ports.append(port.in_a); + continue; } else if (GetSize(port.in_a) != width) { this_summand.first = module->addWire(NEW_ID, width); module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed); @@ -309,7 +313,7 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) summands.push_back(this_summand); } - for (auto &bit : macc.bit_ports) + for (auto &bit : bit_ports) summands.push_back(summand_t(bit, false)); if (GetSize(summands) == 0) @@ -346,14 +350,20 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) else { MaccmapWorker worker(module, width); + RTLIL::SigSpec bit_ports; - for (auto &port : macc.ports) - if (GetSize(port.in_b) == 0) + for (auto &port : macc.ports) { + // Mimic old 'bit_ports' treatment in case it's relevant for performance, + // i.e. defer single-bit summands to be the last ones + if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) + bit_ports.append(port.in_a); + else if (GetSize(port.in_b) == 0) worker.add(port.in_a, port.is_signed, port.do_subtract); else worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract); + } - for (auto &bit : macc.bit_ports) + for (auto bit : bit_ports) worker.add(bit, 0); module->connect(cell->getPort(ID::Y), worker.synth()); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index b9b4d2195..39b7ccd3a 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -201,18 +201,19 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce this_port.do_subtract = xorshift32(2) == 1; macc.ports.push_back(this_port); } - - wire = module->addWire(ID::B); - wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1); - wire->port_input = true; - macc.bit_ports = wire; + // Macc::to_cell sets the input ports + macc.to_cell(cell); wire = module->addWire(ID::Y); wire->width = width; wire->port_output = true; cell->setPort(ID::Y, wire); - macc.to_cell(cell); + // override the B input (macc helpers always sets an empty vector) + wire = module->addWire(ID::B); + wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1); + wire->port_input = true; + cell->setPort(ID::B, wire); } if (cell_type == ID($lut)) diff --git a/techlibs/quicklogic/ql_dsp_simd.cc b/techlibs/quicklogic/ql_dsp_simd.cc index b42823d8b..fdd2de406 100644 --- a/techlibs/quicklogic/ql_dsp_simd.cc +++ b/techlibs/quicklogic/ql_dsp_simd.cc @@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass { DspConfig(const DspConfig &ref) = default; DspConfig(DspConfig &&ref) = default; - unsigned int hash() const { return connections.hash(); } + [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(connections); return h; } bool operator==(const DspConfig &ref) const { return connections == ref.connections; } }; diff --git a/techlibs/quicklogic/qlf_k6n10f/generate_bram_types_sim.py b/techlibs/quicklogic/qlf_k6n10f/generate_bram_types_sim.py index e57c04a08..65bb25476 100644 --- a/techlibs/quicklogic/qlf_k6n10f/generate_bram_types_sim.py +++ b/techlibs/quicklogic/qlf_k6n10f/generate_bram_types_sim.py @@ -1,10 +1,9 @@ import sys -from datetime import datetime, timezone def generate(filename): with open(filename, "w") as f: f.write("// **AUTOGENERATED FILE** **DO NOT EDIT**\n") - f.write(f"// Generated by {sys.argv[0]} at {datetime.now(timezone.utc)}\n") + f.write(f"// Generated by {sys.argv[0]}\n") f.write("`timescale 1ns /10ps\n") for a_width in [1,2,4,9,18,36]: diff --git a/tests/alumacc/macc_b_port_compat.ys b/tests/alumacc/macc_b_port_compat.ys new file mode 100644 index 000000000..1ed2ad34c --- /dev/null +++ b/tests/alumacc/macc_b_port_compat.ys @@ -0,0 +1,50 @@ +# We don't set the B port on $macc cells anymore, +# test compatibility with older RTLIL files which can +# have the B port populated + +read_verilog </dev/null 2>&1 && pwd) # $ generate_target target_name test_command generate_target() { - target_name=$1 + target_name=$(basename $PWD)-$1 test_command=$2 echo "all: $target_name" echo ".PHONY: $target_name" @@ -105,7 +105,6 @@ generate_tests() { fi } -run_tests() { +generate_mk() { generate_tests "$@" > run-test.mk - exec ${MAKE:-make} -f run-test.mk } diff --git a/tests/opt/run-test.sh b/tests/opt/run-test.sh index 74589dfeb..006c731e3 100755 --- a/tests/opt/run-test.sh +++ b/tests/opt/run-test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu source ../gen-tests-makefile.sh -run_tests --yosys-scripts +generate_mk --yosys-scripts diff --git a/tests/sat/run-test.sh b/tests/sat/run-test.sh index 74589dfeb..006c731e3 100755 --- a/tests/sat/run-test.sh +++ b/tests/sat/run-test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu source ../gen-tests-makefile.sh -run_tests --yosys-scripts +generate_mk --yosys-scripts diff --git a/tests/sim/run-test.sh b/tests/sim/run-test.sh index a5120e77e..43571e057 100755 --- a/tests/sim/run-test.sh +++ b/tests/sim/run-test.sh @@ -9,4 +9,4 @@ find tb/* -name tb*.v | while read name; do iverilog -o tb/$test_name.out $name $verilog_name ./tb/$test_name.out -fst done -run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'" +generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'" diff --git a/tests/svtypes/run-test.sh b/tests/svtypes/run-test.sh index 91ceae227..8b0333f27 100755 --- a/tests/svtypes/run-test.sh +++ b/tests/svtypes/run-test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu source ../gen-tests-makefile.sh -run_tests --yosys-scripts --prove-sv +generate_mk --yosys-scripts --prove-sv diff --git a/tests/techmap/run-test.sh b/tests/techmap/run-test.sh index 16741cace..0766db999 100755 --- a/tests/techmap/run-test.sh +++ b/tests/techmap/run-test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu source ../gen-tests-makefile.sh -run_tests --yosys-scripts --tcl-scripts --bash --yosys-args "-e 'select out of bounds'" +generate_mk --yosys-scripts --tcl-scripts --bash --yosys-args "-e 'select out of bounds'" diff --git a/tests/various/json_scopeinfo.ys b/tests/various/json_scopeinfo.ys new file mode 100644 index 000000000..a5adbde10 --- /dev/null +++ b/tests/various/json_scopeinfo.ys @@ -0,0 +1,38 @@ +read_verilog <