diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index dfdcd88c0..1c2386b06 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -8,14 +8,13 @@ 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 libbz2-dev + sudo apt-get install gperf build-essential bison flex libfl-dev 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' shell: bash run: | - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm lld || true + brew bundle - name: Linux runtime environment if: runner.os == 'Linux' @@ -29,7 +28,7 @@ runs: shell: bash run: | echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH - echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH + echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 75d799fe1..95595924a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install deps - run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev + run: sudo apt-get install bison flex libfl-dev libreadline-dev tcl-dev libffi-dev - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml index d7ceb3fe3..9c15c3383 100644 --- a/.github/workflows/extra-builds.yml +++ b/.github/workflows/extra-builds.yml @@ -27,19 +27,20 @@ jobs: with: submodules: true persist-credentials: false + - run: sudo apt-get install libfl-dev - name: Build run: make vcxsrc YOSYS_VER=latest - uses: actions/upload-artifact@v4 with: name: vcxsrc path: yosys-win32-vcxsrc-latest.zip - + vs-build: name: Visual Studio build runs-on: windows-latest needs: [vs-prep, pre_job] if: needs.pre_job.outputs.should_skip != 'true' - steps: + steps: - uses: actions/download-artifact@v4 with: name: vcxsrc @@ -50,7 +51,7 @@ jobs: uses: microsoft/setup-msbuild@v2 - name: MSBuild working-directory: yosys-win32-vcxsrc-latest - run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.17763.0 + run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.26100.0 wasi-build: name: WASI build @@ -68,9 +69,20 @@ jobs: WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi + FLEX_VER=2.6.4 + FLEX=flex-${FLEX_VER} + FLEX_URL=https://github.com/westes/flex/releases/download/v${FLEX_VER}/${FLEX}.tar.gz + if ! [ -d ${FLEX} ]; then curl -L ${FLEX_URL} | tar xzf -; fi + + mkdir -p flex-build + (cd flex-build && + ../${FLEX}/configure --prefix=$(pwd)/../flex-prefix && + make && + make install) + mkdir -p build cat > build/Makefile.conf <> $GITHUB_ENV - echo "PATH=$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV + echo "PATH=$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV - if: ${{ matrix.os.family == 'windows' }} name: "[Windows] Flex/Bison" run: | @@ -100,16 +102,20 @@ jobs: CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh CIBW_ENVIRONMENT: > + OPTFLAGS=-O3 CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a' + PATH="$PWD/bison/src:$PATH" CIBW_ENVIRONMENT_MACOS: > + OPTFLAGS=-O3 CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig MACOSX_DEPLOYMENT_TARGET=11 makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang' + PATH="$PWD/bison/src:$PATH" CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/wheels/_run_cibw_linux.py b/.github/workflows/wheels/_run_cibw_linux.py index 894470a5a..fb4e0b839 100644 --- a/.github/workflows/wheels/_run_cibw_linux.py +++ b/.github/workflows/wheels/_run_cibw_linux.py @@ -20,11 +20,19 @@ import os import yaml import platform import subprocess +from pathlib import Path -__dir__ = os.path.dirname(os.path.abspath(__file__)) +__yosys_root__ = Path(__file__).absolute().parents[3] +for source in ["boost", "ffi", "bison"]: + if not (__yosys_root__ / source).is_dir(): + print( + "You need to download boost, ffi and bison in a similar manner to wheels.yml first." + ) + exit(-1) -workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml"))) +with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f: + workflow = yaml.safe_load(f) env = os.environ.copy() @@ -40,5 +48,5 @@ for key, value in cibw_step["env"].items(): continue env[key] = value -env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine() +env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine()) subprocess.check_call(["cibuildwheel"], env=env) diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh index fbb8dcad8..1aef650d7 100644 --- a/.github/workflows/wheels/cibw_before_all.sh +++ b/.github/workflows/wheels/cibw_before_all.sh @@ -1,23 +1,37 @@ -set -e -set -x +#!/bin/bash +set -e -x # Build-time dependencies ## Linux Docker Images if command -v yum &> /dev/null; then - yum install -y flex bison + yum install -y flex # manylinux's bison versions are hopelessly out of date fi if command -v apk &> /dev/null; then apk add flex bison fi +if ! printf '%s\n' '%require "3.8"' '%%' 'start: ;' | bison -o /dev/null /dev/stdin ; then + ( + set -e -x + cd bison + ./configure + make clean + make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + ) +fi + ## macOS/Windows -- installed in GitHub Action itself, not container -# Build Static FFI (platform-dependent but not Python version dependent) -cd ffi -## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries -CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx -## Without this, SHELL has a space in its path which breaks the makefile -make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) -## Forces static library to be used in all situations -sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc +# Runtime Dependencies +## Build Static FFI (platform-dependent but not Python version dependent) +( + set -e -x + cd ffi + ## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries + CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx + make clean + make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + ## Forces static library to be used in all situations + sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc +) diff --git a/.gitignore b/.gitignore index 3b77edf1f..cac630d7c 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ /kernel/python_wrappers.cc /boost /ffi +/bison /venv /*.whl /*.egg-info diff --git a/Brewfile b/Brewfile index 3696e40b0..7ed3fb906 100644 --- a/Brewfile +++ b/Brewfile @@ -6,9 +6,8 @@ brew "git" brew "graphviz" brew "pkg-config" brew "python3" -brew "tcl-tk" brew "xdot" brew "bash" brew "boost-python3" -brew "llvm" +brew "llvm@20" brew "lld" diff --git a/CHANGELOG b/CHANGELOG index 6365a24e9..468724e81 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,24 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.56 .. Yosys 0.57-dev +Yosys 0.57 .. Yosys 0.58-dev -------------------------- +Yosys 0.56 .. Yosys 0.57 +-------------------------- + * New commands and options + - Added "-initstates" option to "abstract" pass. + - Added "-set-assumes" option to "equiv_induct" + and "equiv_simple" passes. + - Added "-always" option to "raise_error" pass. + - Added "-hierarchy" option to "stat" pass. + - Added "-noflatten" option to "synth_quicklogic" pass. + + * Various + - smtbmc: Support skipping steps in cover mode. + - write_btor: support $buf. + - read_verilog: support package import. + Yosys 0.55 .. Yosys 0.56 -------------------------- * New commands and options diff --git a/Makefile b/Makefile index b27a45424..fb4fb5776 100644 --- a/Makefile +++ b/Makefile @@ -132,8 +132,8 @@ ifeq ($(ENABLE_PYOSYS),1) CXXFLAGS += -I$(BREW_PREFIX)/boost/include LINKFLAGS += -L$(BREW_PREFIX)/boost/lib -L$(BREW_PREFIX)/boost-python3/lib endif -CXXFLAGS += -I$(BREW_PREFIX)/readline/include -LINKFLAGS += -L$(BREW_PREFIX)/readline/lib +CXXFLAGS += -I$(BREW_PREFIX)/readline/include -I$(BREW_PREFIX)/flex/include +LINKFLAGS += -L$(BREW_PREFIX)/readline/lib -L$(BREW_PREFIX)/flex/lib PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.56+0 +YOSYS_VER := 0.57+0 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -182,7 +182,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 9c447ad.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 3aca860.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -745,9 +745,9 @@ $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) - $(P) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) else - $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) endif %.o: %.cc diff --git a/README.md b/README.md index e5b8d1072..6118d6079 100644 --- a/README.md +++ b/README.md @@ -80,10 +80,10 @@ recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make. TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile). Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. -For example on Ubuntu Linux 16.04 LTS the following commands will install all +For example on Ubuntu Linux 22.04 LTS the following commands will install all prerequisites for building yosys: - $ sudo apt-get install build-essential clang lld bison flex \ + $ sudo apt-get install build-essential clang lld bison flex libfl-dev \ libreadline-dev gawk tcl-dev libffi-dev git \ graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev diff --git a/abc b/abc index fa7fa163d..8827bafb7 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit fa7fa163dacdf81bdcb72b2d2713fbe9984b62bb +Subproject commit 8827bafb7f288de6749dc6e30fa452f2040949c0 diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index bfd293557..c2b831a44 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -129,7 +129,7 @@ struct BtorWorker std::replace(src.begin(), src.end(), ' ', '_'); if (srcsymbols.count(src) || module->count_id("\\" + src)) { for (int i = 1;; i++) { - string s = stringf("%s-%d", src.c_str(), i); + string s = stringf("%s-%d", src, i); if (!srcsymbols.count(s) && !module->count_id("\\" + s)) { src = s; break; @@ -192,7 +192,7 @@ struct BtorWorker void btorf_push(const string &id) { if (verbose) { - f << indent << stringf(" ; begin %s\n", id.c_str()); + f << indent << stringf(" ; begin %s\n", id); indent += " "; } } @@ -201,7 +201,7 @@ struct BtorWorker { if (verbose) { indent = indent.substr(4); - f << indent << stringf(" ; end %s\n", id.c_str()); + f << indent << stringf(" ; end %s\n", id); } } @@ -509,7 +509,7 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) + if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; @@ -521,9 +521,9 @@ struct BtorWorker int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); SigSpec sig = sigmap(cell->getPort(ID::Y)); - // the $pos cell just passes through, all other cells need an actual operation applied + // the $pos/$buf cells just pass through, all other cells need an actual operation applied int nid = nid_a; - if (cell->type != ID($pos)) + if (!cell->type.in(ID($pos), ID($buf), ID($_BUF_))) { log_assert(!btor_op.empty()); int sid = get_bv_sid(width); diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index e4254f85a..7c9feebb1 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -253,7 +253,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream const std::string extmoduleFileinfo = getFileinfo(cell); // Emit extmodule header. - f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + f << stringf(" extmodule %s: %s\n", exported_name, extmoduleFileinfo); // Emit extmodule ports. for (auto wire : mod_instance->wires()) @@ -280,7 +280,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream // Emit extmodule "defname" field. This is the name of the verilog blackbox // that is used when verilog is emitted, so we use the name of mod_instance // here. - f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); + f << stringf("%sdefname = %s\n", indent, blackbox_name); // Emit extmodule generic parameters. for (const auto &p : cell->parameters) @@ -301,7 +301,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream param_name.end() ); - f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); + f << stringf("%sparameter %s = %s\n", indent, param_name, param_value); } f << "\n"; @@ -417,7 +417,7 @@ struct FirrtlWorker else { string wire_id = make_id(chunk.wire->name); - new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset); + new_expr = stringf("bits(%s, %d, %d)", wire_id, chunk.offset + chunk.width - 1, chunk.offset); } if (expr.empty()) @@ -477,7 +477,7 @@ struct FirrtlWorker instanceOf; std::string cellFileinfo = getFileinfo(cell); - wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str())); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent, cell_name, cell_name_comment, instanceName, cellFileinfo)); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -518,7 +518,7 @@ struct FirrtlWorker // as part of the coalesced subfield assignments for this wire. register_reverse_wire_map(sourceExpr, *sinkSig); } else { - wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str())); + wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent, sinkExpr, sourceExpr, cellFileinfo)); } } } @@ -535,7 +535,7 @@ struct FirrtlWorker int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1; string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<name), moduleFileinfo.c_str()); + f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo); vector port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs; std::vector memories = Mem::get_all_memories(module); @@ -602,7 +602,7 @@ struct FirrtlWorker if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -610,7 +610,7 @@ struct FirrtlWorker // Don't use the results of logical operations (a single bit) to control padding if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); } // Assume the FIRRTL width is a single bit. @@ -622,27 +622,27 @@ struct FirrtlWorker firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { primop = "eq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); + a_expr = stringf("%s, UInt(0)", a_expr); } else if (cell->type == ID($reduce_and)) primop = "andr"; else if (cell->type == ID($reduce_or)) primop = "orr"; else if (cell->type == ID($reduce_xor)) primop = "xorr"; else if (cell->type == ID($reduce_xnor)) { primop = "not"; - a_expr = stringf("xorr(%s)", a_expr.c_str()); + a_expr = stringf("xorr(%s)", a_expr); } else if (cell->type == ID($reduce_bool)) { primop = "neq"; // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. - a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); + a_expr = stringf("%s, %cInt<%d>(0)", a_expr, a_signed ? 'S' : 'U', a_width); } - string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); + string expr = stringf("%s(%s)", primop, a_expr); if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -654,13 +654,13 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); std::string cellFileinfo = getFileinfo(cell); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; // Expand the "A" operand to the result width if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } } @@ -670,7 +670,7 @@ struct FirrtlWorker b_expr = "asSInt(" + b_expr + ")"; // Expand the "B" operand to the result width if (b_width < y_width) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -680,11 +680,11 @@ struct FirrtlWorker if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_))) { if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } if (b_width < y_width) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -856,23 +856,23 @@ struct FirrtlWorker string expr; // Deal with $xnor == ~^ (not xor) if (primop == "xnor") { - expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); + expr = stringf("not(xor(%s, %s))", a_expr, b_expr); } else { - expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + expr = stringf("%s(%s, %s)", primop, a_expr, b_expr); } // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result. // If the operation is signed, the FIRRTL width will be 1 one bit larger. if (extract_y_bits) { - expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1); + expr = stringf("bits(%s, %d, 0)", expr, y_width - 1); } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { - expr = stringf("pad(%s, %d)", expr.c_str(), y_width); + expr = stringf("pad(%s, %d)", expr, y_width); } if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -887,9 +887,9 @@ struct FirrtlWorker string s_expr = make_expr(cell->getPort(ID::S)); wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str())); - string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + string expr = stringf("mux(%s, %s, %s)", s_expr, b_expr, a_expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -911,9 +911,9 @@ struct FirrtlWorker string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; - wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); + wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent, y_id, width, clk_expr, cellFileinfo)); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; @@ -934,7 +934,7 @@ struct FirrtlWorker int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1; b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string); } - string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); + string expr = stringf("dshr(%s, %s)", a_expr, b_expr); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); @@ -973,7 +973,7 @@ struct FirrtlWorker // Verilog appears to treat the result as signed, so if the result is wider than "A", // we need to pad. if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); } wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str())); diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index dcf107de3..4c098e019 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -172,7 +172,7 @@ struct IntersynthBackend : public Backend { if (sig.size() != 0) { conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first)); - node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str()); + node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig)); } } for (auto ¶m : cell->parameters) { @@ -199,13 +199,13 @@ struct IntersynthBackend : public Backend { if (!flag_notypes) { *f << stringf("### Connection Types\n"); for (auto code : conntypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); *f << stringf("\n### Cell Types\n"); for (auto code : celltypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); } *f << stringf("\n### Netlists\n"); - *f << stringf("%s", netlists_code.c_str()); + *f << stringf("%s", netlists_code); for (auto lib : libs) delete lib; diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index e70c62a71..1ab586e43 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -218,8 +218,8 @@ struct SimplecWorker s[i] -= 'a' - 'A'; util_declarations.push_back(""); - util_declarations.push_back(stringf("#ifndef %s", s.c_str())); - util_declarations.push_back(stringf("#define %s", s.c_str())); + util_declarations.push_back(stringf("#ifndef %s", s)); + util_declarations.push_back(stringf("#define %s", s)); } string util_get_bit(const string &signame, int n, int idx) @@ -232,33 +232,33 @@ struct SimplecWorker if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); - util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name, sigtype(n))); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); - util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name, word_offset)); util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } - return stringf("%s(&%s)", util_name.c_str(), signame.c_str()); + return stringf("%s(&%s)", util_name, signame); } string util_set_bit(const string &signame, int n, int idx, const string &expr) { if (n == 1 && idx == 0) - return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str()); + return stringf(" %s.value_0_0 = %s;", signame, expr); string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); - util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name, sigtype(n))); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; @@ -266,9 +266,9 @@ struct SimplecWorker #if 0 util_declarations.push_back(stringf(" if (value)")); - util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name, word_offset)); util_declarations.push_back(stringf(" else")); - util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name, word_offset)); #else util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);", value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset)); @@ -279,7 +279,7 @@ struct SimplecWorker generated_utils.insert(util_name); } - return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); + return stringf(" %s(&%s, %s);", util_name, signame, expr); } void create_module_struct(Module *mod) @@ -339,38 +339,38 @@ struct SimplecWorker for (int i = 0; i < GetSize(topo.sorted); i++) topoidx[mod->cell(topo.sorted[i])] = i; - string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str()); + string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name)); for (int i = 0; i < GetSize(ifdef_name); i++) if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z') ifdef_name[i] -= 'a' - 'A'; struct_declarations.push_back(""); - struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str())); - struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str())); - struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str())); + struct_declarations.push_back(stringf("#ifndef %s", ifdef_name)); + struct_declarations.push_back(stringf("#define %s", ifdef_name)); + struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name))); struct_declarations.push_back("{"); struct_declarations.push_back(" // Input Ports"); for (Wire *w : mod->wires()) if (w->port_input) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Output Ports"); for (Wire *w : mod->wires()) if (!w->port_input && w->port_output) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Internal Wires"); for (Wire *w : mod->wires()) if (!w->port_input && !w->port_output) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); for (Cell *c : mod->cells()) if (design->module(c->type)) - struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c))); + struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type), cid(c->name), log_id(c))); struct_declarations.push_back(stringf("};")); struct_declarations.push_back("#endif"); @@ -407,14 +407,14 @@ struct SimplecWorker string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string expr; - if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr, b_expr); + if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr, b_expr); + if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr, b_expr); + if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr, b_expr); + if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr, b_expr); + if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr, b_expr); + if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr, b_expr); + if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr, b_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -436,8 +436,8 @@ struct SimplecWorker string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string expr; - if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); - if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); + if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr, b_expr, c_expr); + if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr, b_expr, c_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -461,8 +461,8 @@ struct SimplecWorker string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0"; string expr; - if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); - if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); + if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr, b_expr, c_expr, d_expr); + if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr, b_expr, c_expr, d_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -484,9 +484,9 @@ struct SimplecWorker string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) - string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(), - cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(), - cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str()); + string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr, + cell->type == ID($_NMUX_) ? "!" : "", b_expr, + cell->type == ID($_NMUX_) ? "!" : "", a_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -518,7 +518,7 @@ struct SimplecWorker continue; if (verbose) log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); - funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk))); + funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix, log_signal(chunk))); } for (SigBit bit : dirtysig) @@ -636,7 +636,7 @@ struct SimplecWorker reactivated_cells.clear(); funct_declarations.push_back(""); - funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str())); + funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name, cid(work->module->name))); funct_declarations.push_back("{"); for (auto &line : preamble) funct_declarations.push_back(line); diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index d17bad3fb..4e47117b3 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -1875,6 +1875,11 @@ elif covermode: smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + if step < skip_steps: + print_msg("Skipping step %d.." % (step)) + step += 1 + continue + while "1" in cover_mask: print_msg("Checking cover reachability in step %d.." % (step)) smt_push() diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index e55db95e1..573093ff7 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -51,16 +51,16 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, if (s.wire->port_id) use_inames = true; if (s.wire->width > 1) - f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset); + f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums), s.offset); else - f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str()); + f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums)); } else { if (s == RTLIL::State::S0) - f << stringf(" %s", neg.c_str()); + f << stringf(" %s", neg); else if (s == RTLIL::State::S1) - f << stringf(" %s", pos.c_str()); + f << stringf(" %s", pos); else - f << stringf(" %s%d", ncpf.c_str(), nc_counter++); + f << stringf(" %s%d", ncpf, nc_counter++); } } @@ -119,7 +119,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De } } - f << stringf(" %s\n", spice_id2str(cell->type).c_str()); + f << stringf(" %s\n", spice_id2str(cell->type)); } for (auto &conn : module->connections()) @@ -127,7 +127,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++)); print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); - f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str())); + f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf)); } } @@ -242,18 +242,18 @@ struct SpiceBackend : public Backend { ports.at(wire->port_id-1) = wire; } - *f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str()); + *f << stringf(".SUBCKT %s", spice_id2str(module->name)); for (RTLIL::Wire *wire : ports) { log_assert(wire != NULL); if (wire->width > 1) { for (int i = 0; i < wire->width; i++) - *f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i); + *f << stringf(" %s.%d", spice_id2str(wire->name), big_endian ? wire->width - 1 - i : i); } else - *f << stringf(" %s", spice_id2str(wire->name).c_str()); + *f << stringf(" %s", spice_id2str(wire->name)); } *f << stringf("\n"); print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames); - *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); + *f << stringf(".ENDS %s\n\n", spice_id2str(module->name)); } if (!top_module_name.empty()) { diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 070df1543..525ab5bcf 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2374,8 +2374,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << indent + " " << "reg " << id(initial_id) << " = 0;\n"; } - for (auto w : module->wires()) + // first dump input / output according to their order in module->ports + for (auto port : module->ports) + dump_wire(f, indent + " ", module->wire(port)); + + for (auto w : module->wires()) { + // avoid duplication + if (w->port_id) + continue; dump_wire(f, indent + " ", w); + } for (auto &mem : Mem::get_all_memories(module)) dump_memory(f, indent + " ", mem); diff --git a/docs/source/conf.py b/docs/source/conf.py index ebec6915e..5b93e2e70 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2025 YosysHQ GmbH' -yosys_ver = "0.56" +yosys_ver = "0.57" # select HTML theme html_theme = 'furo-ys' diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index fe96b2314..0ac85d199 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -88,18 +88,18 @@ Build prerequisites ^^^^^^^^^^^^^^^^^^^ A C++ compiler with C++17 support is required as well as some standard tools -such as GNU Flex, GNU Bison, Make and Python. Some additional tools: readline, -libffi, Tcl and zlib; are optional but enabled by default (see +such as GNU Flex, GNU Bison (>=3.8), Make, and Python (>=3.11). Some additional +tools: readline, libffi, Tcl and zlib; are optional but enabled by default (see :makevar:`ENABLE_*` settings in Makefile). Graphviz and Xdot are used by the `show` command to display schematics. -Installing all prerequisites for Ubuntu 20.04: +Installing all prerequisites for Ubuntu 22.04: .. code:: console - 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 \ + sudo apt-get install gperf build-essential clang lld bison flex libfl-dev \ + libreadline-dev gawk tcl-dev libffi-dev git \ + graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev Installing all prerequisites for macOS 13 (with Homebrew): @@ -137,8 +137,9 @@ For Cygwin use the following command to install all prerequisites, or select the minimum required version of Python is 3.11. This means that Cygwin is not compatible with many of the Python-based frontends. While this does not currently prevent Yosys itself from working, no guarantees are made for - continued support. It is instead recommended to use Windows Subsystem for - Linux (WSL) and follow the instructions for Ubuntu. + continued support. You may also need to specify `CXXSTD=gnu++17` to resolve + missing `strdup` function when using gcc. It is instead recommended to use + Windows Subsystem for Linux (WSL) and follow the instructions for Ubuntu. .. For MSYS2 (MINGW64): diff --git a/docs/source/yosys_internals/flow/verilog_frontend.rst b/docs/source/yosys_internals/flow/verilog_frontend.rst index b6a7ba8a0..8381641b3 100644 --- a/docs/source/yosys_internals/flow/verilog_frontend.rst +++ b/docs/source/yosys_internals/flow/verilog_frontend.rst @@ -47,9 +47,9 @@ be found in :file:`frontends/verilog/verilog_lexer.l` in the Yosys source tree. The lexer does little more than identifying all keywords and literals recognised by the Yosys Verilog frontend. -The lexer keeps track of the current location in the Verilog source code using -some global variables. These variables are used by the constructor of AST nodes -to annotate each node with the source code location it originated from. +The lexer keeps track of the current location in the Verilog source code with +a ``VerilogLexer::out_loc`` and uses it to construct parser-defined +symbol objects. Finally the lexer identifies and handles special comments such as "``// synopsys translate_off``" and "``// synopsys full_case``". (It is recommended to use @@ -178,21 +178,22 @@ properties: - | Source code location | Each ``AST::AstNode`` is automatically annotated with the current source - code location by the ``AST::AstNode`` constructor. It is stored in the - ``std::string filename`` and ``int linenum`` member variables. + code location by the ``AST::AstNode`` constructor. The ``location`` type + is a manual reimplementation of the bison-provided location type. This + type is defined at ``frontends/verilog/verilog_location.h``. -The ``AST::AstNode`` constructor can be called with up to two child nodes that -are automatically added to the list of child nodes for the new object. This +The ``AST::AstNode`` constructor can be called with up to 4 child nodes. This simplifies the creation of AST nodes for simple expressions a bit. For example the bison code for parsing multiplications: .. code:: none - :number-lines: + :number-lines: - basic_expr '*' attr basic_expr { - $$ = new AstNode(AST_MUL, $1, $4); - append_attr($$, $3); - } | + basic_expr TOK_ASTER attr basic_expr { + $$ = std::make_unique(AST_MUL, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); + } | The generated AST data structure is then passed directly to the AST frontend that performs the actual conversion to RTLIL. @@ -204,7 +205,7 @@ tree respectively. Transforming AST to RTLIL ------------------------- -The AST Frontend converts a set of modules in AST representation to modules in +The AST frontend converts a set of modules in AST representation to modules in RTLIL representation and adds them to the current design. This is done in two steps: simplification and RTLIL generation. diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 431f7b4f8..aba0ef5dd 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -38,9 +38,7 @@ using namespace AST_INTERNAL; // instantiate global variables (public API) namespace AST { - std::string current_filename; - void (*set_line_num)(int) = NULL; - int (*get_line_num)() = NULL; + bool sv_mode_but_global_and_used_for_literally_one_condition; unsigned long long astnodes = 0; unsigned long long astnode_count() { return astnodes; } } @@ -174,6 +172,7 @@ std::string AST::type2str(AstNodeType type) X(AST_MODPORT) X(AST_MODPORTMEMBER) X(AST_PACKAGE) + X(AST_IMPORT) X(AST_WIRETYPE) X(AST_TYPEDEF) X(AST_STRUCT) @@ -192,7 +191,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) if (attributes.count(id) == 0) return false; - AstNode *attr = attributes.at(id); + auto& attr = attributes.at(id); if (attr->type != AST_CONSTANT) attr->input_error("Attribute `%s' with non-constant value!\n", id.c_str()); @@ -201,7 +200,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) // create new node (AstNode constructor) // (the optional child arguments make it easier to create AST trees) -AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3, AstNode *child4) +AstNode::AstNode(AstSrcLocType loc, AstNodeType type, std::unique_ptr child1, std::unique_ptr child2, std::unique_ptr child3, std::unique_ptr child4) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); @@ -209,7 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch astnodes++; this->type = type; - filename = current_filename; + location = loc; is_input = false; is_output = false; is_reg = false; @@ -239,56 +238,73 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch in_param = false; if (child1) - children.push_back(child1); + children.push_back(std::move(child1)); if (child2) - children.push_back(child2); + children.push_back(std::move(child2)); if (child3) - children.push_back(child3); + children.push_back(std::move(child3)); if (child4) - children.push_back(child4); + children.push_back(std::move(child4)); fixup_hierarchy_flags(); } // create a (deep recursive) copy of a node -AstNode *AstNode::clone() const +std::unique_ptr AstNode::clone() const { - AstNode *that = new AstNode; - *that = *this; - for (auto &it : that->children) - it = it->clone(); - for (auto &it : that->attributes) - it.second = it.second->clone(); - - that->set_in_lvalue_flag(false); - that->set_in_param_flag(false); - that->fixup_hierarchy_flags(); // fixup to set flags on cloned children + auto that = std::make_unique(this->location, this->type); + cloneInto(*that.get()); return that; } // create a (deep recursive) copy of a node use 'other' as target root node -void AstNode::cloneInto(AstNode *other) const +void AstNode::cloneInto(AstNode &other) const { - AstNode *tmp = clone(); - tmp->in_lvalue_from_above = other->in_lvalue_from_above; - tmp->in_param_from_above = other->in_param_from_above; - other->delete_children(); - *other = *tmp; - tmp->children.clear(); - tmp->attributes.clear(); - other->fixup_hierarchy_flags(); - delete tmp; + other.type = type; + other.str = str; + other.bits = bits; + other.is_input = is_input; + other.is_output = is_output; + other.is_reg = is_reg; + other.is_logic = is_logic; + other.is_signed = is_signed; + other.is_string = is_string; + other.is_wand = is_wand; + other.is_wor = is_wor; + other.range_valid = range_valid; + other.range_swapped = range_swapped; + other.was_checked = was_checked; + other.is_unsized = is_unsized; + other.is_custom_type = is_custom_type; + other.port_id = port_id, + other.range_left = range_left, + other.range_right = range_right; + other.integer = integer; + other.realvalue = realvalue; + other.is_enum = is_enum; + other.dimensions = dimensions; + other.unpacked_dimensions = unpacked_dimensions; + other.id2ast = id2ast; + other.basic_prep = basic_prep; + other.lookahead = lookahead; + other.location = location; + other.in_lvalue = in_lvalue; + other.in_param = in_param; + // Keep in_lvalue_from_above and in_param_from_above untouched + + other.delete_children(); + for (auto& child : this->children) + other.children.push_back(child->clone()); + for (auto& [key, val] : this->attributes) + other.attributes[key] = (val->clone()); + // fixup to set flags on cloned children + other.fixup_hierarchy_flags(); } // delete all children in this node void AstNode::delete_children() { - for (auto &it : children) - delete it; children.clear(); - - for (auto &it : attributes) - delete it.second; attributes.clear(); } @@ -423,18 +439,18 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const { case AST_MODULE: fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str()); - for (auto child : children) + for (const auto& child : children) if (child->type == AST_WIRE && (child->is_input || child->is_output)) { fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str()); first = false; } fprintf(f, ");\n"); - for (auto child : children) + for (const auto& child : children) if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_DEFPARAM) child->dumpVlog(f, indent + " "); else - rem_children1.push_back(child); + rem_children1.push_back(child.get()); for (auto child : rem_children1) if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY) @@ -470,7 +486,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str()); if (is_signed) fprintf(f, " signed"); - for (auto child : children) { + for (const auto& child : children) { fprintf(f, " "); child->dumpVlog(f, ""); } @@ -486,7 +502,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "%s" "memory", indent.c_str()); if (is_signed) fprintf(f, " signed"); - for (auto child : children) { + for (const auto& child : children) { fprintf(f, " "); child->dumpVlog(f, ""); if (first) @@ -500,7 +516,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const if (0) { case AST_MEMINIT: txt = "@meminit@"; } if (0) { case AST_MEMWR: txt = "@memwr@"; } fprintf(f, "%s%s", indent.c_str(), txt.c_str()); - for (auto child : children) { + for (const auto& child : children) { fprintf(f, first ? "(" : ", "); child->dumpVlog(f, ""); first = false; @@ -517,7 +533,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const else fprintf(f, "[%d:%d]", range_left, range_right); } else { - for (auto child : children) { + for (const auto& child : children) { fprintf(f, "%c", first ? '[' : ':'); child->dumpVlog(f, ""); first = false; @@ -527,13 +543,13 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_MULTIRANGE: - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; case AST_ALWAYS: fprintf(f, "%s" "always @", indent.c_str()); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) continue; fprintf(f, first ? "(" : ", "); @@ -541,7 +557,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const first = false; } fprintf(f, first ? "*\n" : ")\n"); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } @@ -549,7 +565,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_INITIAL: fprintf(f, "%s" "initial\n", indent.c_str()); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } @@ -562,7 +578,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "posedge "); if (type == AST_NEGEDGE) fprintf(f, "negedge "); - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; @@ -574,7 +590,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const else fprintf(f, "%s", id2vl(str).c_str()); } - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; @@ -602,7 +618,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const children[0]->dumpVlog(f, indent); } else { fprintf(f, "%s" "begin\n", indent.c_str()); - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, indent + " "); fprintf(f, "%s" "end\n", indent.c_str()); } @@ -618,7 +634,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const children[0]->dumpVlog(f, ""); fprintf(f, ")\n"); for (size_t i = 1; i < children.size(); i++) { - AstNode *child = children[i]; + const auto& child = children[i]; child->dumpVlog(f, indent + " "); } fprintf(f, "%s" "endcase\n", indent.c_str()); @@ -627,7 +643,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_COND: case AST_CONDX: case AST_CONDZ: - for (auto child : children) { + for (const auto& child : children) { if (child->type == AST_BLOCK) { fprintf(f, ":\n"); child->dumpVlog(f, indent + " "); @@ -663,7 +679,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_CONCAT: fprintf(f, "{"); for (int i = GetSize(children)-1; i >= 0; i--) { - auto child = children[i]; + const auto& child = children[i]; if (!first) fprintf(f, ", "); child->dumpVlog(f, ""); @@ -818,16 +834,16 @@ bool AstNode::contains(const AstNode *other) const { if (this == other) return true; - for (auto child : children) + for (const auto& child : children) if (child->contains(other)) return true; return false; } // create an AST node for a constant (using a 32 bit int as value) -AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) +std::unique_ptr AstNode::mkconst_int(AstSrcLocType loc, uint32_t v, bool is_signed, int width) { - AstNode *node = new AstNode(AST_CONSTANT); + auto node = std::make_unique(loc, AST_CONSTANT); node->integer = v; node->is_signed = is_signed; for (int i = 0; i < width; i++) { @@ -841,9 +857,9 @@ AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) } // create an AST node for a constant (using a bit vector as value) -AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed, bool is_unsized) +std::unique_ptr AstNode::mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed, bool is_unsized) { - AstNode *node = new AstNode(AST_CONSTANT); + auto node = std::make_unique(loc, AST_CONSTANT); node->is_signed = is_signed; node->bits = v; for (size_t i = 0; i < 32; i++) { @@ -859,15 +875,15 @@ AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signe return node; } -AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed) +std::unique_ptr AstNode::mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed) { - return mkconst_bits(v, is_signed, false); + return mkconst_bits(loc, v, is_signed, false); } // create an AST node for a constant (using a string in bit vector form as value) -AstNode *AstNode::mkconst_str(const std::vector &v) +std::unique_ptr AstNode::mkconst_str(AstSrcLocType loc, const std::vector &v) { - AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); + auto node = mkconst_str(loc, RTLIL::Const(v).decode_string()); while (GetSize(node->bits) < GetSize(v)) node->bits.push_back(RTLIL::State::S0); log_assert(node->bits == v); @@ -875,14 +891,14 @@ AstNode *AstNode::mkconst_str(const std::vector &v) } // create an AST node for a constant (using a string as value) -AstNode *AstNode::mkconst_str(const std::string &str) +std::unique_ptr AstNode::mkconst_str(AstSrcLocType loc, const std::string &str) { - AstNode *node; + std::unique_ptr node; // LRM 1364-2005 5.2.3.3 The empty string literal ("") shall be considered // equivalent to the ASCII NUL ("\0") if (str.empty()) { - node = AstNode::mkconst_int(0, false, 8); + node = AstNode::mkconst_int(loc, 0, false, 8); } else { std::vector data; data.reserve(str.size() * 8); @@ -893,7 +909,7 @@ AstNode *AstNode::mkconst_str(const std::string &str) ch = ch >> 1; } } - node = AstNode::mkconst_bits(data, false); + node = AstNode::mkconst_bits(loc, data, false); } node->is_string = true; @@ -902,18 +918,19 @@ AstNode *AstNode::mkconst_str(const std::string &str) } // create a temporary register -AstNode *AstNode::mktemp_logic(const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed) +std::unique_ptr AstNode::mktemp_logic(AstSrcLocType loc, const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed) { - AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(range_left, true), mkconst_int(range_right, true))); - wire->str = stringf("%s%s:%d$%d", name.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + auto wire_owned = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, mkconst_int(loc, range_left, true), mkconst_int(loc, range_right, true))); + auto* wire = wire_owned.get(); + wire->str = stringf("%s%s:%d$%d", name.c_str(), RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line, autoidx++); if (nosync) - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(loc, 1, false)); wire->is_signed = is_signed; wire->is_logic = true; - mod->children.push_back(wire); + mod->children.push_back(std::move(wire_owned)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(loc, AST_IDENTIFIER); ident->str = wire->str; ident->id2ast = wire; @@ -967,10 +984,9 @@ RTLIL::Const AstNode::asParaConst() const { if (type == AST_REALVALUE) { - AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue)); + auto strnode = AstNode::mkconst_str(location, stringf("%f", realvalue)); RTLIL::Const val = strnode->asAttrConst(); val.flags |= RTLIL::CONST_FLAG_REAL; - delete strnode; return val; } @@ -1070,7 +1086,7 @@ RTLIL::Const AstNode::realAsConst(int width) std::string AstNode::loc_string() const { - return stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + return stringf("%s:%d.%d-%d.%d", location.begin.filename->c_str(), location.begin.line, location.begin.column, location.end.line, location.end.column); } void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) @@ -1078,7 +1094,7 @@ void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) obj->attributes[ID::src] = ast->loc_string(); } -static bool param_has_no_default(const AstNode *param) { +static bool param_has_no_default(const AstNode* param) { const auto &children = param->children; log_assert(param->type == AST_PARAMETER); log_assert(children.size() <= 2); @@ -1086,7 +1102,7 @@ static bool param_has_no_default(const AstNode *param) { (children.size() == 1 && children[0]->type == AST_RANGE); } -static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) +static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, std::unique_ptr original_ast = NULL, bool quiet = false) { log_assert(current_scope.empty()); log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); @@ -1100,15 +1116,15 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d AstModule *module = new AstModule; current_module = module; - module->ast = NULL; + module->ast = nullptr; module->name = ast->str; set_src_attr(module, ast); module->set_bool_attribute(ID::cells_not_processed); current_ast_mod = ast; - AstNode *ast_before_simplify; + std::unique_ptr ast_before_simplify; if (original_ast != NULL) - ast_before_simplify = original_ast; + ast_before_simplify = std::move(original_ast); else ast_before_simplify = ast->clone(); @@ -1125,15 +1141,15 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d if (!defer) { - for (const AstNode *node : ast->children) - if (node->type == AST_PARAMETER && param_has_no_default(node)) + for (auto& node : ast->children) + if (node->type == AST_PARAMETER && param_has_no_default(node.get())) node->input_error("Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str()); bool blackbox_module = flag_lib; if (!blackbox_module && !flag_noblackbox) { blackbox_module = true; - for (auto child : ast->children) { + for (const auto& child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) continue; if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) @@ -1163,36 +1179,33 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (flag_nowb && ast->attributes.count(ID::whitebox)) { - delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (ast->attributes.count(ID::lib_whitebox)) { - if (!flag_lib || flag_nowb) { - delete ast->attributes.at(ID::lib_whitebox); - ast->attributes.erase(ID::lib_whitebox); - } else { - if (ast->attributes.count(ID::whitebox)) { - delete ast->attributes.at(ID::whitebox); - ast->attributes.erase(ID::whitebox); - } - AstNode *n = ast->attributes.at(ID::lib_whitebox); - ast->set_attribute(ID::whitebox, n); - ast->attributes.erase(ID::lib_whitebox); + if (flag_lib && !flag_nowb) { + ast->attributes[ID::whitebox] = std::move( + ast->attributes[ID::lib_whitebox] + ); } + ast->attributes.erase(ID::lib_whitebox); } + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (!blackbox_module && ast->attributes.count(ID::blackbox)) { - AstNode *n = ast->attributes.at(ID::blackbox); + auto& n = ast->attributes.at(ID::blackbox); if (n->type != AST_CONSTANT) ast->input_error("Got blackbox attribute with non-constant value!\n"); blackbox_module = n->asBool(); } if (blackbox_module && ast->attributes.count(ID::whitebox)) { - AstNode *n = ast->attributes.at(ID::whitebox); + auto& n = ast->attributes.at(ID::whitebox); if (n->type != AST_CONSTANT) ast->input_error("Got whitebox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); @@ -1200,62 +1213,59 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d if (ast->attributes.count(ID::noblackbox)) { if (blackbox_module) { - AstNode *n = ast->attributes.at(ID::noblackbox); + auto& n = ast->attributes.at(ID::noblackbox); if (n->type != AST_CONSTANT) ast->input_error("Got noblackbox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); } - delete ast->attributes.at(ID::noblackbox); ast->attributes.erase(ID::noblackbox); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (blackbox_module) { if (ast->attributes.count(ID::whitebox)) { - delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } if (ast->attributes.count(ID::lib_whitebox)) { - delete ast->attributes.at(ID::lib_whitebox); ast->attributes.erase(ID::lib_whitebox); } - std::vector new_children; - for (auto child : ast->children) { + std::vector> new_children; + for (auto& child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) { - new_children.push_back(child); + new_children.push_back(std::move(child)); } else if (child->type == AST_PARAMETER) { - new_children.push_back(child); + new_children.push_back(std::move(child)); } else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) { - new_children.push_back(child); - } else { - delete child; + new_children.push_back(std::move(child)); } } ast->children.swap(new_children); if (ast->attributes.count(ID::blackbox) == 0) { - ast->set_attribute(ID::blackbox, AstNode::mkconst_int(1, false)); + ast->set_attribute(ID::blackbox, AstNode::mkconst_int(ast->location, 1, false)); } } ignoreThisSignalsInInitial = RTLIL::SigSpec(); for (auto &attr : ast->attributes) { + log_assert((bool)attr.second.get()); if (attr.second->type != AST_CONSTANT) ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; + const auto& node = ast->children[i]; if (node->type == AST_WIRE || node->type == AST_MEMORY) node->genRTLIL(); } for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; + const auto& node = ast->children[i]; if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) node->genRTLIL(); } @@ -1263,7 +1273,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d ignoreThisSignalsInInitial.sort_and_unify(); for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; + const auto& node = ast->children[i]; if (node->type == AST_INITIAL) node->genRTLIL(); } @@ -1277,14 +1287,14 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d continue; module->attributes[attr.first] = attr.second->asAttrConst(); } - for (const AstNode *node : ast->children) + for (const auto& node : ast->children) if (node->type == AST_PARAMETER) current_module->avail_parameters(node->str); } if (ast->type == AST_INTERFACE) module->set_bool_attribute(ID::is_interface); - module->ast = ast_before_simplify; + module->ast = std::move(ast_before_simplify); module->nolatches = flag_nolatches; module->nomeminit = flag_nomeminit; module->nomem2reg = flag_nomem2reg; @@ -1311,8 +1321,8 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d RTLIL::Module * AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, RTLIL::Module *old_module, - AstNode *new_ast, - AstNode *original_ast) + AST::AstNode *new_ast, + std::unique_ptr original_ast) { // The old module will be deleted. Rename and mark for deletion, using // a static counter to make sure we get a unique name. @@ -1335,7 +1345,7 @@ AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, } // Generate RTLIL from AST for the new module and add to the design: - RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast); + RTLIL::Module* new_module = process_module(design, new_ast, false, std::move(original_ast)); if (is_top) new_module->set_bool_attribute(ID::top); @@ -1347,17 +1357,17 @@ AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, static void rename_in_package_stmts(AstNode *pkg) { std::unordered_set idents; - for (AstNode *item : pkg->children) + for (auto& item : pkg->children) idents.insert(item->str); - std::function rename = - [&rename, &idents, pkg](AstNode *node) { - for (AstNode *child : node->children) { + std::function&)> rename = + [&rename, &idents, pkg](std::unique_ptr& node) { + for (auto& child : node->children) { if (idents.count(child->str)) child->str = pkg->str + "::" + child->str.substr(1); rename(child); } }; - for (AstNode *item : pkg->children) + for (auto& item : pkg->children) if (item->type == AST_FUNCTION || item->type == AST_TASK) rename(item); } @@ -1390,17 +1400,17 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump ast->fixup_hierarchy_flags(true); log_assert(current_ast->type == AST_DESIGN); - for (AstNode *child : current_ast->children) + for (const auto& child : current_ast->children) { if (child->type == AST_MODULE || child->type == AST_INTERFACE) { - for (auto n : design->verilog_globals) + for (auto& n : design->verilog_globals) child->children.push_back(n->clone()); // append nodes from previous packages using package-qualified names - for (auto &n : design->verilog_packages) { + for (auto& n : design->verilog_packages) { for (auto &o : n->children) { - AstNode *cloned_node = o->clone(); + auto cloned_node = o->clone(); // log("cloned node %s\n", type2str(cloned_node->type).c_str()); if (cloned_node->type == AST_ENUM) { for (auto &e : cloned_node->children) { @@ -1410,7 +1420,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump } else { cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); } - child->children.push_back(cloned_node); + child->children.push_back(std::move(cloned_node)); } } @@ -1419,8 +1429,8 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump bool defer_local = defer; if (!defer_local) - for (const AstNode *node : child->children) - if (node->type == AST_PARAMETER && param_has_no_default(node)) + for (const auto& node : child->children) + if (node->type == AST_PARAMETER && param_has_no_default(node.get())) { log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str()); defer_local = true; @@ -1434,7 +1444,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump if (design->has(child->str)) { RTLIL::Module *existing_mod = design->module(child->str); if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { - log_file_error(child->filename, child->location.first_line, "Re-definition of module `%s'!\n", child->str.c_str()); + log_file_error(*child->location.begin.filename, child->location.begin.line, "Re-definition of module `%s'!\n", child->str.c_str()); } else if (nooverwrite) { log("Ignoring re-definition of module `%s' at %s.\n", child->str.c_str(), child->loc_string().c_str()); @@ -1447,13 +1457,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump } } - process_module(design, child, defer_local); + process_module(design, child.get(), defer_local); current_ast_mod = nullptr; } else if (child->type == AST_PACKAGE) { // process enum/other declarations child->simplify(true, 1, -1, false); - rename_in_package_stmts(child); + rename_in_package_stmts(child.get()); design->verilog_packages.push_back(child->clone()); current_scope.clear(); } @@ -1470,16 +1480,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump current_scope.clear(); } } -} -// AstModule destructor -AstModule::~AstModule() -{ - if (ast != NULL) - delete ast; } - // An interface port with modport is specified like this: // . // This function splits the interface_name from the modport_name, and fails if it is not a valid combination @@ -1516,7 +1519,7 @@ AstNode * AST::find_modport(AstNode *intf, std::string name) for (auto &ch : intf->children) if (ch->type == AST_MODPORT) if (ch->str == name) // Modport found - return ch; + return ch.get(); return NULL; } @@ -1524,7 +1527,8 @@ AstNode * AST::find_modport(AstNode *intf, std::string name) void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport) { for (auto w : intfmodule->wires()){ - AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true))); + auto loc = module_ast->location; + auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true))); std::string origname = log_id(w->name); std::string newname = intfname + "." + origname; wire->str = newname; @@ -1543,16 +1547,13 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule } } if (found_in_modport) { - module_ast->children.push_back(wire); - } - else { // If not found in modport, do not create port - delete wire; + module_ast->children.push_back(std::move(wire)); } } else { // If no modport, set inout wire->is_input = true; wire->is_output = true; - module_ast->children.push_back(wire); + module_ast->children.push_back(std::move(wire)); } } } @@ -1570,7 +1571,7 @@ bool AstModule::reprocess_if_necessary(RTLIL::Design *design) log("Reprocessing module %s because instantiated module %s has become available.\n", log_id(name), log_id(modname)); loadconfig(); - process_and_replace_module(design, this, ast, NULL); + process_and_replace_module(design, this, ast.get(), NULL); return true; } } @@ -1583,32 +1584,33 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictclone(); + auto new_ast = ast->clone(); + auto loc = ast->location; for (auto &intf : local_interfaces) { std::string intfname = intf.first.str(); RTLIL::Module *intfmodule = intf.second; for (auto w : intfmodule->wires()){ - AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true))); + auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true))); std::string newname = log_id(w->name); newname = intfname + "." + newname; wire->str = newname; - new_ast->children.push_back(wire); + new_ast->children.push_back(std::move(wire)); } } - AstNode *ast_before_replacing_interface_ports = new_ast->clone(); + auto ast_before_replacing_interface_ports = new_ast->clone(); // Explode all interface ports. Note this will only have an effect on 'top // level' modules. Other sub-modules will have their interface ports // exploded via the derive(..) function for (size_t i =0; ichildren.size(); i++) { - AstNode *ch2 = new_ast->children[i]; + const auto& ch2 = new_ast->children[i]; if (ch2->type == AST_INTERFACEPORT) { // Is an interface port std::string name_port = ch2->str; // Name of the interface port if (ch2->children.size() > 0) { for(size_t j=0; jchildren.size();j++) { - AstNode *ch = ch2->children[j]; + const auto& ch = ch2->children[j]; if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface std::pair res = split_modport_from_type(ch->str); std::string interface_type = res.first; @@ -1616,11 +1618,11 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictmodule(interface_type) != nullptr) { // Add a cell to the module corresponding to the interface port such that // it can further propagated down if needed: - AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE); + auto celltype_for_intf = std::make_unique(loc, AST_CELLTYPE); celltype_for_intf->str = interface_type; - AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); + auto cell_for_intf = std::make_unique(loc, AST_CELL, std::move(celltype_for_intf)); cell_for_intf->str = name_port + "_inst_from_top_dummy"; - new_ast->children.push_back(cell_for_intf); + new_ast->children.push_back(std::move(cell_for_intf)); // Get all members of this non-overridden dummy interface instance: RTLIL::Module *intfmodule = design->module(interface_type); // All interfaces should at this point in time (assuming @@ -1628,9 +1630,9 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictmodules_ AstModule *ast_module_of_interface = (AstModule*)intfmodule; std::string interface_modport_compare_str = "\\" + interface_modport; - AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport + AstNode *modport = find_modport(ast_module_of_interface->ast.get(), interface_modport_compare_str); // modport == NULL if no modport // Iterate over all wires in the interface and add them to the module: - explode_interface_port(new_ast, intfmodule, name_port, modport); + explode_interface_port(new_ast.get(), intfmodule, name_port, modport); } break; } @@ -1642,9 +1644,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictset_bool_attribute(ID::interfaces_replaced_in_module); @@ -1654,7 +1654,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool /*mayfail*/) { - AstNode *new_ast = NULL; + std::unique_ptr new_ast = NULL; std::string modname = derive_common(design, parameters, &new_ast); // Since interfaces themselves may be instantiated with different parameters, @@ -1690,14 +1690,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict 0) { std::string interface_modport = modports.at(intfname).str(); AstModule *ast_module_of_interface = (AstModule*)intfmodule; - AstNode *ast_node_of_interface = ast_module_of_interface->ast; + AstNode *ast_node_of_interface = ast_module_of_interface->ast.get(); modport = find_modport(ast_node_of_interface, interface_modport); } // Iterate over all wires in the interface and add them to the module: - explode_interface_port(new_ast, intfmodule, intfname, modport); + explode_interface_port(new_ast.get(), intfmodule, intfname, modport); } - process_module(design, new_ast, false); + process_module(design, new_ast.get(), false); design->module(modname)->check(); RTLIL::Module* mod = design->module(modname); @@ -1734,7 +1734,6 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict new_ast = NULL; std::string modname = derive_common(design, parameters, &new_ast, quiet); if (!design->has(modname) && new_ast) { new_ast->str = modname; - process_module(design, new_ast, false, NULL, quiet); + process_module(design, new_ast.get(), false, NULL, quiet); design->module(modname)->check(); } else if (!quiet) { log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); } - delete new_ast; return modname; } @@ -1784,7 +1782,7 @@ std::string AST::derived_module_name(std::string stripped_name, const std::vecto } // create a new parametric module (when needed) and return the name of the generated module -std::string AstModule::derive_common(RTLIL::Design *design, const dict ¶meters, AstNode **new_ast_out, bool quiet) +std::string AstModule::derive_common(RTLIL::Design *design, const dict ¶meters, std::unique_ptr* new_ast_out, bool quiet) { std::string stripped_name = name.str(); (*new_ast_out) = nullptr; @@ -1794,7 +1792,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict> named_parameters; - for (const auto child : ast->children) { + for (const auto& child : ast->children) { if (child->type != AST_PARAMETER) continue; para_counter++; @@ -1828,12 +1826,13 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict rewritten; rewritten.reserve(GetSize(parameters)); - AstNode *new_ast = ast->clone(); + auto new_ast = ast->clone(); + auto loc = ast->location; if (!new_ast->attributes.count(ID::hdlname)) - new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1))); + new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(loc, stripped_name.substr(1))); para_counter = 0; - for (auto child : new_ast->children) { + for (auto& child : new_ast->children) { if (child->type != AST_PARAMETER) continue; para_counter++; @@ -1851,16 +1850,15 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictchildren.insert(child->children.begin(), nullptr); - delete child->children.at(0); if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) { - child->children[0] = new AstNode(AST_REALVALUE); + child->children[0] = std::make_unique(loc, AST_REALVALUE); child->children[0]->realvalue = std::stod(it->second.decode_string()); } else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0) - child->children[0] = AstNode::mkconst_str(it->second.decode_string()); + child->children[0] = AstNode::mkconst_str(loc, it->second.decode_string()); else - child->children[0] = AstNode::mkconst_bits(it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); + child->children[0] = AstNode::mkconst_bits(loc, it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); rewritten.insert(it->first); } @@ -1868,17 +1866,17 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict(loc, AST_DEFPARAM, std::make_unique(loc, AST_IDENTIFIER)); defparam->children[0]->str = param.first.str(); if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0) - defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string())); + defparam->children.push_back(AstNode::mkconst_str(loc, param.second.decode_string())); else - defparam->children.push_back(AstNode::mkconst_bits(param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); - new_ast->children.push_back(defparam); + defparam->children.push_back(AstNode::mkconst_bits(loc, param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); + new_ast->children.push_back(std::move(defparam)); } new_ast->fixup_hierarchy_flags(true); - (*new_ast_out) = new_ast; + new_ast_out->reset(new_ast.release()); return modname; } @@ -1928,7 +1926,7 @@ void AstNode::input_error(const char *format, ...) const { va_list ap; va_start(ap, format); - logv_file_error(filename, location.first_line, format, ap); + logv_file_error(*location.begin.filename, location.begin.line, format, ap); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 2c2d408ce..fdf4d1ec9 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -28,6 +28,7 @@ #include "kernel/rtlil.h" #include "kernel/fmt.h" +#include "frontends/verilog/verilog_location.h" #include #include @@ -153,6 +154,7 @@ namespace AST AST_MODPORT, AST_MODPORTMEMBER, AST_PACKAGE, + AST_IMPORT, AST_WIRETYPE, AST_TYPEDEF, @@ -162,12 +164,7 @@ namespace AST AST_BIND }; - struct AstSrcLocType { - unsigned int first_line, last_line; - unsigned int first_column, last_column; - AstSrcLocType() : first_line(0), last_line(0), first_column(0), last_column(0) {} - AstSrcLocType(int _first_line, int _first_column, int _last_line, int _last_column) : first_line(_first_line), last_line(_last_line), first_column(_first_column), last_column(_last_column) {} - }; + using AstSrcLocType = Location; // convert an node type to a string (e.g. for debug output) std::string type2str(AstNodeType type); @@ -183,10 +180,10 @@ namespace AST AstNodeType type; // the list of child nodes for this node - std::vector children; + std::vector> children; // the list of attributes assigned to this node - std::map attributes; + std::map> attributes; bool get_bool_attribute(RTLIL::IdString id); // node content - most of it is unused in most node types @@ -212,7 +209,7 @@ namespace AST int unpacked_dimensions; // this is set by simplify and used during RTLIL generation - AstNode *id2ast; + AstNode* id2ast; // this is used by simplify to detect if basic analysis has been performed already on the node bool basic_prep; @@ -223,7 +220,6 @@ namespace AST // this is the original sourcecode location that resulted in this AST node // it is automatically set by the constructor using AST::current_filename and // the AST::get_line_num() callback function. - std::string filename; AstSrcLocType location; // are we embedded in an lvalue, param? @@ -234,9 +230,9 @@ namespace AST bool in_param_from_above; // creating and deleting nodes - AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr); - AstNode *clone() const; - void cloneInto(AstNode *other) const; + AstNode(AstSrcLocType loc, AstNodeType type = AST_NONE, std::unique_ptr child1 = nullptr, std::unique_ptr child2 = nullptr, std::unique_ptr child3 = nullptr, std::unique_ptr child4 = nullptr); + std::unique_ptr clone() const; + void cloneInto(AstNode &other) const; void delete_children(); ~AstNode(); @@ -265,14 +261,14 @@ namespace AST // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() bool simplify(bool const_fold, int stage, int width_hint, bool sign_hint); void replace_result_wire_name_in_function(const std::string &from, const std::string &to); - AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); + std::unique_ptr readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); void expand_genblock(const std::string &prefix); void label_genblks(std::set& existing, int &counter); void mem2reg_as_needed_pass1(dict> &mem2reg_places, dict &mem2reg_flags, dict &proc_flags, uint32_t &status_flags); - bool mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); + bool mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode* async_block); bool mem2reg_check(pool &mem2reg_set); - void mem2reg_remove(pool &mem2reg_set, vector &delnodes); + void mem2reg_remove(pool &mem2reg_set); void meminfo(int &mem_width, int &mem_size, int &addr_bits); bool detect_latch(const std::string &var); const RTLIL::Module* lookup_cell_module(); @@ -288,7 +284,7 @@ namespace AST }; bool has_const_only_constructs(); bool replace_variables(std::map &variables, AstNode *fcall, bool must_succeed); - AstNode *eval_const_function(AstNode *fcall, bool must_succeed); + std::unique_ptr eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); // helper for parsing format strings @@ -305,29 +301,30 @@ namespace AST std::vector genBindings() const; // used by genRTLIL() for detecting expression width and sign - void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); - void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL); + void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = nullptr); + void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = nullptr); // create RTLIL code for this AST node // for expressions the resulting signal vector is returned // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); - RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict *new_subst_ptr = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict *new_subst_ptr = nullptr); // compare AST nodes bool operator==(const AstNode &other) const; bool operator!=(const AstNode &other) const; bool contains(const AstNode *other) const; + AstNode operator=(AstNode) = delete; // helper functions for creating AST nodes for constants - static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32); - static AstNode *mkconst_bits(const std::vector &v, bool is_signed, bool is_unsized); - static AstNode *mkconst_bits(const std::vector &v, bool is_signed); - static AstNode *mkconst_str(const std::vector &v); - static AstNode *mkconst_str(const std::string &str); + static std::unique_ptr mkconst_int(AstSrcLocType loc, uint32_t v, bool is_signed, int width = 32); + static std::unique_ptr mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed, bool is_unsized); + static std::unique_ptr mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed); + static std::unique_ptr mkconst_str(AstSrcLocType loc, const std::vector &v); + static std::unique_ptr mkconst_str(AstSrcLocType loc, const std::string &str); // helper function to create an AST node for a temporary register - AstNode *mktemp_logic(const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed); + std::unique_ptr mktemp_logic(AstSrcLocType loc, const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed); // helper function for creating sign-extended const objects RTLIL::Const bitsAsConst(int width, bool is_signed); @@ -356,12 +353,12 @@ namespace AST // helper to clone the node with some of its subexpressions replaced with zero (this is used // to evaluate widths of dynamic ranges) - AstNode *clone_at_zero(); + std::unique_ptr clone_at_zero(); - void set_attribute(RTLIL::IdString key, AstNode *node) + void set_attribute(RTLIL::IdString key, std::unique_ptr node) { - attributes[key] = node; node->set_in_param_flag(true); + attributes[key] = std::move(node); } // helper to set in_lvalue/in_param flags from the hierarchy context (the actual flag @@ -377,7 +374,7 @@ namespace AST void fixup_hierarchy_flags(bool force_descend = false); // helpers for indexing - AstNode *make_index_range(AstNode *node, bool unpacked_range = false); + std::unique_ptr make_index_range(AstNode *node, bool unpacked_range = false); AstNode *get_struct_member() const; // helper to print errors from simplify/genrtlil code @@ -391,12 +388,11 @@ namespace AST // parametric modules are supported directly by the AST library // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { - AstNode *ast; + std::unique_ptr ast; bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire; - ~AstModule() override; RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, bool mayfail) override; RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool mayfail) override; - std::string derive_common(RTLIL::Design *design, const dict ¶meters, AstNode **new_ast_out, bool quiet = false); + std::string derive_common(RTLIL::Design *design, const dict ¶meters, std::unique_ptr* new_ast_out, bool quiet = false); void expand_interfaces(RTLIL::Design *design, const dict &local_interfaces) override; bool reprocess_if_necessary(RTLIL::Design *design) override; RTLIL::Module *clone() const override; @@ -406,9 +402,9 @@ namespace AST // this must be set by the language frontend before parsing the sources // the AstNode constructor then uses current_filename and get_line_num() // to initialize the filename and linenum properties of new nodes - extern std::string current_filename; - extern void (*set_line_num)(int); - extern int (*get_line_num)(); + // extern std::string current_filename; + // also set by the language frontend to control some AST processing + extern bool sv_mode_but_global_and_used_for_literally_one_condition; // for stats unsigned long long astnode_count(); @@ -418,7 +414,7 @@ namespace AST void use_internal_line_num(); // call a DPI function - AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args); + std::unique_ptr dpi_call(AstSrcLocType loc, const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector> &args); // Helper functions related to handling SystemVerilog interfaces std::pair split_modport_from_type(std::string name_type); @@ -464,7 +460,7 @@ namespace AST_INTERNAL process_and_replace_module(RTLIL::Design *design, RTLIL::Module *old_module, AST::AstNode *new_ast, - AST::AstNode *original_ast = nullptr); + std::unique_ptr original_ast = nullptr); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index d6fcc26bd..d76318739 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -64,9 +64,9 @@ static ffi_fptr resolve_fn (std::string symbol_name) log_error("unable to resolve '%s'.\n", symbol_name.c_str()); } -AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args) +std::unique_ptr AST::dpi_call(AstSrcLocType loc, const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector> &args) { - AST::AstNode *newNode = nullptr; + std::unique_ptr newNode = nullptr; union value { double f64; float f32; int32_t i32; void *ptr; }; std::vector value_store(args.size() + 1); std::vector types(args.size() + 1); @@ -125,11 +125,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data()); if (rtype == "real") { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(loc, AST_REALVALUE); newNode->realvalue = value_store[args.size()].f64; log(" return realvalue: %g\n", newNode->asReal(true)); } else if (rtype == "shortreal") { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(loc, AST_REALVALUE); newNode->realvalue = value_store[args.size()].f32; log(" return realvalue: %g\n", newNode->asReal(true)); } else if (rtype == "chandle") { @@ -137,10 +137,10 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, std::vector bits(64); for (int i = 0; i < 64; i++) bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; - newNode = AstNode::mkconst_bits(bits, false); + newNode = AstNode::mkconst_bits(loc, bits, false); log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false)); } else { - newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); + newNode = AstNode::mkconst_int(loc, value_store[args.size()].i32, false); log(" return integer: %lld\n", (long long)newNode->asInt(true)); } @@ -153,7 +153,7 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN -AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector&, const std::vector&) +std::unique_ptr AST::dpi_call(AstSrcLocType, const std::string&, const std::string &fname, const std::vector&, const std::vector>&) { log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str()); } diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 26ed0e3e4..b53fa7a44 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,7 +45,7 @@ using namespace AST_INTERNAL; // helper function for creating RTLIL code for unary operations static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(*that->location.begin.filename).c_str(), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -77,7 +77,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s return; } - IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); + IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(*that->location.begin.filename).c_str(), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); set_src_attr(cell, that); @@ -85,7 +85,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s set_src_attr(wire, that); wire->is_signed = that->is_signed; - if (that != NULL) + if (that != nullptr) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); @@ -104,7 +104,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(*that->location.begin.filename).c_str(), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -138,7 +138,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const log_assert(cond.size() == 1); std::stringstream sstr; - sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++); + sstr << "$ternary$" << RTLIL::encode_filename(*that->location.begin.filename) << ":" << that->location.begin.line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); set_src_attr(cell, that); @@ -195,22 +195,22 @@ struct AST_INTERNAL::LookaheadRewriter if (node->lookahead) { log_assert(node->type == AST_IDENTIFIER); if (!lookaheadids.count(node->str)) { - AstNode *wire = new AstNode(AST_WIRE); - for (auto c : node->id2ast->children) + auto wire = std::make_unique(node->location, AST_WIRE); + for (auto& c : node->id2ast->children) wire->children.push_back(c->clone()); wire->fixup_hierarchy_flags(); wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++); - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(node->location, 1, false)); wire->is_logic = true; while (wire->simplify(true, 1, -1, false)) { } - current_ast_mod->children.push_back(wire); - lookaheadids[node->str] = make_pair(node->id2ast, wire); + lookaheadids[node->str] = make_pair(node->id2ast, wire.get()); wire->genRTLIL(); + current_ast_mod->children.push_back(std::move(wire)); } } - for (auto child : node->children) - collect_lookaheadids(child); + for (auto& child : node->children) + collect_lookaheadids(child.get()); } bool has_lookaheadids(AstNode *node) @@ -218,8 +218,8 @@ struct AST_INTERNAL::LookaheadRewriter if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0) return true; - for (auto child : node->children) - if (has_lookaheadids(child)) + for (auto& child : node->children) + if (has_lookaheadids(child.get())) return true; return false; @@ -230,8 +230,8 @@ struct AST_INTERNAL::LookaheadRewriter if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0) return true; - for (auto child : node->children) - if (has_nonlookaheadids(child)) + for (auto& child : node->children) + if (has_nonlookaheadids(child.get())) return true; return false; @@ -241,16 +241,16 @@ struct AST_INTERNAL::LookaheadRewriter { if (node->type == AST_ASSIGN_LE) { - if (has_lookaheadids(node->children[0])) + if (has_lookaheadids(node->children[0].get())) { - if (has_nonlookaheadids(node->children[0])) + if (has_nonlookaheadids(node->children[0].get())) log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n"); - rewrite_lookaheadids(node->children[0], true); + rewrite_lookaheadids(node->children[0].get(), true); node->type = AST_ASSIGN_EQ; } - rewrite_lookaheadids(node->children[1], lhs); + rewrite_lookaheadids(node->children[1].get(), lhs); return; } @@ -261,21 +261,22 @@ struct AST_INTERNAL::LookaheadRewriter lhs = false; } - for (auto child : node->children) - rewrite_lookaheadids(child, lhs); + for (auto& child : node->children) + rewrite_lookaheadids(child.get(), lhs); } LookaheadRewriter(AstNode *top) { - // top->dumpAst(NULL, "REWRITE-BEFORE> "); - // top->dumpVlog(NULL, "REWRITE-BEFORE> "); + // top->dumpAst(nullptr, "REWRITE-BEFORE> "); + // top->dumpVlog(nullptr, "REWRITE-BEFORE> "); AstNode *block = nullptr; + auto loc = top->location; - for (auto c : top->children) + for (auto& c : top->children) if (c->type == AST_BLOCK) { log_assert(block == nullptr); - block = c; + block = c.get(); } log_assert(block != nullptr); @@ -284,25 +285,25 @@ struct AST_INTERNAL::LookaheadRewriter for (auto it : lookaheadids) { - AstNode *ref_orig = new AstNode(AST_IDENTIFIER); + auto ref_orig = std::make_unique(loc, AST_IDENTIFIER); ref_orig->str = it.second.first->str; ref_orig->id2ast = it.second.first; ref_orig->was_checked = true; - AstNode *ref_temp = new AstNode(AST_IDENTIFIER); + auto ref_temp = std::make_unique(loc, AST_IDENTIFIER); ref_temp->str = it.second.second->str; ref_temp->id2ast = it.second.second; ref_temp->was_checked = true; - AstNode *init_assign = new AstNode(AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone()); - AstNode *final_assign = new AstNode(AST_ASSIGN_LE, ref_orig, ref_temp); + auto init_assign = std::make_unique(loc, AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone()); + auto final_assign = std::make_unique(loc, AST_ASSIGN_LE, std::move(ref_orig), std::move(ref_temp)); - block->children.insert(block->children.begin(), init_assign); - block->children.push_back(final_assign); + block->children.insert(block->children.begin(), std::move(init_assign)); + block->children.push_back(std::move(final_assign)); } - // top->dumpAst(NULL, "REWRITE-AFTER> "); - // top->dumpVlog(NULL, "REWRITE-AFTER> "); + // top->dumpAst(nullptr, "REWRITE-AFTER> "); + // top->dumpVlog(nullptr, "REWRITE-AFTER> "); } }; @@ -310,7 +311,7 @@ struct AST_INTERNAL::LookaheadRewriter struct AST_INTERNAL::ProcessGenerator { // input and output structures - AstNode *always; + std::unique_ptr always; RTLIL::SigSpec initSyncSignals; RTLIL::Process *proc; RTLIL::SigSpec outputSignals; @@ -341,14 +342,14 @@ struct AST_INTERNAL::ProcessGenerator // The most recently assigned $print or $check cell \PRIORITY. int last_effect_priority; - ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_effect_priority(0) + ProcessGenerator(std::unique_ptr a, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(std::move(a)), initSyncSignals(initSyncSignalsArg), last_effect_priority(0) { // rewrite lookahead references - LookaheadRewriter la_rewriter(always); + LookaheadRewriter la_rewriter(always.get()); // generate process and simple root case - proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(always->filename).c_str(), always->location.first_line, autoidx++)); - set_src_attr(proc, always); + proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(*always->location.begin.filename).c_str(), always->location.begin.line, autoidx++)); + set_src_attr(proc, always.get()); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) always->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); @@ -358,13 +359,13 @@ struct AST_INTERNAL::ProcessGenerator // create initial temporary signal for all output registers RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; - collect_lvalues(subst_lvalue_from, always, true, true); + collect_lvalues(subst_lvalue_from, always.get(), true, true); subst_lvalue_to = new_temp_signal(subst_lvalue_from); subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to); bool found_global_syncs = false; bool found_anyedge_syncs = false; - for (auto child : always->children) + for (auto& child : always->children) { if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk)) { @@ -388,7 +389,7 @@ struct AST_INTERNAL::ProcessGenerator // create syncs for the process bool found_clocked_sync = false; - for (auto child : always->children) + for (auto& child : always->children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) { if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk)) @@ -420,9 +421,9 @@ struct AST_INTERNAL::ProcessGenerator } // process the AST - for (auto child : always->children) + for (auto& child : always->children) if (child->type == AST_BLOCK) - processAst(child); + processAst(child.get()); for (auto sync: proc->syncs) processMemWrites(sync); @@ -472,7 +473,7 @@ struct AST_INTERNAL::ProcessGenerator for (int i = 0; i < GetSize(chunks); i++) { RTLIL::SigChunk &chunk = chunks[i]; - if (chunk.wire == NULL) + if (chunk.wire == nullptr) continue; std::string wire_name; @@ -484,7 +485,7 @@ struct AST_INTERNAL::ProcessGenerator } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); - set_src_attr(wire, always); + set_src_attr(wire, always.get()); chunk.wire = wire; chunk.offset = 0; @@ -499,10 +500,10 @@ struct AST_INTERNAL::ProcessGenerator switch (ast->type) { case AST_CASE: - for (auto child : ast->children) + for (auto& child : ast->children) if (child != ast->children[0]) { log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); - collect_lvalues(reg, child, type_eq, type_le, false); + collect_lvalues(reg, child.get(), type_eq, type_le, false); } break; @@ -511,19 +512,19 @@ struct AST_INTERNAL::ProcessGenerator case AST_CONDZ: case AST_ALWAYS: case AST_INITIAL: - for (auto child : ast->children) + for (auto& child : ast->children) if (child->type == AST_BLOCK) - collect_lvalues(reg, child, type_eq, type_le, false); + collect_lvalues(reg, child.get(), type_eq, type_le, false); break; case AST_BLOCK: - for (auto child : ast->children) { + for (auto& child : ast->children) { if (child->type == AST_ASSIGN_EQ && type_eq) reg.append(child->children[0]->genRTLIL()); if (child->type == AST_ASSIGN_LE && type_le) reg.append(child->children[0]->genRTLIL()); if (child->type == AST_CASE || child->type == AST_BLOCK) - collect_lvalues(reg, child, type_eq, type_le, false); + collect_lvalues(reg, child.get(), type_eq, type_le, false); } break; @@ -583,8 +584,8 @@ struct AST_INTERNAL::ProcessGenerator switch (ast->type) { case AST_BLOCK: - for (auto child : ast->children) - processAst(child); + for (auto& child : ast->children) + processAst(child.get()); break; case AST_ASSIGN_EQ: @@ -641,9 +642,9 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue; this_case_eq_rvalue.replace(subst_rvalue_map.stdmap()); - RTLIL::CaseRule *default_case = NULL; - RTLIL::CaseRule *last_generated_case = NULL; - for (auto child : ast->children) + RTLIL::CaseRule *default_case = nullptr; + RTLIL::CaseRule *last_generated_case = nullptr; + for (auto& child : ast->children) { if (child == ast->children[0]) continue; @@ -657,14 +658,14 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; - set_src_attr(current_case, child); + set_src_attr(current_case, child.get()); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); - for (auto node : child->children) { + for (auto& node : child->children) { if (node->type == AST_DEFAULT) default_case = current_case; else if (node->type == AST_BLOCK) - processAst(node); + processAst(node.get()); else current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap())); } @@ -678,7 +679,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.restore(); } - if (last_generated_case != NULL && ast->get_bool_attribute(ID::full_case) && default_case == NULL) { + if (last_generated_case != nullptr && ast->get_bool_attribute(ID::full_case) && default_case == nullptr) { #if 0 // this is a valid transformation, but as optimization it is premature. // better: add a default case that assigns 'x' to everything, and let later @@ -690,7 +691,7 @@ struct AST_INTERNAL::ProcessGenerator sw->cases.push_back(default_case); #endif } else { - if (default_case == NULL) { + if (default_case == nullptr) { default_case = new RTLIL::CaseRule; addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); } @@ -723,7 +724,7 @@ struct AST_INTERNAL::ProcessGenerator if (ast->str == "$display" || ast->str == "$displayb" || ast->str == "$displayh" || ast->str == "$displayo" || ast->str == "$write" || ast->str == "$writeb" || ast->str == "$writeh" || ast->str == "$writeo") { std::stringstream sstr; - sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++); + sstr << ast->str << "$" << ast->location.begin.filename << ":" << ast->location.begin.line << "$" << (autoidx++); Wire *en = current_module->addWire(sstr.str() + "_EN", 1); set_src_attr(en, ast); @@ -760,14 +761,14 @@ struct AST_INTERNAL::ProcessGenerator default_base = 16; std::vector args; - for (auto node : ast->children) { + for (auto& node : ast->children) { int width; bool is_signed; node->detectSignWidth(width, is_signed, nullptr); VerilogFmtArg arg = {}; - arg.filename = node->filename; - arg.first_line = node->location.first_line; + arg.filename = *node->location.begin.filename; + arg.first_line = node->location.begin.line; if (node->type == AST_CONSTANT && node->is_string) { arg.type = VerilogFmtArg::STRING; arg.str = node->bitsAsConst().decode_string(); @@ -793,7 +794,7 @@ struct AST_INTERNAL::ProcessGenerator fmt.append_literal("\n"); fmt.emit_rtlil(cell); } else if (!ast->str.empty()) { - log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); + log_file_error(*ast->location.begin.filename, ast->location.begin.line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); } break; @@ -813,7 +814,7 @@ struct AST_INTERNAL::ProcessGenerator IdString cellname; if (ast->str.empty()) - cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(ast->filename).c_str(), ast->location.first_line, autoidx++); + cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(*ast->location.begin.filename).c_str(), ast->location.begin.line, autoidx++); else cellname = ast->str; check_unique_id(current_module, cellname, ast, "procedural assertion"); @@ -843,7 +844,7 @@ struct AST_INTERNAL::ProcessGenerator set_src_attr(cell, ast); for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + log_file_error(*ast->location.begin.filename, ast->location.begin.line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->setParam(ID::FLAVOR, flavor); @@ -866,8 +867,8 @@ struct AST_INTERNAL::ProcessGenerator break; default: - // ast->dumpAst(NULL, "ast> "); - // current_ast_mod->dumpAst(NULL, "mod> "); + // ast->dumpAst(nullptr, "ast> "); + // current_ast_mod->dumpAst(nullptr, "mod> "); log_abort(); } } @@ -876,14 +877,14 @@ struct AST_INTERNAL::ProcessGenerator { // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array. dict, int> port_map; - for (auto child : always->children) + for (auto& child : always->children) if (child->type == AST_MEMWR) { std::string memid = child->str; int portid = child->children[3]->asInt(false); int cur_idx = GetSize(sync->mem_write_actions); RTLIL::MemWriteAction action; - set_src_attr(&action, child); + set_src_attr(&action, child.get()); action.memid = memid; action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap()); action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap()); @@ -971,11 +972,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun bool sub_sign_hint = true; int sub_width_hint = -1; int this_width = 0; - AstNode *range = NULL; - AstNode *id_ast = NULL; + AstNode *range = nullptr; + AstNode *id_ast = nullptr; bool local_found_real = false; - if (found_real == NULL) + if (found_real == nullptr) found_real = &local_found_real; switch (type) @@ -1019,22 +1020,22 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun input_error("Failed to detect width for parameter %s!\n", str.c_str()); } if (children.size() != 0) - range = children[0]; + range = children[0].get(); } else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) { if (!id_ast->range_valid) { if (id_ast->type == AST_AUTOWIRE) this_width = 1; else { - // current_ast_mod->dumpAst(NULL, "mod> "); + // current_ast_mod->dumpAst(nullptr, "mod> "); // log("---\n"); - // id_ast->dumpAst(NULL, "decl> "); - // dumpAst(NULL, "ref> "); + // id_ast->dumpAst(nullptr, "decl> "); + // dumpAst(nullptr, "ref> "); input_error("Failed to detect width of signal access `%s'!\n", str.c_str()); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; if (children.size() != 0) - range = children[0]; + range = children[0].get(); } } else if (id_ast->type == AST_GENVAR) { this_width = 32; @@ -1043,26 +1044,23 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun input_error("Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; if (children.size() > 1) - range = children[1]; + range = children[1].get(); } else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) { - AstNode *tmp_range = make_index_range(id_ast); + auto tmp_range = make_index_range(id_ast); this_width = tmp_range->range_left - tmp_range->range_right + 1; - delete tmp_range; } else input_error("Failed to detect width for identifier %s!\n", str.c_str()); if (range) { if (range->children.size() == 1) this_width = 1; else if (!range->range_valid) { - AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); - AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); + auto left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + auto right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); while (left_at_zero_ast->simplify(true, 1, -1, false)) { } while (right_at_zero_ast->simplify(true, 1, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - delete left_at_zero_ast; - delete right_at_zero_ast; } else this_width = range->range_left - range->range_right + 1; sign_hint = false; @@ -1106,7 +1104,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; case AST_CONCAT: - for (auto child : children) { + for (auto& child : children) { sub_width_hint = 0; sub_sign_hint = true; child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); @@ -1135,7 +1133,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_BIT_OR: case AST_BIT_XOR: case AST_BIT_XNOR: - for (auto child : children) + for (auto& child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; @@ -1175,7 +1173,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_MUL: case AST_DIV: case AST_MOD: - for (auto child : children) + for (auto& child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; @@ -1216,12 +1214,13 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun width_hint = max(width_hint, sub_width_hint); sign_hint &= sub_sign_hint; }; - visit_case_expr(children[0]); + visit_case_expr(children[0].get()); for (size_t i = 1; i < children.size(); i++) { - AstNode *child = children[i]; - for (AstNode *v : child->children) + AstNode *child = children[i].get(); + for (auto& v : child->children) { if (v->type != AST_DEFAULT && v->type != AST_BLOCK) - visit_case_expr(v); + visit_case_expr(v.get()); + } } break; } @@ -1269,9 +1268,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (func->type != AST_FUNCTION) input_error("Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); const AstNode *wire = nullptr; - for (const AstNode *child : func->children) + for (const auto& child : func->children) if (child->str == func->str) { - wire = child; + wire = child.get(); break; } log_assert(wire && wire->type == AST_WIRE); @@ -1280,10 +1279,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!wire->children.empty()) { log_assert(wire->children.size() == 1); - const AstNode *range = wire->children.at(0); + const AstNode *range = wire->children.at(0).get(); log_assert(range->type == AST_RANGE && range->children.size() == 2); - AstNode *left = range->children.at(0)->clone(); - AstNode *right = range->children.at(1)->clone(); + auto left = range->children.at(0)->clone(); + auto right = range->children.at(1)->clone(); left->set_in_param_flag(true); right->set_in_param_flag(true); while (left->simplify(true, 1, -1, false)) { } @@ -1292,8 +1291,6 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun input_error("Function %s has non-constant width!", RTLIL::unescape_id(str).c_str()); result_width = abs(int(left->asInt(true) - right->asInt(true))); - delete left; - delete right; } width_hint = max(width_hint, result_width); break; @@ -1306,6 +1303,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun for (auto f : log_files) current_scope_ast->dumpAst(f, "verilog-ast> "); input_error("Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); + } if (*found_real) @@ -1342,8 +1340,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // be instantiated for this type of AST node. IdString type_name; - current_filename = filename; - switch (type) { // simply ignore this nodes. @@ -1361,6 +1357,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: + case AST_IMPORT: case AST_ENUM: case AST_MODPORT: case AST_MODPORTMEMBER: @@ -1508,7 +1505,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } RTLIL::SigSpec sig = realAsConst(width_hint); - log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); + log_file_warning(*location.begin.filename, location.begin.line, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); return sig; } @@ -1517,11 +1514,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // shifter cell is created and the output signal of this cell is returned case AST_IDENTIFIER: { - RTLIL::Wire *wire = NULL; + RTLIL::Wire *wire = nullptr; RTLIL::SigChunk chunk; bool is_interface = false; - AST::AstNode *member_node = NULL; + AST::AstNode *member_node = nullptr; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -1540,7 +1537,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (dynamic_cast(current_module)) { /* nothing to do here */ } else if (flag_autowire) - log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); + log_file_warning(*location.begin.filename, location.begin.line, "Identifier `%s' is implicitly declared.\n", str.c_str()); else input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); } @@ -1608,14 +1605,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } if (!children[0]->range_valid) { - AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); - AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); + auto left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + auto right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); while (left_at_zero_ast->simplify(true, 1, -1, false)) { } while (right_at_zero_ast->simplify(true, 1, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? + auto fake_ast = std::make_unique(children[0]->location, AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); fake_ast->children[0]->delete_children(); if (member_node) @@ -1636,10 +1633,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } if (GetSize(shift_val) >= 32) fake_ast->children[1]->is_signed = true; - RTLIL::SigSpec sig = binop2rtlil(fake_ast, ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val); - delete left_at_zero_ast; - delete right_at_zero_ast; - delete fake_ast; + RTLIL::SigSpec sig = binop2rtlil(fake_ast.get(), ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val); return sig; } else { chunk.width = children[0]->range_left - children[0]->range_right + 1; @@ -1648,10 +1642,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset = source_width - (chunk.offset + chunk.width); if (chunk.offset > chunk_left || chunk.offset + chunk.width < chunk_right) { if (chunk.width == 1) - log_file_warning(filename, location.first_line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", + log_file_warning(*location.begin.filename, location.begin.line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", str.c_str()); else - log_file_warning(filename, location.first_line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", + log_file_warning(*location.begin.filename, location.begin.line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { @@ -1665,10 +1659,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) - log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", + log_file_warning(*location.begin.filename, location.begin.line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); if (add_undef_bits_msb) - log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", + log_file_warning(*location.begin.filename, location.begin.line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); } } @@ -1942,7 +1936,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMRD: { std::stringstream sstr; - sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); set_src_attr(cell, this); @@ -1980,7 +1974,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMINIT: { std::stringstream sstr; - sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); SigSpec en_sig = children[2]->genRTLIL(); @@ -2025,7 +2019,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) IdString cellname; if (str.empty()) - cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line, autoidx++); else cellname = str; check_unique_id(current_module, cellname, this, "procedural assertion"); @@ -2068,7 +2062,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) new_left.append(left[i]); new_right.append(right[i]); } - log_file_warning(filename, location.first_line, "Ignoring assignment to constant bits:\n" + log_file_warning(*location.begin.filename, location.begin.line, "Ignoring assignment to constant bits:\n" " old assignment: %s = %s\n new assignment: %s = %s.\n", log_signal(left), log_signal(right), log_signal(new_left), log_signal(new_right)); @@ -2092,7 +2086,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cell->set_bool_attribute(ID::module_not_derived); for (auto it = children.begin(); it != children.end(); it++) { - AstNode *child = *it; + auto* child = it->get(); if (child->type == AST_CELLTYPE) { cell->type = child->str; if (flag_icells && cell->type.begins_with("\\$")) @@ -2101,9 +2095,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } if (child->type == AST_PARASET) { IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; - const AstNode *value = child->children[0]; + const auto* value = child->children[0].get(); if (value->type == AST_REALVALUE) - log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n", + log_file_warning(*location.begin.filename, location.begin.line, "Replacing floating point parameter %s.%s = %f with string.\n", log_id(cell), log_id(paraname), value->realvalue); else if (value->type != AST_CONSTANT) input_error("Parameter %s.%s with non-constant value!\n", @@ -2114,7 +2108,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (child->type == AST_ARGUMENT) { RTLIL::SigSpec sig; if (child->children.size() > 0) { - AstNode *arg = child->children[0]; + auto* arg = child->children[0].get(); int local_width_hint = -1; bool local_sign_hint = false; // don't inadvertently attempt to detect the width of interfaces @@ -2186,30 +2180,27 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // use ProcessGenerator for always blocks case AST_ALWAYS: { - AstNode *always = this->clone(); - ProcessGenerator generator(always); + ProcessGenerator generator(this->clone()); ignoreThisSignalsInInitial.append(generator.outputSignals); - delete always; } break; case AST_INITIAL: { - AstNode *always = this->clone(); - ProcessGenerator generator(always, ignoreThisSignalsInInitial); - delete always; + auto always = this->clone(); + ProcessGenerator generator(this->clone(), ignoreThisSignalsInInitial); } break; case AST_TECALL: { int sz = children.size(); if (str == "$info") { if (sz > 0) - log_file_info(filename, location.first_line, "%s.\n", children[0]->str.c_str()); + log_file_info(*location.begin.filename, location.begin.line, "%s.\n", children[0]->str.c_str()); else - log_file_info(filename, location.first_line, "\n"); + log_file_info(*location.begin.filename, location.begin.line, "\n"); } else if (str == "$warning") { if (sz > 0) - log_file_warning(filename, location.first_line, "%s.\n", children[0]->str.c_str()); + log_file_warning(*location.begin.filename, location.begin.line, "%s.\n", children[0]->str.c_str()); else - log_file_warning(filename, location.first_line, "\n"); + log_file_warning(*location.begin.filename, location.begin.line, "\n"); } else if (str == "$error") { if (sz > 0) input_error("%s.\n", children[0]->str.c_str()); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 4d8c57ced..b1a615d76 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -35,25 +35,14 @@ #include #include #include -// For std::gcd in C++17 -// #include +#include +#include YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// gcd computed by Euclidian division. -// To be replaced by C++17 std::gcd -template I gcd(I a, I b) { - while (b != 0) { - I tmp = b; - b = a%b; - a = tmp; - } - return std::abs(a); -} - void AstNode::set_in_lvalue_flag(bool flag, bool no_descend) { if (flag != in_lvalue_from_above) { @@ -87,7 +76,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_PARASET: case AST_PREFIX: in_param = true; - for (auto child : children) + for (auto& child : children) child->set_in_param_flag(true, force_descend); break; @@ -95,7 +84,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_WIRE: case AST_GENIF: case AST_GENCASE: - for (auto child : children) + for (auto& child : children) child->set_in_param_flag(in_param, force_descend); if (children.size() >= 1) children[0]->set_in_param_flag(true, force_descend); @@ -103,19 +92,21 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_GENFOR: case AST_FOR: - for (auto child : children) + for (auto& child : children) { + log_assert((bool)child); child->set_in_param_flag(in_param, force_descend); + } if (children.size() >= 2) children[1]->set_in_param_flag(true, force_descend); break; default: in_param = in_param_from_above; - for (auto child : children) + for (auto& child : children) child->set_in_param_flag(in_param, force_descend); } - for (auto attr : attributes) + for (auto& attr : attributes) attr.second->set_in_param_flag(true, force_descend); in_lvalue = in_lvalue_from_above; @@ -131,14 +122,14 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) break; default: - for (auto child : children) + for (auto& child : children) child->set_in_lvalue_flag(in_lvalue, force_descend); } if (force_descend) { - for (auto child : children) + for (auto& child : children) child->fixup_hierarchy_flags(true); - for (auto attr : attributes) + for (auto& attr : attributes) attr.second->fixup_hierarchy_flags(true); } } @@ -148,12 +139,12 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) { std::vector args; for (size_t index = first_arg_at; index < children.size(); index++) { - AstNode *node_arg = children[index]; + AstNode *node_arg = children[index].get(); while (node_arg->simplify(true, stage, -1, false)) { } VerilogFmtArg arg = {}; - arg.filename = filename; - arg.first_line = location.first_line; + arg.filename = *location.begin.filename; + arg.first_line = location.begin.line; if (node_arg->type == AST_CONSTANT && node_arg->is_string) { arg.type = VerilogFmtArg::STRING; arg.str = node_arg->bitsAsConst().decode_string(); @@ -170,10 +161,10 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_ arg.sig = node_arg->bitsAsConst(); arg.signed_ = node_arg->is_signed; } else if (may_fail) { - log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); + log_file_info(*location.begin.filename, location.begin.line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); return Fmt(); } else { - log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); + log_file_error(*location.begin.filename, location.begin.line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); } args.push_back(arg); } @@ -199,7 +190,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node) while (enum_node->simplify(true, 1, -1, false)) { } //get width from 1st enum item: log_assert(enum_node->children.size() >= 1); - AstNode *enum_item0 = enum_node->children[0]; + AstNode *enum_item0 = enum_node->children[0].get(); log_assert(enum_item0->type == AST_ENUM_ITEM); int width; if (!enum_item0->range_valid) @@ -210,7 +201,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node) width = enum_item0->range_left - enum_item0->range_right + 1; log_assert(width > 0); //add declared enum items: - for (auto enum_item : enum_node->children){ + for (auto& enum_item : enum_node->children){ log_assert(enum_item->type == AST_ENUM_ITEM); //get is_signed bool is_signed; @@ -237,20 +228,20 @@ void AstNode::annotateTypedEnums(AstNode *template_node) RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); enum_item_str.append(val.as_string()); //set attribute for available val to enum item name mappings - set_attribute(enum_item_str.c_str(), mkconst_str(enum_item->str)); + set_attribute(enum_item_str.c_str(), mkconst_str(location, enum_item->str)); } } } -static AstNode *make_range(int left, int right, bool is_signed = false) +static std::unique_ptr make_range(AstSrcLocType loc, int left, int right, bool is_signed = false) { // generate a pre-validated range node for a fixed signal range. - auto range = new AstNode(AST_RANGE); + auto range = std::make_unique(loc, AST_RANGE); range->range_left = left; range->range_right = right; range->range_valid = true; - range->children.push_back(AstNode::mkconst_int(left, true)); - range->children.push_back(AstNode::mkconst_int(right, true)); + range->children.push_back(AstNode::mkconst_int(loc, left, true)); + range->children.push_back(AstNode::mkconst_int(loc, right, true)); range->is_signed = is_signed; return range; } @@ -287,7 +278,7 @@ static int size_packed_struct(AstNode *snode, int base_offset) int packed_width = -1; // examine members from last to first for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { - auto node = *it; + auto node = it->get(); int width; if (node->type == AST_STRUCT || node->type == AST_UNION) { // embedded struct or union @@ -297,14 +288,14 @@ static int size_packed_struct(AstNode *snode, int base_offset) log_assert(node->type == AST_STRUCT_ITEM); if (node->children.size() > 0 && node->children[0]->type == AST_RANGE) { // member width e.g. bit [7:0] a - width = range_width(node, node->children[0]); + width = range_width(node, node->children[0].get()); if (node->children.size() == 2) { // Unpacked array. Note that this is a Yosys extension; only packed data types // and integer data types are allowed in packed structs / unions in SystemVerilog. if (node->children[1]->type == AST_RANGE) { // Unpacked array, e.g. bit [63:0] a [0:3] // Pretend it's declared as a packed array, e.g. bit [0:3][63:0] a - auto rnode = node->children[1]; + auto rnode = node->children[1].get(); if (rnode->children.size() == 1) { // C-style array size, e.g. bit [63:0] a [4] node->dimensions.push_back({ 0, rnode->range_left, true }); @@ -312,7 +303,7 @@ static int size_packed_struct(AstNode *snode, int base_offset) } else { width *= add_dimension(node, rnode); } - add_dimension(node, node->children[0]); + add_dimension(node, node->children[0].get()); } else { // The Yosys extension for unpacked arrays in packed structs / unions @@ -321,11 +312,9 @@ static int size_packed_struct(AstNode *snode, int base_offset) } } else { // Vector - add_dimension(node, node->children[0]); + add_dimension(node, node->children[0].get()); } // range nodes are now redundant - for (AstNode *child : node->children) - delete child; node->children.clear(); } else if (node->children.size() > 0 && node->children[0]->type == AST_MULTIRANGE) { @@ -336,12 +325,10 @@ static int size_packed_struct(AstNode *snode, int base_offset) struct_array_packing_error(node); } width = 1; - for (auto rnode : node->children[0]->children) { - width *= add_dimension(node, rnode); + for (auto& rnode : node->children[0]->children) { + width *= add_dimension(node, rnode.get()); } // range nodes are now redundant - for (AstNode *child : node->children) - delete child; node->children.clear(); } else if (node->range_left < 0) { @@ -389,85 +376,88 @@ static int size_packed_struct(AstNode *snode, int base_offset) return width; } -static AstNode *node_int(int ival) +static std::unique_ptr node_int(AstSrcLocType loc, int ival) { - return AstNode::mkconst_int(ival, true); + return AstNode::mkconst_int(loc, ival, true); } -static AstNode *multiply_by_const(AstNode *expr_node, int stride) +static std::unique_ptr multiply_by_const(std::unique_ptr expr_node, int stride) { - return new AstNode(AST_MUL, expr_node, node_int(stride)); + auto loc = expr_node->location; + return std::make_unique(loc, AST_MUL, std::move(expr_node), node_int(loc, stride)); } -static AstNode *normalize_index(AstNode *expr, AstNode *decl_node, int dimension) +static std::unique_ptr normalize_index(AstNode *expr, AstNode *decl_node, int dimension) { - expr = expr->clone(); + auto new_expr = expr->clone(); + auto loc = expr->location; int offset = decl_node->dimensions[dimension].range_right; if (offset) { - expr = new AstNode(AST_SUB, expr, node_int(offset)); + new_expr = std::make_unique(loc, AST_SUB, std::move(new_expr), node_int(loc, offset)); } // Packed dimensions are normally indexed by lsb, while unpacked dimensions are normally indexed by msb. if ((dimension < decl_node->unpacked_dimensions) ^ decl_node->dimensions[dimension].range_swapped) { // Swap the index if the dimension is declared the "wrong" way. int left = decl_node->dimensions[dimension].range_width - 1; - expr = new AstNode(AST_SUB, node_int(left), expr); + new_expr = std::make_unique(loc, AST_SUB, node_int(loc, left), std::move(new_expr)); } - return expr; + return new_expr; } -static AstNode *index_offset(AstNode *offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride) +static std::unique_ptr index_offset(std::unique_ptr offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride) { stride /= decl_node->dimensions[dimension].range_width; - auto right = normalize_index(rnode->children.back(), decl_node, dimension); - auto add_offset = stride > 1 ? multiply_by_const(right, stride) : right; - return offset ? new AstNode(AST_ADD, offset, add_offset) : add_offset; + auto right = normalize_index(rnode->children.back().get(), decl_node, dimension); + auto add_offset = stride > 1 ? multiply_by_const(std::move(right), stride) : std::move(right); + return offset ? std::make_unique(rnode->location, AST_ADD, std::move(offset), std::move(add_offset)) : std::move(add_offset); } -static AstNode *index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride) +static std::unique_ptr index_msb_offset(std::unique_ptr lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride) { log_assert(rnode->children.size() <= 2); + auto loc = rnode->location; // Offset to add to LSB - AstNode *add_offset; + std::unique_ptr add_offset; if (rnode->children.size() == 1) { // Index, e.g. s.a[i] - add_offset = node_int(stride - 1); + add_offset = node_int(rnode->location, stride - 1); } else { // rnode->children.size() == 2 // Slice, e.g. s.a[i:j] - auto left = normalize_index(rnode->children[0], decl_node, dimension); - auto right = normalize_index(rnode->children[1], decl_node, dimension); - add_offset = new AstNode(AST_SUB, left, right); + auto left = normalize_index(rnode->children[0].get(), decl_node, dimension); + auto right = normalize_index(rnode->children[1].get(), decl_node, dimension); + add_offset = std::make_unique(loc, AST_SUB, std::move(left), std::move(right)); if (stride > 1) { // offset = (msb - lsb + 1)*stride - 1 - auto slice_width = new AstNode(AST_ADD, add_offset, node_int(1)); - add_offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1)); + auto slice_width = std::make_unique(loc, AST_ADD, std::move(add_offset), node_int(loc, 1)); + add_offset = std::make_unique(loc, AST_SUB, multiply_by_const(std::move(slice_width), stride), node_int(loc, 1)); } } - return new AstNode(AST_ADD, lsb_offset, add_offset); + return std::make_unique(loc, AST_ADD, std::move(lsb_offset), std::move(add_offset)); } -AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) +std::unique_ptr AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) { // Work out the range in the packed array that corresponds to a struct member // taking into account any range operations applicable to the current node // such as array indexing or slicing if (children.empty()) { // no range operations apply, return the whole width - return make_range(decl_node->range_left - decl_node->range_right, 0); + return make_range(decl_node->location, decl_node->range_left - decl_node->range_right, 0); } log_assert(children.size() == 1); // Range operations - AstNode *rnode = children[0]; - AstNode *offset = NULL; + AstNode *rnode = children[0].get(); + std::unique_ptr offset = nullptr; int dim = unpacked_range ? 0 : decl_node->unpacked_dimensions; int max_dim = unpacked_range ? decl_node->unpacked_dimensions : GetSize(decl_node->dimensions); @@ -478,15 +468,15 @@ AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) // Calculate LSB offset for the final index / slice if (rnode->type == AST_RANGE) { - offset = index_offset(offset, rnode, decl_node, dim, stride); + offset = index_offset(std::move(offset), rnode, decl_node, dim, stride); } else if (rnode->type == AST_MULTIRANGE) { // Add offset for each dimension AstNode *mrnode = rnode; int stop_dim = std::min(GetSize(mrnode->children), max_dim); for (; dim < stop_dim; dim++) { - rnode = mrnode->children[dim]; - offset = index_offset(offset, rnode, decl_node, dim, stride); + rnode = mrnode->children[dim].get(); + offset = index_offset(std::move(offset), rnode, decl_node, dim, stride); } dim--; // Step back to the final index / slice } @@ -494,15 +484,15 @@ AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) input_error("Unsupported range operation for %s\n", str.c_str()); } - AstNode *index_range = new AstNode(AST_RANGE); + std::unique_ptr index_range = std::make_unique(rnode->location, AST_RANGE); if (!unpacked_range && (stride > 1 || GetSize(rnode->children) == 2)) { // Calculate MSB offset for the final index / slice of packed dimensions. - AstNode *msb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride); - index_range->children.push_back(msb_offset); + std::unique_ptrmsb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride); + index_range->children.push_back(std::move(msb_offset)); } - index_range->children.push_back(offset); + index_range->children.push_back(std::move(offset)); return index_range; } @@ -510,8 +500,8 @@ AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) AstNode *AstNode::get_struct_member() const { AstNode *member_node; - if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype)) && - (member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION)) + if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype).get()) && + (member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION)) { return member_node; } @@ -523,20 +513,21 @@ static void add_members_to_scope(AstNode *snode, std::string name) // add all the members in a struct or union to local scope // in case later referenced in assignments log_assert(snode->type==AST_STRUCT || snode->type==AST_UNION); - for (auto *node : snode->children) { + for (auto &node : snode->children) { auto member_name = name + "." + node->str; - current_scope[member_name] = node; + current_scope[member_name] = node.get(); if (node->type != AST_STRUCT_ITEM) { // embedded struct or union - add_members_to_scope(node, name + "." + node->str); + add_members_to_scope(node.get(), name + "." + node->str); } } } -static AstNode *make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes) +std::unique_ptr make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes) { // create a wire for the packed struct - auto wnode = new AstNode(AST_WIRE, make_range(template_node->range_left, 0)); + auto loc = template_node->location; + auto wnode = std::make_unique(loc, AST_WIRE, make_range(loc, template_node->range_left, 0)); wnode->str = name; wnode->is_logic = true; wnode->range_valid = true; @@ -547,24 +538,25 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name, de // resolve packed dimension while (wnode->simplify(true, 1, -1, false)) {} // make sure this node is the one in scope for this name - current_scope[name] = wnode; + current_scope[name] = wnode.get(); // add all the struct members to scope under the wire's name add_members_to_scope(template_node, name); return wnode; } -static void prepend_ranges(AstNode *&range, AstNode *range_add) +static void prepend_ranges(std::unique_ptr &range, AstNode *range_add) { // Convert range to multirange. + auto loc = range->location; if (range->type == AST_RANGE) - range = new AstNode(AST_MULTIRANGE, range); + range = std::make_unique(loc, AST_MULTIRANGE, std::move(range)); // Add range or ranges. if (range_add->type == AST_RANGE) range->children.insert(range->children.begin(), range_add->clone()); else { int i = 0; - for (auto child : range_add->children) + for (auto& child : range_add->children) range->children.insert(range->children.begin() + i++, child->clone()); } } @@ -575,16 +567,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) { // current node is iteslf an assignment log_assert(node->children.size() >= 2); - const AstNode* lhs = node->children[0]; + const AstNode* lhs = node->children[0].get(); if (lhs->type == AST_IDENTIFIER && lhs->str == var->str) return false; } - for (const AstNode* child : node->children) { + for (auto& child : node->children) { // if this child shadows the given variable - if (child != var && child->str == var->str && child->type == AST_WIRE) + if (child.get() != var && child->str == var->str && child->type == AST_WIRE) break; // skip the remainder of this block/scope // depth-first short circuit - if (!node_contains_assignment_to(child, var)) + if (!node_contains_assignment_to(child.get(), var)) return false; } return true; @@ -621,13 +613,13 @@ const RTLIL::Module* AstNode::lookup_cell_module() auto reprocess_after = [this] (const std::string &modname) { if (!attributes.count(ID::reprocess_after)) - set_attribute(ID::reprocess_after, AstNode::mkconst_str(modname)); + set_attribute(ID::reprocess_after, AstNode::mkconst_str(location, modname)); }; const AstNode *celltype = nullptr; - for (const AstNode *child : children) + for (auto& child : children) if (child->type == AST_CELLTYPE) { - celltype = child; + celltype = child.get(); break; } log_assert(celltype != nullptr); @@ -644,7 +636,7 @@ const RTLIL::Module* AstNode::lookup_cell_module() // build a mapping from true param name to param value size_t para_counter = 0; dict cell_params_map; - for (AstNode *child : children) { + for (auto& child : children) { if (child->type != AST_PARASET) continue; @@ -652,7 +644,7 @@ const RTLIL::Module* AstNode::lookup_cell_module() return nullptr; // let hierarchy handle this error IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str; - const AstNode *value = child->children[0]; + const AstNode *value = child->children[0].get(); if (value->type != AST_REALVALUE && value->type != AST_CONSTANT) return nullptr; // let genrtlil handle this error cell_params_map[paraname] = value->asParaConst(); @@ -684,29 +676,29 @@ static bool contains_unbased_unsized(const AstNode *node) { if (node->type == AST_CONSTANT) return node->is_unsized; - for (const AstNode *child : node->children) - if (contains_unbased_unsized(child)) + for (auto& child : node->children) + if (contains_unbased_unsized(child.get())) return true; return false; } // adds a wire to the current module with the given name that matches the // dimensions of the given wire reference -void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str) +void add_wire_for_ref(Location loc, const RTLIL::Wire *ref, const std::string &str) { - AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true); - AstNode *right = AstNode::mkconst_int(ref->start_offset, true); + auto left = AstNode::mkconst_int(loc, ref->width - 1 + ref->start_offset, true); + auto right = AstNode::mkconst_int(loc, ref->start_offset, true); if (ref->upto) std::swap(left, right); - AstNode *range = new AstNode(AST_RANGE, left, right); + auto range = std::make_unique(loc, AST_RANGE, std::move(left), std::move(right)); - AstNode *wire = new AstNode(AST_WIRE, range); + auto wire = std::make_unique(loc, AST_WIRE, std::move(range)); wire->is_signed = ref->is_signed; wire->is_logic = true; wire->str = str; - current_ast_mod->children.push_back(wire); - current_scope[str] = wire; + current_scope[str] = wire.get(); + current_ast_mod->children.push_back(std::move(wire)); } enum class IdentUsage { @@ -733,10 +725,10 @@ static IdentUsage always_asgn_before_use(const AstNode *node, const std::string bool all_defined = true; bool any_used = false; bool has_default = false; - for (const AstNode *child : node->children) { + for (auto& child : node->children) { if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT) has_default = true; - IdentUsage nested = always_asgn_before_use(child, target); + IdentUsage nested = always_asgn_before_use(child.get(), target); if (nested != IdentUsage::Assigned && child->type == AST_COND) all_defined = false; if (nested == IdentUsage::SyncRequired) @@ -753,20 +745,20 @@ static IdentUsage always_asgn_before_use(const AstNode *node, const std::string // Check if this is an assignment to the target variable. For simplicity, we // don't analyze sub-ranges of the variable. if (node->type == AST_ASSIGN_EQ) { - const AstNode *ident = node->children.at(0); + auto& ident = node->children.at(0); if (ident->type == AST_IDENTIFIER && ident->str == target) return IdentUsage::Assigned; } - for (const AstNode *child : node->children) { - IdentUsage nested = always_asgn_before_use(child, target); + for (auto& child : node->children) { + IdentUsage nested = always_asgn_before_use(child.get(), target); if (nested != IdentUsage::NotReferenced) return nested; } return IdentUsage::NotReferenced; } -AstNode *AstNode::clone_at_zero() +std::unique_ptr AstNode::clone_at_zero() { int width_hint; bool sign_hint; @@ -789,14 +781,13 @@ AstNode *AstNode::clone_at_zero() YS_FALLTHROUGH case AST_MEMRD: detectSignWidth(width_hint, sign_hint); - return mkconst_int(0, sign_hint, width_hint); + return mkconst_int(location, 0, sign_hint, width_hint); default: break; } - AstNode *that = new AstNode; - *that = *this; + auto that = clone(); for (auto &it : that->children) it = it->clone_at_zero(); for (auto &it : that->attributes) @@ -818,8 +809,8 @@ static bool try_determine_range_width(AstNode *range, int &result_width) return true; } - AstNode *left_at_zero_ast = range->children[0]->clone_at_zero(); - AstNode *right_at_zero_ast = range->children[1]->clone_at_zero(); + auto left_at_zero_ast = range->children[0]->clone_at_zero(); + auto right_at_zero_ast = range->children[1]->clone_at_zero(); while (left_at_zero_ast->simplify(true, 1, -1, false)) {} while (right_at_zero_ast->simplify(true, 1, -1, false)) {} @@ -831,8 +822,6 @@ static bool try_determine_range_width(AstNode *range, int &result_width) result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } - delete left_at_zero_ast; - delete right_at_zero_ast; return ok; } @@ -844,7 +833,7 @@ static void mark_auto_nosync(AstNode *block, const AstNode *wire) { log_assert(block->type == AST_BLOCK); log_assert(wire->type == AST_WIRE); - block->set_attribute(auto_nosync_prefix + wire->str, AstNode::mkconst_int(1, false)); + block->set_attribute(auto_nosync_prefix + wire->str, AstNode::mkconst_int(block->location, 1, false)); } // block names can be prefixed with an explicit scope during elaboration @@ -885,19 +874,18 @@ static void check_auto_nosync(AstNode *node) // mark the wire with `nosync` AstNode *wire = it->second; log_assert(wire->type == AST_WIRE); - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(wire->location, 1, false)); } // remove the attributes we've "consumed" for (const RTLIL::IdString &str : attrs_to_drop) { auto it = node->attributes.find(str); - delete it->second; node->attributes.erase(it); } // check local variables in any nested blocks - for (AstNode *child : node->children) - check_auto_nosync(child); + for (auto& child : node->children) + check_auto_nosync(child.get()); } // convert the AST into a simpler AST that has all parameters substituted by their @@ -918,15 +906,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin static bool unevaluated_tern_branch = false; - AstNode *newNode = NULL; + std::unique_ptr newNode = nullptr; bool did_something = false; #if 0 log("-------------\n"); - log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), location.first_line, type2str(type).c_str(), this); + log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, location.begin.filename->c_str(), location.begin.line, type2str(type).c_str(), this); log("const_fold=%d, stage=%d, width_hint=%d, sign_hint=%d\n", int(const_fold), int(stage), int(width_hint), int(sign_hint)); - // dumpAst(NULL, "> "); + // dumpAst(nullptr, "> "); #endif if (stage == 0) @@ -1005,30 +993,27 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (node->children[0]->range_swapped) std::swap(data_range_left, data_range_right); + auto loc = node->location; for (int i = 0; i < mem_size; i++) { - AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, - mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); + auto reg = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, + mkconst_int(loc, data_range_left, true), mkconst_int(loc, data_range_right, true))); reg->str = stringf("%s[%d]", node->str.c_str(), i); reg->is_reg = true; reg->is_signed = node->is_signed; for (auto &it : node->attributes) if (it.first != ID::mem2reg) reg->set_attribute(it.first, it.second->clone()); - reg->filename = node->filename; + reg->location.begin.filename = node->location.begin.filename; reg->location = node->location; - children.push_back(reg); while (reg->simplify(true, 1, -1, false)) { } + children.push_back(std::move(reg)); } } - AstNode *async_block = NULL; - while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL, async_block)) { } + AstNode* async_block = nullptr; + while (mem2reg_as_needed_pass2(mem2reg_set, this, nullptr, async_block)) { } - vector delnodes; - mem2reg_remove(mem2reg_set, delnodes); - - for (auto node : delnodes) - delete node; + mem2reg_remove(mem2reg_set); } while (simplify(const_fold, 2, width_hint, sign_hint)) { } @@ -1036,8 +1021,6 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin return false; } - current_filename = filename; - // we do not look inside a task or function // (but as soon as a task or function is instantiated we process the generated AST as usual) if (type == AST_FUNCTION || type == AST_TASK) { @@ -1049,22 +1032,22 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { - log_file_warning(filename, location.first_line, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str()); + log_file_warning(*location.begin.filename, location.begin.line, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str()); delete_children(); str = std::string(); } if ((type == AST_TCALL) && - (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || - str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo")) + (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || + str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo")) { if (!current_always) { - log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); + log_file_warning(*location.begin.filename, location.begin.line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); delete_children(); str = std::string(); } else { // simplify the expressions and convert them to a special cell later in genrtlil - for (auto node : children) + for (auto& node : children) while (node->simplify(true, stage, -1, false)) {} if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) { @@ -1103,17 +1086,78 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int counter = 0; label_genblks(existing, counter); std::map this_wire_scope; + + // Process package imports after clearing the scope but before processing module declarations for (size_t i = 0; i < children.size(); i++) { - AstNode *node = children[i]; + AstNode *child = children[i].get(); + if (child->type == AST_IMPORT) { + // Find the package in the design + AstNode *package_node = nullptr; + + // First look in current_ast->children (for packages in same file) + if (current_ast != nullptr) { + for (auto &design_child : current_ast->children) { + if (design_child->type == AST_PACKAGE) { + if (design_child->str == child->str) { + package_node = design_child.get(); + break; + } + } + } + } + + // If not found, look in design->verilog_packages (for packages from other files) + if (!package_node && simplify_design_context != nullptr) { + for (auto &design_package : simplify_design_context->verilog_packages) { + // Handle both with and without leading backslash + std::string package_name = design_package->str; + if (package_name[0] == '\\') { + package_name = package_name.substr(1); + } + if (package_name == child->str || design_package->str == child->str) { + package_node = design_package.get(); + break; + } + } + } + + if (package_node) { + // Import all names from the package into current scope + for (auto& pkg_child : package_node->children) { + if (pkg_child->type == AST_PARAMETER || pkg_child->type == AST_LOCALPARAM || + pkg_child->type == AST_TYPEDEF || pkg_child->type == AST_FUNCTION || + pkg_child->type == AST_TASK || pkg_child->type == AST_ENUM) { + current_scope[pkg_child->str] = pkg_child.get(); + } + if (pkg_child->type == AST_ENUM) { + for (auto& enode : pkg_child->children) { + log_assert(enode->type==AST_ENUM_ITEM); + if (current_scope.count(enode->str) == 0) + current_scope[enode->str] = enode.get(); + else + input_error("enum item %s already exists in current scope\n", enode->str.c_str()); + } + } + } + // Remove the import node since it's been processed + children.erase(children.begin() + i); + i--; // Adjust index since we removed an element + } else { + // If we can't find the package, just remove the import node to avoid errors later + log_warning("Package `%s' not found for import, removing import statement\n", child->str.c_str()); + children.erase(children.begin() + i); + i--; // Adjust index since we removed an element + } + } + } + for (size_t i = 0; i < children.size(); i++) { + AstNode* node = children[i].get(); if (node->type == AST_WIRE) { if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { - for (auto c : node->children[0]->children) { - if (!c->is_simple_const_expr()) { - if (attributes.count(ID::dynports)) - delete attributes.at(ID::dynports); - set_attribute(ID::dynports, AstNode::mkconst_int(1, true)); - } + for (auto& c : node->children[0]->children) { + if (!c->is_simple_const_expr()) + set_attribute(ID::dynports, AstNode::mkconst_int(c->location, 1, true)); } } if (this_wire_scope.count(node->str) > 0) { @@ -1123,16 +1167,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0) goto wires_are_compatible; if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) { - AstNode *r = node->children[0]; + AstNode* r = node->children[0].get(); if (r->range_valid && r->range_left == 0 && r->range_right == 0) { - delete r; node->children.pop_back(); } } if (first_node->children.size() != node->children.size()) goto wires_are_incompatible; for (size_t j = 0; j < node->children.size(); j++) { - AstNode *n1 = first_node->children[j], *n2 = node->children[j]; + auto &n1 = first_node->children[j], &n2 = node->children[j]; if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) { if (n1->range_left != n2->range_left) goto wires_are_incompatible; @@ -1159,13 +1202,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (node->is_signed) first_node->is_signed = true; for (auto &it : node->attributes) { - if (first_node->attributes.count(it.first) > 0) - delete first_node->attributes[it.first]; first_node->set_attribute(it.first, it.second->clone()); } children.erase(children.begin()+(i--)); did_something = true; - delete node; continue; wires_are_incompatible: if (stage > 1) @@ -1183,22 +1223,22 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (node->type == AST_ENUM) { current_scope[node->str] = node; - for (auto enode : node->children) { + for (auto& enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; + current_scope[enode->str] = enode.get(); else input_error("enum item %s already exists\n", enode->str.c_str()); } } } for (size_t i = 0; i < children.size(); i++) { - AstNode *node = children[i]; + auto& node = children[i]; if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF) while (node->simplify(true, 1, -1, false)) did_something = true; if (node->type == AST_ENUM) { - for (auto enode : node->children){ + for (auto& enode : node->children){ log_assert(enode->type==AST_ENUM_ITEM); while (node->simplify(true, 1, -1, false)) did_something = true; @@ -1206,27 +1246,27 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - for (AstNode *child : children) + for (auto& child : children) if (child->type == AST_ALWAYS && child->attributes.count(ID::always_comb)) - check_auto_nosync(child); + check_auto_nosync(child.get()); } // create name resolution entries for all objects with names if (type == AST_PACKAGE) { //add names to package scope for (size_t i = 0; i < children.size(); i++) { - AstNode *node = children[i]; + auto& node = children[i]; // these nodes appear at the top level in a package and can define names if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) { - current_scope[node->str] = node; + current_scope[node->str] = node.get(); } if (node->type == AST_ENUM) { - current_scope[node->str] = node; - for (auto enode : node->children) { + current_scope[node->str] = node.get(); + for (auto& enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; + current_scope[enode->str] = enode.get(); else input_error("enum item %s already exists in package\n", enode->str.c_str()); } @@ -1250,7 +1290,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin current_always_clocked = false; if (type == AST_ALWAYS) - for (auto child : children) { + for (auto& child : children) { if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) current_always_clocked = true; if (child->type == AST_EDGE && GetSize(child->children) == 1 && @@ -1262,7 +1302,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_CELL) { bool lookup_suggested = false; - for (AstNode *child : children) { + for (auto& child : children) { // simplify any parameters to constants if (child->type == AST_PARASET) while (child->simplify(true, 1, -1, false)) { } @@ -1272,7 +1312,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (child->type == AST_ARGUMENT) { if (child->children.size() != 1) continue; - const AstNode *value = child->children[0]; + const auto& value = child->children[0]; if (value->type == AST_IDENTIFIER) { const AstNode *elem = value->id2ast; if (elem == nullptr) { @@ -1289,7 +1329,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // to be indirected to produce an unsigned connection lookup_suggested = true; } - else if (contains_unbased_unsized(value)) + else if (contains_unbased_unsized(value.get())) // unbased unsized literals extend to width of the context lookup_suggested = true; } @@ -1300,7 +1340,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin module = lookup_cell_module(); if (module) { size_t port_counter = 0; - for (AstNode *child : children) { + for (auto& child : children) { if (child->type != AST_ARGUMENT) continue; @@ -1324,39 +1364,44 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin log_assert(child->children.size() <= 1); if (child->children.empty()) continue; - AstNode *arg = child->children[0]; - // plain identifiers never need indirection; this also prevents - // adding infinite levels of indirection - if (arg->type == AST_IDENTIFIER && arg->children.empty()) - continue; + { + auto arg_check = child->children[0].get(); - // only add indirection for standard inputs or outputs - if (ref->port_input == ref->port_output) - continue; + // plain identifiers never need indirection; this also prevents + // adding infinite levels of indirection + if (arg_check->type == AST_IDENTIFIER && arg_check->children.empty()) + continue; + // only add indirection for standard inputs or outputs + if (ref->port_input == ref->port_output) + continue; + } + + auto arg = std::move(child->children[0]); did_something = true; // create the indirection wire std::stringstream sstr; - sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string tmp_str = sstr.str(); - add_wire_for_ref(ref, tmp_str); + add_wire_for_ref(location, ref, tmp_str); - AstNode *asgn = new AstNode(AST_ASSIGN); - current_ast_mod->children.push_back(asgn); + auto asgn_owned = std::make_unique(child->location, AST_ASSIGN); + auto* asgn = asgn_owned.get(); + current_ast_mod->children.push_back(std::move(asgn_owned)); - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(child->location, AST_IDENTIFIER); ident->str = tmp_str; child->children[0] = ident->clone(); if (ref->port_input && !ref->port_output) { - asgn->children.push_back(ident); - asgn->children.push_back(arg); + asgn->children.push_back(std::move(ident)); + asgn->children.push_back(std::move(arg)); } else { log_assert(!ref->port_input && ref->port_output); - asgn->children.push_back(arg); - asgn->children.push_back(ident); + asgn->children.push_back(std::move(arg)); + asgn->children.push_back(std::move(ident)); } asgn->fixup_hierarchy_flags(); } @@ -1416,7 +1461,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_STRUCT: case AST_UNION: if (!basic_prep) { - for (auto *node : children) { + for (auto& node : children) { // resolve any ranges while (!node->basic_prep && node->simplify(true, stage, -1, false)) { did_something = true; @@ -1430,7 +1475,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // instance so add a wire for the packed structure auto wnode = make_packed_struct(this, str, attributes); log_assert(current_ast_mod); - current_ast_mod->children.push_back(wnode); + current_ast_mod->children.push_back(std::move(wnode)); } basic_prep = true; is_custom_type = false; @@ -1456,7 +1501,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_ENUM: //log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep); if (!basic_prep) { - for (auto item_node : children) { + for (auto& item_node : children) { while (!item_node->basic_prep && item_node->simplify(false, stage, -1, false)) did_something = true; } @@ -1473,8 +1518,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin auto item_node = current_scope[children[0]->str]; if (item_node->type == AST_STRUCT || item_node->type == AST_UNION) { set_attribute(ID::wiretype, item_node->clone()); - size_packed_struct(attributes[ID::wiretype], 0); - add_members_to_scope(attributes[ID::wiretype], str); + size_packed_struct(attributes[ID::wiretype].get(), 0); + add_members_to_scope(attributes[ID::wiretype].get(), str); } } while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true) @@ -1502,15 +1547,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin break; case AST_CAST_SIZE: { - int width = 1; - AstNode *node; - AstNode *child = children[0]; - - if (child->type == AST_WIRE) { + if (children[0]->type == AST_WIRE) { + int width = 1; + std::unique_ptr node; + auto* child = children[0].get(); if (child->children.size() == 0) { // Base type (e.g., int) width = child->range_left - child->range_right +1; - node = mkconst_int(width, child->is_signed); + node = mkconst_int(child->location, width, child->is_signed); } else { // User defined type log_assert(child->children[0]->type == AST_WIRETYPE); @@ -1522,7 +1566,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (resolved_type_node->type != AST_TYPEDEF) input_error("`%s' does not name a type\n", type_name.c_str()); log_assert(resolved_type_node->children.size() == 1); - AstNode *template_node = resolved_type_node->children[0]; + auto* template_node = resolved_type_node->children[0].get(); // Ensure typedef itself is fully simplified while (template_node->simplify(const_fold, stage, width_hint, sign_hint)) {}; @@ -1531,9 +1575,9 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin { case AST_WIRE: { if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE) - width = range_width(this, template_node->children[0]); + width = range_width(this, template_node->children[0].get()); child->delete_children(); - node = mkconst_int(width, true); + node = mkconst_int(child->location, width, true); break; } @@ -1541,7 +1585,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_UNION: { child->delete_children(); width = size_packed_struct(template_node, 0); - node = mkconst_int(width, false); + node = mkconst_int(child->location, width, false); break; } @@ -1550,9 +1594,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - delete child; children.erase(children.begin()); - children.insert(children.begin(), node); + children.insert(children.begin(), std::move(node)); } detect_width_simple = true; @@ -1609,7 +1652,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_GT: width_hint = -1; sign_hint = true; - for (auto child : children) { + for (auto& child : children) { while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; child->detectSignWidthWorker(width_hint, sign_hint); @@ -1647,7 +1690,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_REPLICATE) while (children[0]->simplify(true, stage, -1, false) == true) did_something = true; - for (auto child : children) + for (auto& child : children) while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; detectSignWidth(width_hint, sign_hint); @@ -1664,11 +1707,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin bool backup_unevaluated_tern_branch = unevaluated_tern_branch; AstNode *chosen = get_tern_choice().first; - unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2].get(); while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false)) did_something = true; - unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1]; + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1].get(); while (!children[2]->basic_prep && children[2]->simplify(false, stage, -1, false)) did_something = true; @@ -1705,12 +1748,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { children[0]->is_signed = sign_hint; RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint); - std::vector new_children; - new_children.push_back(children[0]); + std::vector> new_children; + new_children.push_back(std::move(children[0])); for (int i = 1; i < GetSize(children); i++) { - AstNode *child = children[i]; + auto& child = children[i]; log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); - for (auto v : child->children) { + for (auto& v : child->children) { if (v->type == AST_DEFAULT) goto keep_const_cond; if (v->type == AST_BLOCK) @@ -1721,8 +1764,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1); log_assert(match.size() == 1); if (match.front() == RTLIL::State::S1) { - while (i+1 < GetSize(children)) - delete children[++i]; + // This is the only reachable case. Skip to the end + i = GetSize(children); goto keep_const_cond; } continue; @@ -1731,9 +1774,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (0) keep_const_cond: - new_children.push_back(child); - else - delete child; + new_children.push_back(std::move(child)); } new_children.swap(children); } @@ -1769,7 +1810,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin flag_autowire = true; if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) { AstNode *chosen = get_tern_choice().first; - unevaluated_tern_branch = chosen && chosen != children[i]; + unevaluated_tern_branch = chosen && chosen != children[i].get(); } while (did_something_here && i < children.size()) { bool const_fold_here = const_fold; @@ -1781,10 +1822,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin const_fold_here = true; if (type == AST_BLOCK) { current_block = this; - current_block_child = children[i]; + current_block_child = children[i].get(); } if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) - current_top_block = children[i]; + current_top_block = children[i].get(); if (i == 0 && child_0_is_self_determined) width_hint_here = -1, sign_hint_here = false; if (i == 1 && child_1_is_self_determined) @@ -1798,7 +1839,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin did_something = true; } if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) { - current_ast_mod->children.push_back(children[i]); + current_ast_mod->children.push_back(std::move(children[i])); children.erase(children.begin() + (i--)); did_something = true; } @@ -1838,14 +1879,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin current_always_clocked = backup_current_always_clocked; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { - if (it->second == NULL) + if (it->second == nullptr) current_scope.erase(it->first); else current_scope[it->first] = it->second; } - current_filename = filename; - if (type == AST_MODULE || type == AST_INTERFACE) current_scope.clear(); @@ -1878,18 +1917,18 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin input_error("Defparam argument `%s . %s` does not match a cell!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); - AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL); + auto paraset = std::make_unique(location, AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : nullptr); paraset->str = paramname; AstNode *cell = current_scope.at(modname); - cell->children.insert(cell->children.begin() + 1, paraset); + cell->children.insert(cell->children.begin() + 1, std::move(paraset)); delete_children(); } // resolve typedefs if (type == AST_TYPEDEF) { log_assert(children.size() == 1); - auto type_node = children[0]; + auto& type_node = children[0]; log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT || type_node->type == AST_UNION); while (type_node->simplify(const_fold, stage, width_hint, sign_hint)) { did_something = true; @@ -1910,7 +1949,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (resolved_type_node->type != AST_TYPEDEF) input_error("`%s' does not name a type\n", type_name.c_str()); log_assert(resolved_type_node->children.size() == 1); - AstNode *template_node = resolved_type_node->children[0]; + auto& template_node = resolved_type_node->children[0]; // Resolve the typedef from the bottom up, recursing within the current // block of code. Defer further simplification until the complete type is @@ -1919,10 +1958,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (!str.empty() && str[0] == '\\' && (template_node->type == AST_STRUCT || template_node->type == AST_UNION)) { // replace instance with wire representing the packed structure - newNode = make_packed_struct(template_node, str, attributes); - if (newNode->attributes.count(ID::wiretype)) - delete newNode->attributes[ID::wiretype]; - newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + newNode = make_packed_struct(template_node.get(), str, attributes); + newNode->set_attribute(ID::wiretype, mkconst_str(newNode->location, resolved_type_node->str)); // add original input/output attribute to resolved wire newNode->is_input = this->is_input; newNode->is_output = this->is_output; @@ -1933,9 +1970,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // Prepare replacement node. newNode = template_node->clone(); newNode->str = str; - if (newNode->attributes.count(ID::wiretype)) - delete newNode->attributes[ID::wiretype]; - newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + newNode->set_attribute(ID::wiretype, mkconst_str(newNode->location, resolved_type_node->str)); newNode->is_input = is_input; newNode->is_output = is_output; newNode->is_wand = is_wand; @@ -1944,7 +1979,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin newNode->set_attribute(pair.first, pair.second->clone()); // if an enum then add attributes to support simulator tracing - newNode->annotateTypedEnums(template_node); + newNode->annotateTypedEnums(template_node.get()); bool add_packed_dimensions = (type == AST_WIRE && GetSize(children) > 1) || (type == AST_MEMORY && GetSize(children) > 2); @@ -1954,20 +1989,20 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // Add packed dimensions. if (add_packed_dimensions) { - AstNode *packed = children[1]; + auto& packed = children[1]; if (newNode->children.empty()) newNode->children.insert(newNode->children.begin(), packed->clone()); else - prepend_ranges(newNode->children[0], packed); + prepend_ranges(newNode->children[0], packed.get()); } // Add unpacked dimensions. if (type == AST_MEMORY) { - AstNode *unpacked = children.back(); + auto& unpacked = children.back(); if (GetSize(newNode->children) < 2) newNode->children.push_back(unpacked->clone()); else - prepend_ranges(newNode->children[1], unpacked); + prepend_ranges(newNode->children[1], unpacked.get()); newNode->type = type; } @@ -1988,11 +2023,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // Pretend it's just a wire in order to resolve the type in the code block above. AstNodeType param_type = type; type = AST_WIRE; - AstNode *expr = children[0]; + auto expr = std::move(children.front()); children.erase(children.begin()); while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {}; type = param_type; - children.insert(children.begin(), expr); + children.insert(children.begin(), std::move(expr)); if (children[1]->type == AST_MEMORY) input_error("unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); @@ -2005,7 +2040,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // resolve constant prefixes if (type == AST_PREFIX) { if (children[0]->type != AST_CONSTANT) { - // dumpAst(NULL, "> "); + // dumpAst(nullptr, "> "); input_error("Index in generate block prefix syntax is not constant!\n"); } if (children[1]->type == AST_PREFIX) @@ -2026,7 +2061,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (children[1]->type != AST_CONSTANT) input_error("Right operand of to_bits expression is not constant!\n"); RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed); - newNode = mkconst_bits(new_value.to_bits(), children[1]->is_signed); + newNode = mkconst_bits(location, new_value.to_bits(), children[1]->is_signed); goto apply_newNode; } @@ -2070,13 +2105,13 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin range_right = children[0]->range_right; bool force_upto = false, force_downto = false; if (attributes.count(ID::force_upto)) { - AstNode *val = attributes[ID::force_upto]; + auto* val = attributes[ID::force_upto].get(); if (val->type != AST_CONSTANT) input_error("Attribute `force_upto' with non-constant value!\n"); force_upto = val->asAttrConst().as_bool(); } if (attributes.count(ID::force_downto)) { - AstNode *val = attributes[ID::force_downto]; + auto* val = attributes[ID::force_downto].get(); if (val->type != AST_CONSTANT) input_error("Attribute `force_downto' with non-constant value!\n"); force_downto = val->asAttrConst().as_bool(); @@ -2088,7 +2123,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin range_swapped = force_upto; } if (range_left == range_right && !attributes.count(ID::single_bit_vector)) - set_attribute(ID::single_bit_vector, mkconst_int(1, false)); + set_attribute(ID::single_bit_vector, mkconst_int(location, 1, false)); } } else { if (!range_valid) @@ -2097,10 +2132,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin range_swapped = false; range_left = 0; range_right = 0; - if (attributes.count(ID::single_bit_vector)) { - delete attributes[ID::single_bit_vector]; - attributes.erase(ID::single_bit_vector); - } + attributes.erase(ID::single_bit_vector); } } @@ -2111,19 +2143,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (int i = std::min(GetSize(children), 2) - 1; i >= 0; i--) { if (children[i]->type == AST_MULTIRANGE) { int width = 1; - for (auto range : children[i]->children) { - width *= add_dimension(this, range); + for (auto& range : children[i]->children) { + width *= add_dimension(this, range.get()); if (i) unpacked_dimensions++; } - delete children[i]; int left = width - 1, right = 0; if (i) std::swap(left, right); - children[i] = new AstNode(AST_RANGE, mkconst_int(left, true), mkconst_int(right, true)); + auto loc = children[i]->location; + children[i] = std::make_unique(loc, AST_RANGE, mkconst_int(loc, left, true), mkconst_int(loc, right, true)); fixup_hierarchy_flags(); did_something = true; } else if (children[i]->type == AST_RANGE) { - add_dimension(this, children[i]); + add_dimension(this, children[i].get()); if (i) unpacked_dimensions++; } } @@ -2135,15 +2167,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // Resolve multidimensional array access. if (type == AST_IDENTIFIER && !basic_prep && id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_MEMORY) && - children.size() > 0 && (children[0]->type == AST_RANGE || children[0]->type == AST_MULTIRANGE)) + children.size() > 0 && (children[0]->type == AST_RANGE || children[0]->type == AST_MULTIRANGE)) { int dims_sel = children[0]->type == AST_MULTIRANGE ? children[0]->children.size() : 1; // Save original number of dimensions for $size() etc. integer = dims_sel; // Split access into unpacked and packed parts. - AstNode *unpacked_range = nullptr; - AstNode *packed_range = nullptr; + std::unique_ptr unpacked_range = nullptr; + std::unique_ptr packed_range = nullptr; if (id2ast->unpacked_dimensions) { if (id2ast->unpacked_dimensions > 1) { @@ -2151,8 +2183,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin unpacked_range = make_index_range(id2ast, true); } else { // Index into one-dimensional unpacked part; unlink simple range node. - AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0]; - unpacked_range = range; + auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0]; + unpacked_range = std::move(range); range = nullptr; } } @@ -2163,21 +2195,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin packed_range = make_index_range(id2ast, false); } else { // Index into one-dimensional packed part; unlink simple range node. - AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0]; - packed_range = range; + auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0]; + packed_range = std::move(range); range = nullptr; } } - for (auto &it : children) - delete it; children.clear(); if (unpacked_range) - children.push_back(unpacked_range); + children.push_back(std::move(unpacked_range)); if (packed_range) - children.push_back(packed_range); + children.push_back(std::move(packed_range)); fixup_hierarchy_flags(); basic_prep = true; @@ -2192,10 +2222,9 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int width = std::abs(children[1]->range_left - children[1]->range_right) + 1; if (children[0]->type == AST_REALVALUE) { RTLIL::Const constvalue = children[0]->realAsConst(width); - log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", + log_file_warning(*location.begin.filename, location.begin.line, "converting real value %e to binary %s.\n", children[0]->realvalue, log_signal(constvalue)); - delete children[0]; - children[0] = mkconst_bits(constvalue.to_bits(), sign_hint); + children[0] = mkconst_bits(location, constvalue.to_bits(), sign_hint); fixup_hierarchy_flags(); did_something = true; } @@ -2203,9 +2232,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (width != int(children[0]->bits.size())) { RTLIL::SigSpec sig(children[0]->bits); sig.extend_u0(width, children[0]->is_signed); - AstNode *old_child_0 = children[0]; - children[0] = mkconst_bits(sig.as_const().to_bits(), is_signed); - delete old_child_0; + children[0] = mkconst_bits(location, sig.as_const().to_bits(), is_signed); fixup_hierarchy_flags(); } children[0]->is_signed = is_signed; @@ -2217,8 +2244,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } else if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) { double as_realvalue = children[0]->asReal(sign_hint); - delete children[0]; - children[0] = new AstNode(AST_REALVALUE); + children[0] = std::make_unique(location, AST_REALVALUE); children[0]->realvalue = as_realvalue; fixup_hierarchy_flags(); did_something = true; @@ -2247,7 +2273,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (found_sname) { // structure member, rewrite this node to reference the packed struct wire auto range = make_index_range(item_node); - newNode = new AstNode(AST_IDENTIFIER, range); + newNode = std::make_unique(location, AST_IDENTIFIER, std::move(range)); newNode->str = sname; // save type and original number of dimensions for $size() etc. newNode->set_attribute(ID::wiretype, item_node->clone()); @@ -2259,7 +2285,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } newNode->basic_prep = true; if (item_node->is_signed) - newNode = new AstNode(AST_TO_SIGNED, newNode); + newNode = std::make_unique(location, AST_TO_SIGNED, std::move(newNode)); goto apply_newNode; } } @@ -2270,7 +2296,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; str = try_pop_module_prefix(); - for (auto node : current_scope_ast->children) { + for (auto& node : current_scope_ast->children) { //log("looking at mod scope child %s\n", type2str(node->type).c_str()); switch (node->type) { case AST_PARAMETER: @@ -2285,16 +2311,16 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin //log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str()); if (str == node->str) { //log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str()); - current_scope[node->str] = node; + current_scope[node->str] = node.get(); } break; case AST_ENUM: - current_scope[node->str] = node; - for (auto enum_node : node->children) { + current_scope[node->str] = node.get(); + for (auto& enum_node : node->children) { log_assert(enum_node->type==AST_ENUM_ITEM); if (str == enum_node->str) { //log("\nadding enum item %s to scope\n", str.c_str()); - current_scope[str] = enum_node; + current_scope[str] = enum_node.get(); } } break; @@ -2307,10 +2333,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (current_ast_mod == nullptr) { input_error("Identifier `%s' is implicitly declared outside of a module.\n", str.c_str()); } else if (flag_autowire || str == "\\$global_clock") { - AstNode *auto_wire = new AstNode(AST_AUTOWIRE); + auto auto_wire = std::make_unique(location, AST_AUTOWIRE); auto_wire->str = str; - current_ast_mod->children.push_back(auto_wire); - current_scope[str] = auto_wire; + current_scope[str] = auto_wire.get(); + current_ast_mod->children.push_back(std::move(auto_wire)); did_something = true; } else { input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); @@ -2325,7 +2351,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // split memory access with bit select to individual statements if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2) { - if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1) + if (id2ast == nullptr || id2ast->type != AST_MEMORY || children[0]->children.size() != 1) input_error("Invalid bit-select on memory access!\n"); int mem_width, mem_size, addr_bits; @@ -2338,41 +2364,41 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::swap(data_range_left, data_range_right); std::stringstream sstr; - sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string wire_id = sstr.str(); - AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); + auto wire_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, data_range_left, true), mkconst_int(location, data_range_right, true))); + auto* wire = wire_owned.get(); + current_ast_mod->children.push_back(std::move(wire_owned)); wire->str = wire_id; if (current_block) - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - current_ast_mod->children.push_back(wire); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *data = clone(); - delete data->children[1]; + auto data = clone(); data->children.pop_back(); - AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data); + auto assign = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), std::move(data)); assign->children[0]->str = wire_id; assign->children[0]->was_checked = true; if (current_block) { size_t assign_idx = 0; - while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child) + while (assign_idx < current_block->children.size() && current_block->children[assign_idx].get() != current_block_child) assign_idx++; log_assert(assign_idx < current_block->children.size()); - current_block->children.insert(current_block->children.begin()+assign_idx, assign); + current_block->children.insert(current_block->children.begin()+assign_idx, std::move(assign)); wire->is_reg = true; } else { - AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - proc->children[0]->children.push_back(assign); - current_ast_mod->children.push_back(proc); + auto proc = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK)); + proc->children[0]->children.push_back(std::move(assign)); + current_ast_mod->children.push_back(std::move(proc)); } - newNode = new AstNode(AST_IDENTIFIER, children[1]->clone()); + newNode = std::make_unique(location, AST_IDENTIFIER, children[1]->clone()); newNode->str = wire_id; newNode->integer = integer; // save original number of dimensions for $size() etc. newNode->id2ast = wire; @@ -2384,8 +2410,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_REPEAT) { - AstNode *count = children[0]; - AstNode *body = children[1]; + auto count = std::move(children[0]); + auto body = std::move(children[1]); // eval count expression while (count->simplify(true, stage, 32, true)) { } @@ -2399,22 +2425,20 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (int i = 0; i < count->bitsAsConst().as_int(); i++) children.insert(children.begin(), body->clone()); - delete count; - delete body; did_something = true; } // unroll for loops and generate-for blocks if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0) { - AstNode *init_ast = children[0]; - AstNode *while_ast = children[1]; - AstNode *next_ast = children[2]; - AstNode *body_ast = children[3]; + auto& init_ast = children[0]; + auto& while_ast = children[1]; + auto& next_ast = children[2]; + auto* body_ast = children[3].get(); while (body_ast->type == AST_GENBLOCK && body_ast->str.empty() && body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK) - body_ast = body_ast->children.at(0); + body_ast = body_ast->children.at(0).get(); const char* loop_type_str = "procedural"; const char* var_type_str = "register"; @@ -2430,16 +2454,16 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (next_ast->type != AST_ASSIGN_EQ) input_error("Unsupported 3rd expression of %s for-loop!\n", loop_type_str); - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type) + if (init_ast->children[0]->id2ast == nullptr || init_ast->children[0]->id2ast->type != var_type) input_error("Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type) + if (next_ast->children[0]->id2ast == nullptr || next_ast->children[0]->id2ast->type != var_type) input_error("Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) input_error("Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); // eval 1st expression - AstNode *varbuf = init_ast->children[1]->clone(); + auto varbuf = init_ast->children[1]->clone(); { int expr_width_hint = -1; bool expr_sign_hint = true; @@ -2464,23 +2488,23 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - varbuf = new AstNode(AST_LOCALPARAM, varbuf); + varbuf = std::make_unique(location, AST_LOCALPARAM, std::move(varbuf)); varbuf->str = init_ast->children[0]->str; AstNode *backup_scope_varbuf = current_scope[varbuf->str]; - current_scope[varbuf->str] = varbuf; + current_scope[varbuf->str] = varbuf.get(); size_t current_block_idx = 0; if (type == AST_FOR) { while (current_block_idx < current_block->children.size() && - current_block->children[current_block_idx] != current_block_child) + current_block->children[current_block_idx].get() != current_block_child) current_block_idx++; } while (1) { // eval 2nd expression - AstNode *buf = while_ast->clone(); + auto buf = while_ast->clone(); { int expr_width_hint = -1; bool expr_sign_hint = true; @@ -2492,10 +2516,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin input_error("2nd expression of %s for-loop is not constant!\n", loop_type_str); if (buf->integer == 0) { - delete buf; break; } - delete buf; // expand body int index = varbuf->children[0]->integer; @@ -2508,27 +2530,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::string prefix = sstr.str(); // create a scoped localparam for the current value of the loop variable - AstNode *local_index = varbuf->clone(); + auto local_index = varbuf->clone(); size_t pos = local_index->str.rfind('.'); if (pos != std::string::npos) // remove outer prefix local_index->str = "\\" + local_index->str.substr(pos + 1); local_index->str = prefix_id(prefix, local_index->str); - current_scope[local_index->str] = local_index; - current_ast_mod->children.push_back(local_index); + current_scope[local_index->str] = local_index.get(); + current_ast_mod->children.push_back(std::move(local_index)); buf->expand_genblock(prefix); if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { buf->children[i]->simplify(const_fold, stage, -1, false); - current_ast_mod->children.push_back(buf->children[i]); + current_ast_mod->children.push_back(std::move(buf->children[i])); } } else { for (size_t i = 0; i < buf->children.size(); i++) - current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]); + current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf->children[i])); } buf->children.clear(); - delete buf; // eval 3rd expression buf = next_ast->children[1]->clone(); @@ -2543,19 +2564,16 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (buf->type != AST_CONSTANT) input_error("Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); - delete varbuf->children[0]; - varbuf->children[0] = buf; + varbuf->children[0] = std::move(buf); } if (type == AST_FOR) { - AstNode *buf = next_ast->clone(); - delete buf->children[1]; + auto buf = next_ast->clone(); buf->children[1] = varbuf->children[0]->clone(); - current_block->children.insert(current_block->children.begin() + current_block_idx++, buf); + current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf)); } current_scope[varbuf->str] = backup_scope_varbuf; - delete varbuf; delete_children(); did_something = true; } @@ -2566,7 +2584,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) { - log_assert(!VERILOG_FRONTEND::sv_mode); + log_assert(!sv_mode_but_global_and_used_for_literally_one_condition); children[i]->input_error("Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); } } @@ -2581,19 +2599,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin && is_autonamed_block(str)) // track local variables in this block so we can consider adding // nosync once the block has been fully elaborated - for (AstNode *child : children) + for (auto& child : children) if (child->type == AST_WIRE && !child->attributes.count(ID::nosync)) - mark_auto_nosync(this, child); + mark_auto_nosync(this, child.get()); - std::vector new_children; + std::vector> new_children; for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) { children[i]->simplify(false, stage, -1, false); - current_ast_mod->children.push_back(children[i]); - current_scope[children[i]->str] = children[i]; + current_scope[children[i]->str] = children[i].get(); + current_ast_mod->children.push_back(std::move(children[i])); } else - new_children.push_back(children[i]); + new_children.push_back(std::move(children[i])); children.swap(new_children); did_something = true; @@ -2609,7 +2627,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (size_t i = 0; i < children.size(); i++) { children[i]->simplify(const_fold, stage, -1, false); - current_ast_mod->children.push_back(children[i]); + current_ast_mod->children.push_back(std::move(children[i])); } children.clear(); @@ -2619,7 +2637,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // simplify generate-if blocks if (type == AST_GENIF && children.size() != 0) { - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) @@ -2627,17 +2645,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin input_error("Condition for generate if is not constant!\n"); } if (buf->asBool() != 0) { - delete buf; buf = children[1]->clone(); } else { - delete buf; - buf = children.size() > 2 ? children[2]->clone() : NULL; + buf = children.size() > 2 ? children[2]->clone() : nullptr; } if (buf) { if (buf->type != AST_GENBLOCK) - buf = new AstNode(AST_GENBLOCK, buf); + buf = std::make_unique(location, AST_GENBLOCK, std::move(buf)); if (!buf->str.empty()) { buf->expand_genblock(buf->str + "."); @@ -2645,11 +2661,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (size_t i = 0; i < buf->children.size(); i++) { buf->children[i]->simplify(const_fold, stage, -1, false); - current_ast_mod->children.push_back(buf->children[i]); + current_ast_mod->children.push_back(std::move(buf->children[i])); } buf->children.clear(); - delete buf; } delete_children(); @@ -2659,7 +2674,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // simplify generate-case blocks if (type == AST_GENCASE && children.size() != 0) { - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) @@ -2669,24 +2684,23 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin bool ref_signed = buf->is_signed; RTLIL::Const ref_value = buf->bitsAsConst(); - delete buf; - AstNode *selected_case = NULL; + AstNode *selected_case = nullptr; for (size_t i = 1; i < children.size(); i++) { log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ); - AstNode *this_genblock = NULL; - for (auto child : children.at(i)->children) { - log_assert(this_genblock == NULL); + AstNode *this_genblock = nullptr; + for (auto& child : children.at(i)->children) { + log_assert(this_genblock == nullptr); if (child->type == AST_GENBLOCK) - this_genblock = child; + this_genblock = child.get(); } - for (auto child : children.at(i)->children) + for (auto& child : children.at(i)->children) { if (child->type == AST_DEFAULT) { - if (selected_case == NULL) + if (selected_case == nullptr) selected_case = this_genblock; continue; } @@ -2703,7 +2717,6 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); - delete buf; if (is_selected) { selected_case = this_genblock; @@ -2713,7 +2726,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - if (selected_case != NULL) + if (selected_case != nullptr) { log_assert(selected_case->type == AST_GENBLOCK); buf = selected_case->clone(); @@ -2724,11 +2737,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin for (size_t i = 0; i < buf->children.size(); i++) { buf->children[i]->simplify(const_fold, stage, -1, false); - current_ast_mod->children.push_back(buf->children[i]); + current_ast_mod->children.push_back(std::move(buf->children[i])); } buf->children.clear(); - delete buf; } delete_children(); @@ -2741,13 +2753,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (!children.at(0)->range_valid) input_error("Non-constant array range on cell array.\n"); - newNode = new AstNode(AST_GENBLOCK); + newNode = std::make_unique(location, AST_GENBLOCK); int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1; for (int i = 0; i < num; i++) { int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; - AstNode *new_cell = children.at(1)->clone(); - newNode->children.push_back(new_cell); + auto new_cell_owned = children.at(1)->clone(); + auto* new_cell = new_cell_owned.get(); + newNode->children.push_back(std::move(new_cell_owned)); new_cell->str += stringf("[%d]", idx); if (new_cell->type == AST_PRIMITIVE) { input_error("Cell arrays of primitives are currently not supported.\n"); @@ -2767,13 +2780,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (children.size() < 2) input_error("Insufficient number of arguments for primitive `%s'!\n", str.c_str()); - std::vector children_list; - for (auto child : children) { + std::vector> children_list; + for (auto& child : children) { log_assert(child->type == AST_ARGUMENT); log_assert(child->children.size() == 1); - children_list.push_back(child->children[0]); + children_list.push_back(std::move(child->children[0])); child->children.clear(); - delete child; } children.clear(); @@ -2784,39 +2796,38 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::vector z_const(1, RTLIL::State::Sz); - AstNode *mux_input = children_list.at(1); + auto& mux_input = children_list.at(1); if (str == "notif0" || str == "notif1") { - mux_input = new AstNode(AST_BIT_NOT, mux_input); + mux_input = std::make_unique(location, AST_BIT_NOT, std::move(mux_input)); } - AstNode *node = new AstNode(AST_TERNARY, children_list.at(2)); + auto node = std::make_unique(location, AST_TERNARY, std::move(children_list.at(2))); if (str == "bufif0") { - node->children.push_back(AstNode::mkconst_bits(z_const, false)); - node->children.push_back(mux_input); + node->children.push_back(AstNode::mkconst_bits(location, z_const, false)); + node->children.push_back(std::move(mux_input)); } else { - node->children.push_back(mux_input); - node->children.push_back(AstNode::mkconst_bits(z_const, false)); + node->children.push_back(std::move(mux_input)); + node->children.push_back(AstNode::mkconst_bits(location, z_const, false)); } str.clear(); type = AST_ASSIGN; - children.push_back(children_list.at(0)); + children.push_back(std::move(children_list.at(0))); children.back()->was_checked = true; - children.push_back(node); + children.push_back(std::move(node)); fixup_hierarchy_flags(); did_something = true; } else if (str == "buf" || str == "not") { - AstNode *input = children_list.back(); + auto& input = children_list.back(); if (str == "not") - input = new AstNode(AST_BIT_NOT, input); + input = std::make_unique(location, AST_BIT_NOT, std::move(input)); - newNode = new AstNode(AST_GENBLOCK); + newNode = std::make_unique(location, AST_GENBLOCK); for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) { - newNode->children.push_back(new AstNode(AST_ASSIGN, *it, input->clone())); + newNode->children.push_back(std::make_unique(location, AST_ASSIGN, std::move(*it), input->clone())); newNode->children.back()->was_checked = true; } - delete input; did_something = true; } @@ -2839,20 +2850,20 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin op_type = AST_BIT_XOR, invert_results = true; log_assert(op_type != AST_NONE); - AstNode *node = children_list[1]; + auto& node = children_list[1]; if (op_type != AST_POS) for (size_t i = 2; i < children_list.size(); i++) { - node = new AstNode(op_type, node, children_list[i]); + node = std::make_unique(location, op_type, std::move(node), std::move(children_list[i])); node->location = location; } if (invert_results) - node = new AstNode(AST_BIT_NOT, node); + node = std::make_unique(location, AST_BIT_NOT, std::move(node)); str.clear(); type = AST_ASSIGN; - children.push_back(children_list[0]); + children.push_back(std::move(children_list[0])); children.back()->was_checked = true; - children.push_back(node); + children.push_back(std::move(node)); fixup_hierarchy_flags(); did_something = true; } @@ -2867,7 +2878,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin goto skip_dynamic_range_lvalue_expansion; if (children[0]->children[0]->range_valid || did_something) goto skip_dynamic_range_lvalue_expansion; - if (children[0]->id2ast == NULL || children[0]->id2ast->type != AST_WIRE) + if (children[0]->id2ast == nullptr || children[0]->id2ast->type != AST_WIRE) goto skip_dynamic_range_lvalue_expansion; if (!children[0]->id2ast->range_valid) goto skip_dynamic_range_lvalue_expansion; @@ -2879,10 +2890,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int wire_offset = children[0]->id2ast->range_right; int result_width = 1; - AstNode *shift_expr = NULL; - AstNode *range = children[0]->children[0]; + std::unique_ptr shift_expr = nullptr; + auto& range = children[0]->children[0]; - if (!try_determine_range_width(range, result_width)) + if (!try_determine_range_width(range.get(), result_width)) input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); if (range->children.size() >= 2) @@ -2926,14 +2937,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin AstNode *lsb_expr = shift_expr->type == AST_ADD && shift_expr->children[0]->type == AST_SELFSZ && shift_expr->children[1]->type == AST_CONSTANT && shift_expr->children[1]->integer == 0 ? - shift_expr->children[0]->children[0] : - shift_expr; + shift_expr->children[0]->children[0].get() : + shift_expr.get(); // Extract stride from indexing of two-dimensional packed arrays and // variable slices on the form dst[i*stride +: width] = src. if (lsb_expr->type == AST_MUL && - (lsb_expr->children[0]->type == AST_CONSTANT || - lsb_expr->children[1]->type == AST_CONSTANT)) + (lsb_expr->children[0]->type == AST_CONSTANT || + lsb_expr->children[1]->type == AST_CONSTANT)) { int stride_ix = lsb_expr->children[1]->type == AST_CONSTANT; stride = (int)lsb_expr->children[stride_ix]->integer; @@ -2965,9 +2976,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // // long long is at least 64 bits in C++11 long long shift_mod = 1ll << (max_width - case_sign_hint); - // std::gcd requires C++17 - // bitno_div = std::gcd(stride, shift_mod); - bitno_div = gcd((long long)stride, shift_mod); + bitno_div = std::gcd((long long)stride, shift_mod); } } } @@ -2981,11 +2990,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int rvalue_width; bool rvalue_sign; children[1]->detectSignWidth(rvalue_width, rvalue_sign); - AstNode *rvalue = mktemp_logic("$bitselwrite$rvalue$", current_ast_mod, true, rvalue_width - 1, 0, rvalue_sign); - AstNode *caseNode = new AstNode(AST_CASE, shift_expr); - newNode = new AstNode(AST_BLOCK, - new AstNode(AST_ASSIGN_EQ, rvalue, children[1]->clone()), - caseNode); + auto rvalue = mktemp_logic(location, "$bitselwrite$rvalue$", current_ast_mod, true, rvalue_width - 1, 0, rvalue_sign); + auto* rvalue_leaky = rvalue.get(); + log("make 1\n"); + auto case_node_owned = std::make_unique(location, AST_CASE, std::move(shift_expr)); + auto* case_node = case_node_owned.get(); + newNode = std::make_unique(location, AST_BLOCK, + std::make_unique(location, AST_ASSIGN_EQ, std::move(rvalue), children[1]->clone()), + std::move(case_node_owned)); did_something = true; for (int i = 1 - result_width; i < wire_width; i++) { @@ -2998,26 +3010,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (start_bit%bitno_div != 0 || (stride == 0 && start_bit != 0)) continue; - AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, case_sign_hint, max_width)); - AstNode *lvalue = children[0]->clone(); + auto cond = std::make_unique(location, AST_COND, mkconst_int(location, start_bit, case_sign_hint, max_width)); + auto lvalue = children[0]->clone(); lvalue->delete_children(); if (member_node) lvalue->set_attribute(ID::wiretype, member_node->clone()); - lvalue->children.push_back(new AstNode(AST_RANGE, - mkconst_int(end_bit, true), mkconst_int(start_bit, true))); - cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, rvalue->clone()))); - caseNode->children.push_back(cond); + lvalue->children.push_back(std::make_unique(location, AST_RANGE, + mkconst_int(location, end_bit, true), mkconst_int(location, start_bit, true))); + cond->children.push_back(std::make_unique(location, AST_BLOCK, std::make_unique(location, std::move(type), std::move(lvalue), rvalue_leaky->clone()))); + case_node->children.push_back(std::move(cond)); } } else { // mask and shift operations // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) - AstNode *lvalue = children[0]->clone(); + auto lvalue = children[0]->clone(); lvalue->delete_children(); if (member_node) lvalue->set_attribute(ID::wiretype, member_node->clone()); - AstNode *old_data = lvalue->clone(); + auto old_data = lvalue->clone(); if (type == AST_ASSIGN_LE) old_data->lookahead = true; @@ -3026,51 +3038,52 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint); // All operations are carried out in a new block. - newNode = new AstNode(AST_BLOCK); + newNode = std::make_unique(location, AST_BLOCK); // Temporary register holding the result of the bit- or part-select position expression. - AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint); - newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr)); - + auto pos = mktemp_logic(location, "$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint); // Calculate lsb from position. - AstNode *shift_val = pos->clone(); + auto shift_val = pos->clone(); + + newNode->children.push_back(std::make_unique(location, AST_ASSIGN_EQ, std::move(pos), std::move(shift_expr))); // If the expression is signed, we must add an extra bit for possible negation of the most negative number. // If the expression is unsigned, we must add an extra bit for sign. - shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val); + shift_val = std::make_unique(location, AST_CAST_SIZE, mkconst_int(location, shift_width_hint + 1, true), std::move(shift_val)); if (!shift_sign_hint) - shift_val = new AstNode(AST_TO_SIGNED, shift_val); + shift_val = std::make_unique(location, AST_TO_SIGNED, std::move(shift_val)); // offset the shift amount by the lower bound of the dimension if (wire_offset != 0) - shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true)); + shift_val = std::make_unique(location, AST_SUB, std::move(shift_val), mkconst_int(location, wire_offset, true)); // reflect the shift amount if the dimension is swapped if (children[0]->id2ast->range_swapped) - shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val); + shift_val = std::make_unique(location, AST_SUB, mkconst_int(location, wire_width - result_width, true), std::move(shift_val)); // AST_SHIFT uses negative amounts for shifting left - shift_val = new AstNode(AST_NEG, shift_val); + shift_val = std::make_unique(location, AST_NEG, std::move(shift_val)); + auto also_shift_val = shift_val->clone(); // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) did_something = true; - AstNode *bitmask = mkconst_bits(std::vector(result_width, State::S1), false); + auto bitmask = mkconst_bits(location, std::vector(result_width, State::S1), false); newNode->children.push_back( - new AstNode(type, - lvalue, - new AstNode(AST_BIT_OR, - new AstNode(AST_BIT_AND, - old_data, - new AstNode(AST_BIT_NOT, - new AstNode(AST_SHIFT, - bitmask, - shift_val->clone()))), - new AstNode(AST_SHIFT, - new AstNode(AST_TO_UNSIGNED, - new AstNode(AST_CAST_SIZE, - mkconst_int(result_width, true), - children[1]->clone())), - shift_val)))); + std::make_unique(location, std::move(type), + std::move(lvalue), + std::make_unique(location, AST_BIT_OR, + std::make_unique(location, AST_BIT_AND, + std::move(old_data), + std::make_unique(location, AST_BIT_NOT, + std::make_unique(location, AST_SHIFT, + std::move(bitmask), + std::move(shift_val)))), + std::make_unique(location, AST_SHIFT, + std::make_unique(location, AST_TO_UNSIGNED, + std::make_unique(location, AST_CAST_SIZE, + mkconst_int(location, result_width, true), + children[1]->clone())), + std::move(also_shift_val))))); newNode->fixup_hierarchy_flags(true); } @@ -3080,11 +3093,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin skip_dynamic_range_lvalue_expansion:; // found right-hand side identifier for memory -> replace with memory read port - if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && + if (stage > 1 && type == AST_IDENTIFIER && id2ast != nullptr && id2ast->type == AST_MEMORY && !in_lvalue && children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { if (integer < (unsigned)id2ast->unpacked_dimensions) input_error("Insufficient number of array indices for %s.\n", log_id(str)); - newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); + newNode = std::make_unique(location, AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; goto apply_newNode; @@ -3095,39 +3108,41 @@ skip_dynamic_range_lvalue_expansion:; { bool found_nontrivial_member = false; - for (auto child : children[0]->children) { - if (child->type == AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == AST_MEMORY) + for (auto& child : children[0]->children) { + if (child->type == AST_IDENTIFIER && child->id2ast != nullptr && child->id2ast->type == AST_MEMORY) found_nontrivial_member = true; } if (found_nontrivial_member) { - newNode = new AstNode(AST_BLOCK); + newNode = std::make_unique(location, AST_BLOCK); - AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); - current_ast_mod->children.push_back(wire_tmp); + auto wire_tmp_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, width_hint-1, true), mkconst_int(location, 0, true))); + auto wire_tmp = wire_tmp_owned.get(); + wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line, autoidx++); current_scope[wire_tmp->str] = wire_tmp; - wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + current_ast_mod->children.push_back(std::move(wire_tmp_owned)); + wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_tmp->simplify(true, 1, -1, false)) { } wire_tmp->is_logic = true; - AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); + auto wire_tmp_id_owned = std::make_unique(location, AST_IDENTIFIER); + auto* wire_tmp_id = wire_tmp_id_owned.get(); wire_tmp_id->str = wire_tmp->str; - newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone())); + newNode->children.push_back(std::make_unique(location, AST_ASSIGN_EQ, std::move(wire_tmp_id_owned), children[1]->clone())); newNode->children.back()->was_checked = true; int cursor = 0; - for (auto child : children[0]->children) + for (auto& child : children[0]->children) { int child_width_hint = -1; bool child_sign_hint = true; child->detectSignWidth(child_width_hint, child_sign_hint); - AstNode *rhs = wire_tmp_id->clone(); - rhs->children.push_back(new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+child_width_hint-1, true), AstNode::mkconst_int(cursor, true))); - newNode->children.push_back(new AstNode(type, child->clone(), rhs)); + auto rhs = wire_tmp_id->clone(); + rhs->children.push_back(std::make_unique(location, AST_RANGE, AstNode::mkconst_int(location, cursor+child_width_hint-1, true), AstNode::mkconst_int(location, cursor, true))); + newNode->children.push_back(std::make_unique(location, type, child->clone(), std::move(rhs))); cursor += child_width_hint; } @@ -3146,15 +3161,15 @@ skip_dynamic_range_lvalue_expansion:; input_error("Insufficient number of array indices for %s.\n", log_id(str)); std::stringstream sstr; - sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; int mem_width, mem_size, addr_bits; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); - newNode = new AstNode(AST_BLOCK); - AstNode *defNode = new AstNode(AST_BLOCK); + newNode = std::make_unique(location, AST_BLOCK); + auto defNode = std::make_unique(location, AST_BLOCK); int data_range_left = children[0]->id2ast->children[0]->range_left; int data_range_right = children[0]->id2ast->children[0]->range_right; @@ -3173,74 +3188,75 @@ skip_dynamic_range_lvalue_expansion:; for (int i = 0; i < mem_width; i++) set_bits_en.push_back(RTLIL::State::S1); - AstNode *node_addr = nullptr; + std::unique_ptr node_addr = nullptr; if (children[0]->children[0]->children[0]->isConst()) { node_addr = children[0]->children[0]->children[0]->clone(); } else { - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + auto wire_addr_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 0, true))); + auto* wire_addr = wire_addr_owned.get(); wire_addr->str = id_addr; wire_addr->was_checked = true; - current_ast_mod->children.push_back(wire_addr); + current_ast_mod->children.push_back(std::move(wire_addr_owned)); current_scope[wire_addr->str] = wire_addr; while (wire_addr->simplify(true, 1, -1, false)) { } - AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); + auto assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, x_bits_addr, false)); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; - defNode->children.push_back(assign_addr); + defNode->children.push_back(std::move(assign_addr)); - assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; - newNode->children.push_back(assign_addr); + newNode->children.push_back(std::move(assign_addr)); - node_addr = new AstNode(AST_IDENTIFIER); + node_addr = std::make_unique(location, AST_IDENTIFIER); node_addr->str = id_addr; } - AstNode *node_data = nullptr; + std::unique_ptr node_data = nullptr; if (children[0]->children.size() == 1 && children[1]->isConst()) { node_data = children[1]->clone(); } else { - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_data_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); + auto* wire_data = wire_data_owned.get(); wire_data->str = id_data; wire_data->was_checked = true; wire_data->is_signed = mem_signed; - current_ast_mod->children.push_back(wire_data); current_scope[wire_data->str] = wire_data; + current_ast_mod->children.push_back(std::move(wire_data_owned)); while (wire_data->simplify(true, 1, -1, false)) { } - AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); + auto assign_data = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, x_bits_data, false)); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - defNode->children.push_back(assign_data); + defNode->children.push_back(std::move(assign_data)); - node_data = new AstNode(AST_IDENTIFIER); + node_data = std::make_unique(location, AST_IDENTIFIER); node_data->str = id_data; } - AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_en_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); + auto* wire_en = wire_en_owned.get(); wire_en->str = id_en; wire_en->was_checked = true; - current_ast_mod->children.push_back(wire_en); current_scope[wire_en->str] = wire_en; + current_ast_mod->children.push_back(std::move(wire_en_owned)); while (wire_en->simplify(true, 1, -1, false)) { } - AstNode *assign_en_first = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + auto assign_en_first = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_int(location, 0, false, mem_width)); assign_en_first->children[0]->str = id_en; assign_en_first->children[0]->was_checked = true; - defNode->children.push_back(assign_en_first); + defNode->children.push_back(std::move(assign_en_first)); - AstNode *node_en = new AstNode(AST_IDENTIFIER); + auto node_en = std::make_unique(location, AST_IDENTIFIER); node_en->str = id_en; if (!defNode->children.empty()) - current_top_block->children.insert(current_top_block->children.begin(), defNode); - else - delete defNode; + current_top_block->children.insert(current_top_block->children.begin(), std::move(defNode)); - AstNode *assign_data = nullptr; - AstNode *assign_en = nullptr; + std::unique_ptr assign_data = nullptr; + std::unique_ptr assign_en = nullptr; if (children[0]->children.size() == 2) { if (children[0]->children[1]->range_valid) @@ -3251,24 +3267,24 @@ skip_dynamic_range_lvalue_expansion:; std::vector padding_x(offset, RTLIL::State::Sx); - assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), - new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); + assign_data = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, AST_CONCAT, mkconst_bits(location, padding_x, false), children[1]->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; for (int i = 0; i < mem_width; i++) set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, set_bits_en, false)); assign_en->children[0]->str = id_en; assign_en->children[0]->was_checked = true; } else { - AstNode *the_range = children[0]->children[1]; - AstNode *offset_ast; + auto& the_range = children[0]->children[1]; + std::unique_ptr offset_ast; int width; - if (!try_determine_range_width(the_range, width)) + if (!try_determine_range_width(the_range.get(), width)) input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); if (the_range->children.size() >= 2) @@ -3277,65 +3293,63 @@ skip_dynamic_range_lvalue_expansion:; offset_ast = the_range->children[0]->clone(); if (mem_data_range_offset) - offset_ast = new AstNode(AST_SUB, offset_ast, mkconst_int(mem_data_range_offset, true)); + offset_ast = std::make_unique(location, AST_SUB, std::move(offset_ast), mkconst_int(location, mem_data_range_offset, true)); - assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), - new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); + assign_data = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; for (int i = 0; i < mem_width; i++) set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), - new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); + assign_en = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, AST_SHIFT_LEFT, mkconst_bits(location, set_bits_en, false), offset_ast->clone())); assign_en->children[0]->str = id_en; assign_en->children[0]->was_checked = true; - delete offset_ast; } } else { if (!(children[0]->children.size() == 1 && children[1]->isConst())) { - assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone()); + assign_data = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; } - assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, set_bits_en, false)); assign_en->children[0]->str = id_en; assign_en->children[0]->was_checked = true; } if (assign_data) - newNode->children.push_back(assign_data); + newNode->children.push_back(std::move(assign_data)); if (assign_en) - newNode->children.push_back(assign_en); + newNode->children.push_back(std::move(assign_en)); - AstNode *wrnode; + std::unique_ptr wrnode; if (current_always->type == AST_INITIAL) - wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false)); + wrnode = std::make_unique(location, AST_MEMINIT, std::move(node_addr), std::move(node_data), std::move(node_en), mkconst_int(location, 1, false)); else - wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en); + wrnode = std::make_unique(location, AST_MEMWR, std::move(node_addr), std::move(node_data), std::move(node_en)); wrnode->str = children[0]->str; wrnode->id2ast = children[0]->id2ast; wrnode->location = location; if (wrnode->type == AST_MEMWR) { int portid = current_memwr_count[wrnode->str]++; - wrnode->children.push_back(mkconst_int(portid, false)); + wrnode->children.push_back(mkconst_int(location, portid, false)); std::vector priority_mask; for (int i = 0; i < portid; i++) { bool has_prio = current_memwr_visible[wrnode->str].count(i); priority_mask.push_back(State(has_prio)); } - wrnode->children.push_back(mkconst_bits(priority_mask, false)); + wrnode->children.push_back(mkconst_bits(location, priority_mask, false)); current_memwr_visible[wrnode->str].insert(portid); - current_always->children.push_back(wrnode); + current_always->children.push_back(std::move(wrnode)); } else { - current_ast_mod->children.push_back(wrnode); + current_ast_mod->children.push_back(std::move(wrnode)); } if (newNode->children.empty()) { - delete newNode; - newNode = new AstNode(); + newNode = std::make_unique(location); } goto apply_newNode; } @@ -3349,21 +3363,23 @@ skip_dynamic_range_lvalue_expansion:; { int myidx = autoidx++; - AstNode *wire = new AstNode(AST_WIRE); + auto wire_owned = std::make_unique(location, AST_WIRE); + auto* wire = wire_owned.get(); + current_ast_mod->children.push_back(std::move(wire_owned)); wire->str = stringf("$initstate$%d_wire", myidx); - current_ast_mod->children.push_back(wire); while (wire->simplify(true, 1, -1, false)) { } - AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE), new AstNode(AST_ARGUMENT, new AstNode(AST_IDENTIFIER))); + auto cell_owned = std::make_unique(location, AST_CELL, std::make_unique(location, AST_CELLTYPE), std::make_unique(location, AST_ARGUMENT, std::make_unique(location, AST_IDENTIFIER))); + auto* cell = cell_owned.get(); cell->str = stringf("$initstate$%d", myidx); cell->children[0]->str = "$initstate"; cell->children[1]->str = "\\Y"; cell->children[1]->children[0]->str = wire->str; cell->children[1]->children[0]->id2ast = wire; - current_ast_mod->children.push_back(cell); + current_ast_mod->children.push_back(std::move(cell_owned)); while (cell->simplify(true, 1, -1, false)) { } - newNode = new AstNode(AST_IDENTIFIER); + newNode = std::make_unique(location, AST_IDENTIFIER); newNode->str = wire->str; newNode->id2ast = wire; goto apply_newNode; @@ -3386,20 +3402,19 @@ skip_dynamic_range_lvalue_expansion:; if (GetSize(children) == 2) { - AstNode *buf = children[1]->clone(); + auto buf = children[1]->clone(); while (buf->simplify(true, stage, -1, false)) { } if (buf->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); num_steps = buf->asInt(true); - delete buf; } AstNode *block = nullptr; - for (auto child : current_always->children) + for (auto& child : current_always->children) if (child->type == AST_BLOCK) - block = child; + block = child.get(); log_assert(block != nullptr); @@ -3409,41 +3424,42 @@ skip_dynamic_range_lvalue_expansion:; } int myidx = autoidx++; - AstNode *outreg = nullptr; + AstNode* outreg = nullptr; for (int i = 0; i < num_steps; i++) { - AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, - mkconst_int(width_hint-1, true), mkconst_int(0, true))); + auto reg_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, + mkconst_int(location, width_hint-1, true), mkconst_int(location, 0, true))); + auto* reg = reg_owned.get(); + current_ast_mod->children.push_back(std::move(reg_owned)); - reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i); + reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line, myidx, i); reg->is_reg = true; reg->is_signed = sign_hint; - current_ast_mod->children.push_back(reg); while (reg->simplify(true, 1, -1, false)) { } - AstNode *regid = new AstNode(AST_IDENTIFIER); + auto regid = std::make_unique(location, AST_IDENTIFIER); regid->str = reg->str; regid->id2ast = reg; regid->was_checked = true; - AstNode *rhs = nullptr; + std::unique_ptr rhs = nullptr; if (outreg == nullptr) { rhs = children.at(0)->clone(); } else { - rhs = new AstNode(AST_IDENTIFIER); + rhs = std::make_unique(location, AST_IDENTIFIER); rhs->str = outreg->str; rhs->id2ast = outreg; } - block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs)); + block->children.push_back(std::make_unique(location, AST_ASSIGN_LE, std::move(regid), std::move(rhs))); outreg = reg; } - newNode = new AstNode(AST_IDENTIFIER); + newNode = std::make_unique(location, AST_IDENTIFIER); newNode->str = outreg->str; newNode->id2ast = outreg; goto apply_newNode; @@ -3459,25 +3475,25 @@ skip_dynamic_range_lvalue_expansion:; input_error("System function %s is only allowed in clocked blocks.\n", RTLIL::unescape_id(str).c_str()); - AstNode *present = children.at(0)->clone(); - AstNode *past = clone(); + auto present = children.at(0)->clone(); + auto past = clone(); past->str = "\\$past"; if (str == "\\$stable") - newNode = new AstNode(AST_EQ, past, present); + newNode = std::make_unique(location, AST_EQ, std::move(past), std::move(present)); else if (str == "\\$changed") - newNode = new AstNode(AST_NE, past, present); + newNode = std::make_unique(location, AST_NE, std::move(past), std::move(present)); else if (str == "\\$rose") - newNode = new AstNode(AST_LOGIC_AND, - new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, past, mkconst_int(1,false))), - new AstNode(AST_BIT_AND, present, mkconst_int(1,false))); + newNode = std::make_unique(location, AST_LOGIC_AND, + std::make_unique(location, AST_LOGIC_NOT, std::make_unique(location, AST_BIT_AND, std::move(past), mkconst_int(location, 1, false))), + std::make_unique(location, AST_BIT_AND, std::move(present), mkconst_int(location, 1, false))); else if (str == "\\$fell") - newNode = new AstNode(AST_LOGIC_AND, - new AstNode(AST_BIT_AND, past, mkconst_int(1,false)), - new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, present, mkconst_int(1,false)))); + newNode = std::make_unique(location, AST_LOGIC_AND, + std::make_unique(location, AST_BIT_AND, std::move(past), mkconst_int(location, 1, false)), + std::make_unique(location, AST_LOGIC_NOT, std::make_unique(location, AST_BIT_AND, std::move(present), mkconst_int(location, 1, false)))); else log_abort(); @@ -3497,7 +3513,7 @@ skip_dynamic_range_lvalue_expansion:; input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); @@ -3505,19 +3521,18 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const arg_value = buf->bitsAsConst(); if (arg_value.as_bool()) arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value)); - delete buf; uint32_t result = 0; for (auto i = 0; i < arg_value.size(); i++) if (arg_value.at(i) == RTLIL::State::S1) result = i + 1; - newNode = mkconst_int(result, true); + newNode = mkconst_int(location, result, true); goto apply_newNode; } if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" || - str == "\\$increment" || str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") + str == "\\$increment" || str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { int dim = 1; if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" || str == "\\$bits") { @@ -3529,24 +3544,23 @@ skip_dynamic_range_lvalue_expansion:; input_error("System function %s got %d arguments, expected 1 or 2.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); if (children.size() == 2) { - AstNode *buf = children[1]->clone(); + auto buf = children[1]->clone(); // Evaluate constant expression while (buf->simplify(true, stage, width_hint, sign_hint)) { } dim = buf->asInt(false); - delete buf; } } - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); int mem_depth = 1; int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire int expr_dimensions = 0, expr_unpacked_dimensions = 0; - AstNode *id_ast = NULL; + AstNode *id_ast = nullptr; buf->detectSignWidth(width_hint, sign_hint); if (buf->type == AST_IDENTIFIER) { id_ast = buf->id2ast; - if (id_ast == NULL && current_scope.count(buf->str)) + if (id_ast == nullptr && current_scope.count(buf->str)) id_ast = current_scope.at(buf->str); if (!id_ast) input_error("Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); @@ -3585,7 +3599,6 @@ skip_dynamic_range_lvalue_expansion:; left = high = width - 1; expr_dimensions = 1; } - delete buf; if (str == "\\$dimensions") result = expr_dimensions; else if (str == "\\$unpacked_dimensions") @@ -3605,7 +3618,7 @@ skip_dynamic_range_lvalue_expansion:; else { // str == "\\$bits" result = width * mem_depth; } - newNode = mkconst_int(result, true); + newNode = mkconst_int(location, result, true); goto apply_newNode; } @@ -3651,10 +3664,10 @@ skip_dynamic_range_lvalue_expansion:; } if (str == "\\$rtoi") { - newNode = AstNode::mkconst_int(x, true); + newNode = AstNode::mkconst_int(location, x, true); } else { - newNode = new AstNode(AST_REALVALUE); - if (str == "\\$ln") newNode->realvalue = ::log(x); + newNode = std::make_unique(location, AST_REALVALUE); + if (str == "\\$ln") newNode->realvalue = ::log(x); else if (str == "\\$log10") newNode->realvalue = ::log10(x); else if (str == "\\$exp") newNode->realvalue = ::exp(x); else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x); @@ -3683,7 +3696,7 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$sformatf") { Fmt fmt = processFormat(stage, /*sformat_like=*/true); - newNode = AstNode::mkconst_str(fmt.render()); + newNode = AstNode::mkconst_str(location, fmt.render()); goto apply_newNode; } @@ -3696,7 +3709,7 @@ skip_dynamic_range_lvalue_expansion:; // Determine which bits to count for (size_t i = 1; i < children.size(); i++) { - AstNode *node = children[i]; + auto& node = children[i]; while (node->simplify(true, stage, -1, false)) { } if (node->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant control bit argument.\n", str.c_str()); @@ -3708,39 +3721,37 @@ skip_dynamic_range_lvalue_expansion:; // Detect width of exp (first argument of $countbits) int exp_width = -1; bool exp_sign = false; - AstNode *exp = children[0]; - exp->detectSignWidth(exp_width, exp_sign, NULL); + auto& exp = children[0]; + exp->detectSignWidth(exp_width, exp_sign, nullptr); - newNode = mkconst_int(0, false); + newNode = mkconst_int(location, 0, false); for (int i = 0; i < exp_width; i++) { // Generate nodes for: exp << i >> ($size(exp) - 1) - // ^^ ^^ - AstNode *lsh_node = new AstNode(AST_SHIFT_LEFT, exp->clone(), mkconst_int(i, false)); - AstNode *rsh_node = new AstNode(AST_SHIFT_RIGHT, lsh_node, mkconst_int(exp_width - 1, false)); + // ^^ ^^ + auto lsh_node = std::make_unique(location, AST_SHIFT_LEFT, exp->clone(), mkconst_int(location, i, false)); + auto rsh_node = std::make_unique(location, AST_SHIFT_RIGHT, std::move(lsh_node), mkconst_int(location, exp_width - 1, false)); - AstNode *or_node = nullptr; + std::unique_ptr or_node = nullptr; for (RTLIL::State control_bit : control_bits) { // Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit - // ^^^ - AstNode *eq_node = new AstNode(AST_EQX, rsh_node->clone(), mkconst_bits({control_bit}, false)); + // ^^^ + auto eq_node = std::make_unique(location, AST_EQX, rsh_node->clone(), mkconst_bits(location, {control_bit}, false)); // Or the result for each checked bit value if (or_node) - or_node = new AstNode(AST_LOGIC_OR, or_node, eq_node); + or_node = std::make_unique(location, AST_LOGIC_OR, std::move(or_node), std::move(eq_node)); else - or_node = eq_node; + or_node = std::move(eq_node); } // We should have at least one element in control_bits, // because we checked for the number of arguments above log_assert(or_node != nullptr); - delete rsh_node; - // Generate node for adding with result of previous bit - newNode = new AstNode(AST_ADD, newNode, or_node); + newNode = std::make_unique(location, AST_ADD, std::move(newNode), std::move(or_node)); } goto apply_newNode; @@ -3751,22 +3762,22 @@ skip_dynamic_range_lvalue_expansion:; input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); - AstNode *countbits = clone(); + auto countbits = clone(); countbits->str = "\\$countbits"; if (str == "\\$countones") { - countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); - newNode = countbits; + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::move(countbits); } else if (str == "\\$isunknown") { - countbits->children.push_back(mkconst_bits({RTLIL::Sx}, false)); - countbits->children.push_back(mkconst_bits({RTLIL::Sz}, false)); - newNode = new AstNode(AST_GT, countbits, mkconst_int(0, false)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::Sx}, false)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::Sz}, false)); + newNode = std::make_unique(location, AST_GT, std::move(countbits), mkconst_int(location, 0, false)); } else if (str == "\\$onehot") { - countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); - newNode = new AstNode(AST_EQ, countbits, mkconst_int(1, false)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::make_unique(location, AST_EQ, std::move(countbits), mkconst_int(location, 1, false)); } else if (str == "\\$onehot0") { - countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); - newNode = new AstNode(AST_LE, countbits, mkconst_int(1, false)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::make_unique(location, AST_LE, std::move(countbits), mkconst_int(location, 1, false)); } else { log_abort(); } @@ -3780,7 +3791,7 @@ skip_dynamic_range_lvalue_expansion:; std::string rtype, fname; std::vector argtypes; - std::vector args; + std::vector> args; rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); @@ -3798,10 +3809,7 @@ skip_dynamic_range_lvalue_expansion:; input_error("Failed to evaluate DPI function with non-constant argument.\n"); } - newNode = dpi_call(rtype, fname, argtypes, args); - - for (auto arg : args) - delete arg; + newNode = dpi_call(dpi_decl->location, rtype, fname, argtypes, args); goto apply_newNode; } @@ -3828,12 +3836,12 @@ skip_dynamic_range_lvalue_expansion:; input_error("System function %s got %d arguments, expected 2-4.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); - AstNode *node_filename = children[0]->clone(); + auto node_filename = children[0]->clone(); while (node_filename->simplify(true, stage, width_hint, sign_hint)) { } if (node_filename->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); - AstNode *node_memory = children[1]->clone(); + auto node_memory = children[1]->clone(); while (node_memory->simplify(true, stage, width_hint, sign_hint)) { } if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY) input_error("Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str()); @@ -3841,7 +3849,7 @@ skip_dynamic_range_lvalue_expansion:; int start_addr = -1, finish_addr = -1; if (GetSize(children) > 2) { - AstNode *node_addr = children[2]->clone(); + auto node_addr = children[2]->clone(); while (node_addr->simplify(true, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str()); @@ -3849,7 +3857,7 @@ skip_dynamic_range_lvalue_expansion:; } if (GetSize(children) > 3) { - AstNode *node_addr = children[3]->clone(); + auto node_addr = children[3]->clone(); while (node_addr->simplify(true, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str()); @@ -3860,22 +3868,20 @@ skip_dynamic_range_lvalue_expansion:; if (current_always->type == AST_INITIAL) { pool queue; log_assert(current_always->children[0]->type == AST_BLOCK); - queue.insert(current_always->children[0]); + queue.insert(current_always->children[0].get()); while (!unconditional_init && !queue.empty()) { pool next_queue; - for (auto n : queue) - for (auto c : n->children) { - if (c == this) + for (auto& n : queue) + for (auto& c : n->children) { + if (c.get() == this) unconditional_init = true; - next_queue.insert(c); + next_queue.insert(c.get()); } next_queue.swap(queue); } } newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); - delete node_filename; - delete node_memory; goto apply_newNode; } @@ -3887,13 +3893,14 @@ skip_dynamic_range_lvalue_expansion:; std::stringstream sstr; - sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; + sstr << str << "$func$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); - AstNode *decl = current_scope[str]; + auto* decl = current_scope[str]; if (unevaluated_tern_branch && decl->is_recursive_function()) goto replace_fcall_later; - decl = decl->clone(); + auto decl_clone = decl->clone(); + decl = decl_clone.get(); // sketchy? decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion decl->expand_genblock(prefix); @@ -3901,20 +3908,19 @@ skip_dynamic_range_lvalue_expansion:; { bool require_const_eval = decl->has_const_only_constructs(); bool all_args_const = true; - for (auto child : children) { + for (auto& child : children) { while (child->simplify(true, 1, -1, false)) { } if (child->type != AST_CONSTANT && child->type != AST_REALVALUE) all_args_const = false; } if (all_args_const) { - AstNode *func_workspace = decl->clone(); + auto func_workspace = decl->clone(); func_workspace->set_in_param_flag(true); func_workspace->str = prefix_id(prefix, "$result"); + // func_workspace->dumpAst(stdout, "func_workspace "); newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); - delete func_workspace; if (newNode) { - delete decl; goto apply_newNode; } } @@ -3927,36 +3933,37 @@ skip_dynamic_range_lvalue_expansion:; size_t arg_count = 0; dict wire_cache; - vector new_stmts; - vector output_assignments; + vector> new_stmts; + vector> output_assignments; - if (current_block == NULL) + if (current_block == nullptr) { log_assert(type == AST_FCALL); - AstNode *wire = NULL; + std::unique_ptr wire = nullptr; std::string res_name = prefix_id(prefix, "$result"); - for (auto child : decl->children) + for (auto& child : decl->children) if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); - log_assert(wire != NULL); + log_assert(wire != nullptr); wire->port_id = 0; wire->is_input = false; wire->is_output = false; - current_scope[wire->str] = wire; - current_ast_mod->children.push_back(wire); - while (wire->simplify(true, 1, -1, false)) { } + auto* wire_leaky = wire.get(); + current_scope[wire->str] = wire_leaky; + current_ast_mod->children.push_back(std::move(wire)); + while (wire_leaky->simplify(true, 1, -1, false)) { } - AstNode *lvalue = new AstNode(AST_IDENTIFIER); - lvalue->str = wire->str; + auto lvalue = std::make_unique(location, AST_IDENTIFIER); + lvalue->str = wire_leaky->str; - AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, - new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); + auto always = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK, + std::make_unique(location, AST_ASSIGN_EQ, std::move(lvalue), clone()))); always->children[0]->children[0]->was_checked = true; - current_ast_mod->children.push_back(always); + current_ast_mod->children.push_back(std::move(always)); goto replace_fcall_with_id; } @@ -3973,57 +3980,57 @@ skip_dynamic_range_lvalue_expansion:; } else celltype = RTLIL::escape_id(celltype); - AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); + auto cell = std::make_unique(location, AST_CELL, std::make_unique(location, AST_CELLTYPE)); cell->str = prefix.substr(0, GetSize(prefix)-1); cell->children[0]->str = celltype; - for (auto attr : decl->attributes) + for (auto& attr : decl->attributes) if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0) { - AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone()); + auto cell_arg = std::make_unique(location, AST_PARASET, attr.second->clone()); cell_arg->str = RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_"))); - cell->children.push_back(cell_arg); + cell->children.push_back(std::move(cell_arg)); } - for (auto child : decl->children) + for (auto& child : decl->children) if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { - AstNode *wire = child->clone(); + auto wire = child->clone(); wire->port_id = 0; wire->is_input = false; wire->is_output = false; - current_ast_mod->children.push_back(wire); + current_ast_mod->children.push_back(std::move(wire)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *wire_id = new AstNode(AST_IDENTIFIER); + auto wire_id = std::make_unique(location, AST_IDENTIFIER); wire_id->str = wire->str; if ((child->is_input || child->is_output) && arg_count < children.size()) { - AstNode *arg = children[arg_count++]->clone(); - AstNode *assign = child->is_input ? - new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : - new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); + auto arg = children[arg_count++]->clone(); + auto assign = child->is_input ? + std::make_unique(location, AST_ASSIGN_EQ, wire_id->clone(), std::move(arg)) : + std::make_unique(location, AST_ASSIGN_EQ, std::move(arg), wire_id->clone()); assign->children[0]->was_checked = true; for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { - if (*it != current_block_child) + if (it->get() != current_block_child) continue; - current_block->children.insert(it, assign); + current_block->children.insert(it, std::move(assign)); break; } } - AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id); + auto cell_arg = std::make_unique(location, AST_ARGUMENT, std::move(wire_id)); cell_arg->str = child->str == str ? outport : child->str; - cell->children.push_back(cell_arg); + cell->children.push_back(std::move(cell_arg)); } - current_ast_mod->children.push_back(cell); + current_ast_mod->children.push_back(std::move(cell)); goto replace_fcall_with_id; } - for (auto child : decl->children) + for (auto& child : decl->children) if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM) { AstNode *wire = nullptr; @@ -4033,7 +4040,7 @@ skip_dynamic_range_lvalue_expansion:; wire = wire_cache.at(child->str); bool contains_value = wire->type == AST_LOCALPARAM; if (wire->children.size() == contains_value) { - for (auto c : child->children) + for (auto& c : child->children) wire->children.push_back(c->clone()); } else if (!child->children.empty()) { while (child->simplify(true, stage, -1, false)) { } @@ -4049,38 +4056,36 @@ skip_dynamic_range_lvalue_expansion:; } else { - wire = child->clone(); + current_ast_mod->children.push_back(child->clone()); + wire = current_ast_mod->children.back().get(); wire->port_id = 0; wire->is_input = false; wire->is_output = false; wire->is_reg = true; - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); if (child->type == AST_ENUM_ITEM) - wire->set_attribute(ID::enum_base_type, child->attributes[ID::enum_base_type]); + wire->set_attribute(ID::enum_base_type, std::move(child->attributes[ID::enum_base_type])); wire_cache[child->str] = wire; current_scope[wire->str] = wire; - current_ast_mod->children.push_back(wire); } while (wire->simplify(true, 1, -1, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { - AstNode *arg = children[arg_count++]->clone(); + auto arg = children[arg_count++]->clone(); // convert purely constant arguments into localparams - if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) { + if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child.get())) { wire->type = AST_LOCALPARAM; - if (wire->attributes.count(ID::nosync)) - delete wire->attributes.at(ID::nosync); wire->attributes.erase(ID::nosync); wire->children.insert(wire->children.begin(), arg->clone()); // args without a range implicitly have width 1 if (wire->children.back()->type != AST_RANGE) { // check if this wire is redeclared with an explicit size bool uses_explicit_size = false; - for (const AstNode *other_child : decl->children) + for (auto& other_child : decl->children) if (other_child->type == AST_WIRE && child->str == other_child->str && !other_child->children.empty() && other_child->children.back()->type == AST_RANGE) { @@ -4088,56 +4093,53 @@ skip_dynamic_range_lvalue_expansion:; break; } if (!uses_explicit_size) { - AstNode* range = new AstNode(); + auto range = std::make_unique(location); range->type = AST_RANGE; - wire->children.push_back(range); - range->children.push_back(mkconst_int(0, true)); - range->children.push_back(mkconst_int(0, true)); + range->children.push_back(mkconst_int(location, 0, true)); + range->children.push_back(mkconst_int(location, 0, true)); + wire->children.push_back(std::move(range)); } } wire->fixup_hierarchy_flags(); // updates the sizing while (wire->simplify(true, 1, -1, false)) { } - delete arg; continue; } - AstNode *wire_id = new AstNode(AST_IDENTIFIER); + auto wire_id = std::make_unique(location, AST_IDENTIFIER); wire_id->str = wire->str; - if (child->is_input) { - AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg->clone()); + auto assign = std::make_unique(location, AST_ASSIGN_EQ, wire_id->clone(), arg->clone()); assign->children[0]->was_checked = true; - new_stmts.push_back(assign); + new_stmts.push_back(std::move(assign)); } if (child->is_output) { - AstNode *assign = new AstNode(AST_ASSIGN_EQ, arg->clone(), wire_id->clone()); + auto assign = std::make_unique(location, AST_ASSIGN_EQ, arg->clone(), wire_id->clone()); assign->children[0]->was_checked = true; - output_assignments.push_back(assign); + output_assignments.push_back(std::move(assign)); } - - delete arg; - delete wire_id; } } - for (auto child : decl->children) + for (auto& child : decl->children) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) new_stmts.push_back(child->clone()); - new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); + new_stmts.reserve(new_stmts.size() + output_assignments.size()); + std::move(output_assignments.begin(), output_assignments.end(), std::back_inserter(new_stmts)); for (auto it = current_block->children.begin(); ; it++) { log_assert(it != current_block->children.end()); - if (*it == current_block_child) { - current_block->children.insert(it, new_stmts.begin(), new_stmts.end()); + if (it->get() == current_block_child) { + current_block->children.insert(it, + std::make_move_iterator(new_stmts.begin()), + std::make_move_iterator(new_stmts.end())); break; } } replace_fcall_with_id: - delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; @@ -4184,7 +4186,7 @@ replace_fcall_later:; else data.push_back(RTLIL::State::Sx); } - newNode = mkconst_bits(data, false); + newNode = mkconst_bits(location, data, false); } else if (children.size() == 0) newNode = current_scope[str]->children[0]->clone(); @@ -4196,14 +4198,14 @@ replace_fcall_later:; case AST_BIT_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } break; case AST_TO_SIGNED: case AST_TO_UNSIGNED: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint); - newNode = mkconst_bits(y.to_bits(), type == AST_TO_SIGNED); + newNode = mkconst_bits(location, y.to_bits(), type == AST_TO_SIGNED); } break; if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; } @@ -4213,7 +4215,7 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } break; if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } @@ -4223,16 +4225,16 @@ replace_fcall_later:; if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1); - newNode = mkconst_bits(y.to_bits(), false); + newNode = mkconst_bits(location, y.to_bits(), false); } break; case AST_LOGIC_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); - newNode = mkconst_bits(y.to_bits(), false); + newNode = mkconst_bits(location, y.to_bits(), false); } else if (children[0]->isConst()) { - newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1); + newNode = mkconst_int(location, children[0]->asReal(sign_hint) == 0, false, 1); } break; if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } @@ -4240,27 +4242,27 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, children[1]->is_signed, -1); - newNode = mkconst_bits(y.to_bits(), false); + newNode = mkconst_bits(location, y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { if (type == AST_LOGIC_AND) - newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1); + newNode = mkconst_int(location, (children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1); else - newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1); + newNode = mkconst_int(location, (children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1); } break; if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; } if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; } if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; } if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; } - if (0) { case AST_POW: const_func = RTLIL::const_pow; } + if (0) { case AST_POW: const_func = RTLIL::const_pow; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint); - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); } break; @@ -4277,19 +4279,19 @@ replace_fcall_later:; bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); - newNode = mkconst_bits(y.to_bits(), false); + newNode = mkconst_bits(location, y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed); switch (type) { - case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break; - case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; - case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; - case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; - case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; - case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; - case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; - case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break; + case AST_LT: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break; + case AST_LE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQ: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQX: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NEX: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_GE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; + case AST_GT: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break; default: log_abort(); } } @@ -4302,10 +4304,10 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (children[0]->isConst() && children[1]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); switch (type) { case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break; case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break; @@ -4321,10 +4323,10 @@ replace_fcall_later:; if (0) { case AST_NEG: const_func = RTLIL::const_neg; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (children[0]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); if (type == AST_NEG) newNode->realvalue = -children[0]->asReal(sign_hint); else @@ -4338,21 +4340,21 @@ replace_fcall_later:; AstNode *choice = pair.first; AstNode *not_choice = pair.second; - if (choice != NULL) { + if (choice != nullptr) { if (choice->type == AST_CONSTANT) { int other_width_hint = width_hint; bool other_sign_hint = sign_hint, other_real = false; not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real); if (other_real) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); choice->detectSignWidth(width_hint, sign_hint); newNode->realvalue = choice->asReal(sign_hint); } else { RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); if (choice->is_string && y.size() % 8 == 0 && sign_hint == false) - newNode = mkconst_str(y.to_bits()); + newNode = mkconst_str(location, y.to_bits()); else - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } } else if (choice->isConst()) { @@ -4365,9 +4367,9 @@ replace_fcall_later:; for (auto i = 0; i < a.size(); i++) if (a[i] != b[i]) a.bits()[i] = RTLIL::State::Sx; - newNode = mkconst_bits(a.to_bits(), sign_hint); + newNode = mkconst_bits(location, a.to_bits(), sign_hint); } else if (children[1]->isConst() && children[2]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) newNode->realvalue = children[1]->asReal(sign_hint); else @@ -4386,7 +4388,7 @@ replace_fcall_later:; val = children[1]->bitsAsUnsizedConst(width); else val = children[1]->bitsAsConst(width); - newNode = mkconst_bits(val.to_bits(), children[1]->is_signed); + newNode = mkconst_bits(location, val.to_bits(), children[1]->is_signed); } break; case AST_CONCAT: @@ -4398,14 +4400,14 @@ replace_fcall_later:; string_op = false; tmp_bits.insert(tmp_bits.end(), (*it)->bits.begin(), (*it)->bits.end()); } - newNode = string_op ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false); + newNode = string_op ? mkconst_str(location, tmp_bits) : mkconst_bits(location, tmp_bits, false); break; case AST_REPLICATE: if (children.at(0)->type != AST_CONSTANT || children.at(1)->type != AST_CONSTANT) goto not_const; for (int i = 0; i < children[0]->bitsAsConst().as_int(); i++) tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end()); - newNode = children.at(1)->is_string ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false); + newNode = children.at(1)->is_string ? mkconst_str(location, tmp_bits) : mkconst_bits(location, tmp_bits, false); break; default: not_const: @@ -4419,12 +4421,11 @@ apply_newNode: // fprintf(stderr, "----\n"); // dumpAst(stderr, "- "); // newNode->dumpAst(stderr, "+ "); - log_assert(newNode != NULL); - newNode->filename = filename; + log_assert(newNode != nullptr); + newNode->location.begin.filename = location.begin.filename; newNode->location = location; - newNode->cloneInto(this); + newNode->cloneInto(*this); fixup_hierarchy_flags(); - delete newNode; did_something = true; } @@ -4437,21 +4438,21 @@ apply_newNode: void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to) { - for (AstNode *child : children) + for (auto& child : children) child->replace_result_wire_name_in_function(from, to); if (str == from && type != AST_FCALL && type != AST_TCALL) str = to; } // replace a readmem[bh] TCALL ast node with a block of memory assignments -AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) +std::unique_ptr AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) { int mem_width, mem_size, addr_bits; memory->meminfo(mem_width, mem_size, addr_bits); - AstNode *block = new AstNode(AST_BLOCK); + auto block = std::make_unique(location, AST_BLOCK); - AstNode *meminit = nullptr; + AstNode* meminit = nullptr; int next_meminit_cursor=0; vector meminit_bits; vector en_bits; @@ -4468,7 +4469,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m #else char slash = '/'; #endif - std::string path = filename.substr(0, filename.find_last_of(slash)+1); + std::string path = location.begin.filename->substr(0, location.begin.filename->find_last_of(slash)+1); f.open(path + mem_filename.c_str()); yosys_input_files.insert(path + mem_filename); } else { @@ -4525,39 +4526,46 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m continue; } - AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); + VERILOG_FRONTEND::ConstParser p{memory->location}; + auto value = p.const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); if (unconditional_init) { if (meminit == nullptr || cursor != next_meminit_cursor) { if (meminit != nullptr) { - meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[3] = AstNode::mkconst_int(meminit_size, false); + meminit->children[1] = AstNode::mkconst_bits(location, meminit_bits, false); + meminit->children[3] = AstNode::mkconst_int(location, meminit_size, false); } - meminit = new AstNode(AST_MEMINIT); - meminit->children.push_back(AstNode::mkconst_int(cursor, false)); + auto meminit_owned = std::make_unique(location, AST_MEMINIT); + meminit = meminit_owned.get(); + meminit->children.push_back(AstNode::mkconst_int(location, cursor, false)); meminit->children.push_back(nullptr); - meminit->children.push_back(AstNode::mkconst_bits(en_bits, false)); + meminit->children.push_back(AstNode::mkconst_bits(location, en_bits, false)); meminit->children.push_back(nullptr); meminit->str = memory->str; meminit->id2ast = memory; meminit_bits.clear(); meminit_size = 0; - current_ast_mod->children.push_back(meminit); + current_ast_mod->children.push_back(std::move(meminit_owned)); next_meminit_cursor = cursor; } meminit_size++; next_meminit_cursor++; meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end()); - delete value; } else { - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); + block->children.push_back( + std::make_unique(location, + AST_ASSIGN_EQ, std::make_unique(location, + AST_IDENTIFIER, std::make_unique(location, + AST_RANGE, AstNode::mkconst_int(location, + cursor, false))), + std::move(value))); block->children.back()->children[0]->str = memory->str; block->children.back()->children[0]->id2ast = memory; block->children.back()->children[0]->was_checked = true; @@ -4573,8 +4581,8 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m } if (meminit != nullptr) { - meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[3] = AstNode::mkconst_int(meminit_size, false); + meminit->children[1] = AstNode::mkconst_bits(location, meminit_bits, false); + meminit->children[3] = AstNode::mkconst_int(location, meminit_size, false); } return block; @@ -4634,7 +4642,7 @@ void AstNode::expand_genblock(const std::string &prefix) }; for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; + auto* child = children[i].get(); switch (child->type) { case AST_WIRE: @@ -4660,9 +4668,9 @@ void AstNode::expand_genblock(const std::string &prefix) case AST_ENUM: current_scope[child->str] = child; - for (auto enode : child->children){ + for (auto& enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); - prefix_node(enode); + prefix_node(enode.get()); } break; @@ -4672,7 +4680,7 @@ void AstNode::expand_genblock(const std::string &prefix) } for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; + auto& child = children[i]; // AST_PREFIX member names should not be prefixed; we recurse into them // as normal to ensure indices and ranges are properly resolved, and // then restore the previous string @@ -4704,7 +4712,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // seeing a proper generate control flow construct increments the // counter once ++counter; - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing, counter); break; @@ -4721,7 +4729,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // within a genblk, the counter starts fresh std::set existing_local = existing; int counter_local = 0; - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing_local, counter_local); break; } @@ -4730,7 +4738,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // track names which could conflict with implicit genblk names if (str.rfind("\\genblk", 0) == 0) existing.insert(str); - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing, counter); break; } @@ -4741,12 +4749,12 @@ static void mark_memories_assign_lhs_complex(dict> & dict &mem2reg_candidates, AstNode *that) { for (auto &child : that->children) - mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child.get()); if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { AstNode *mem = that->id2ast; if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*that->location.begin.filename).c_str(), that->location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; } } @@ -4765,7 +4773,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg { // mark all memories that are used in a complex expression on the left side of an assignment for (auto &lhs_child : children[0]->children) - mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child); + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child.get()); if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) { @@ -4774,14 +4782,14 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // activate mem2reg if this is assigned in an async proc if (flags & AstNode::MEM2REG_FL_ASYNC) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC; } // remember if this is assigned blocking (=) if (type == AST_ASSIGN_EQ) { if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line)); proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } @@ -4798,11 +4806,11 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // remember where this is if (flags & MEM2REG_FL_INIT) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT; } else { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } } @@ -4819,7 +4827,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // flag if used after blocking assignment (in same proc) if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) { - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename).c_str(), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; } } @@ -4831,11 +4839,11 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg if ((type == AST_MODULE || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg)) children_flags |= AstNode::MEM2REG_FL_ALL; - dict *proc_flags_p = NULL; + dict *proc_flags_p = nullptr; if (type == AST_ALWAYS) { int count_edge_events = 0; - for (auto child : children) + for (auto& child : children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) count_edge_events++; if (count_edge_events != 1) @@ -4851,12 +4859,12 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg flags |= children_flags; log_assert((flags & ~0x000000ff) == 0); - for (auto child : children) + for (auto& child : children) { if (lhs_children_counter > 0) { lhs_children_counter--; if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) { - for (auto c : child->children[0]->children) { + for (auto& c : child->children[0]->children) { if (proc_flags_p) c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); else @@ -4892,7 +4900,7 @@ bool AstNode::mem2reg_check(pool &mem2reg_set) return true; } -void AstNode::mem2reg_remove(pool &mem2reg_set, vector &delnodes) +void AstNode::mem2reg_remove(pool &mem2reg_set) { log_assert(mem2reg_set.count(this) == 0); @@ -4900,17 +4908,16 @@ void AstNode::mem2reg_remove(pool &mem2reg_set, vector &deln id2ast = nullptr; for (size_t i = 0; i < children.size(); i++) { - if (mem2reg_set.count(children[i]) > 0) { - delnodes.push_back(children[i]); + if (mem2reg_set.count(children[i].get()) > 0) { children.erase(children.begin() + (i--)); } else { - children[i]->mem2reg_remove(mem2reg_set, delnodes); + children[i]->mem2reg_remove(mem2reg_set); } } } // actually replace memories with registers -bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block) +bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode* async_block) { bool did_something = false; @@ -4937,9 +4944,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (length != 0) { - AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK)); - mod->children.push_back(block); - block = block->children[0]; + auto block_owned = std::make_unique(location, + AST_INITIAL, std::make_unique(location, + AST_BLOCK)); + auto block = block_owned.get(); + mod->children.push_back(std::move(block_owned)); + block = block->children[0].get(); int wordsz = GetSize(data) / length; @@ -4953,11 +4963,13 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, while (epos < wordsz && en[epos] == State::S1) epos++; int clen = epos - pos; - AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false)); + auto range = std::make_unique(location, + AST_RANGE, AstNode::mkconst_int(location, + cursor+i, false)); if (pos != 0 || epos != wordsz) { int left; int right; - AstNode *mrange = id2ast->children[0]; + auto& mrange = id2ast->children[0]; if (mrange->range_left < mrange->range_right) { right = mrange->range_right - pos; left = mrange->range_right - epos + 1; @@ -4965,42 +4977,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, right = mrange->range_right + pos; left = mrange->range_right + epos - 1; } - range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true))); + range = std::make_unique(location, + AST_MULTIRANGE, std::move(range), std::make_unique(location, + AST_RANGE, + AstNode::mkconst_int(location, left, true), + AstNode::mkconst_int(location, right, true))); } - AstNode *target = new AstNode(AST_IDENTIFIER, range); + auto target = std::make_unique(location, AST_IDENTIFIER, std::move(range)); target->str = str; target->id2ast = id2ast; target->was_checked = true; - block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).to_bits(), false))); + block->children.push_back(std::make_unique(location, + AST_ASSIGN_EQ, + std::move(target), + mkconst_bits(location, + data.extract(i*wordsz + pos, clen).to_bits(), + false))); pos = epos; } } } } - AstNode *newNode = new AstNode(AST_NONE); - newNode->cloneInto(this); - delete newNode; - + auto newNode = std::make_unique(location, AST_NONE); + newNode->cloneInto(*this); did_something = true; } - if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set)) + if (type == AST_ASSIGN && block == nullptr && children[0]->mem2reg_check(mem2reg_set)) { - if (async_block == NULL) { - async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - mod->children.push_back(async_block); + if (async_block == nullptr) { + auto async_block_owned = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK)); + async_block = async_block_owned.get(); + mod->children.push_back(std::move(async_block_owned)); } - AstNode *newNode = clone(); + auto newNode = clone(); newNode->type = AST_ASSIGN_EQ; newNode->children[0]->was_checked = true; - async_block->children[0]->children.push_back(newNode); - - newNode = new AstNode(AST_NONE); - newNode->cloneInto(this); - delete newNode; + async_block->children[0]->children.push_back(std::move(newNode)); + newNode = std::make_unique(location, AST_NONE); + newNode->cloneInto(*this); did_something = true; } @@ -5008,64 +5026,64 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + auto wire_addr = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 0, true))); wire_addr->str = id_addr; wire_addr->is_reg = true; wire_addr->was_checked = true; - wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - mod->children.push_back(wire_addr); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_addr->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_addr)); - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_data = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); wire_data->str = id_data; wire_data->is_reg = true; wire_data->was_checked = true; wire_data->is_signed = mem_signed; - wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - mod->children.push_back(wire_data); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_data->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_data)); - log_assert(block != NULL); + log_assert(block != nullptr); size_t assign_idx = 0; - while (assign_idx < block->children.size() && block->children[assign_idx] != this) + while (assign_idx < block->children.size() && block->children[assign_idx].get() != this) assign_idx++; log_assert(assign_idx < block->children.size()); - AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + auto assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; - block->children.insert(block->children.begin()+assign_idx+1, assign_addr); + block->children.insert(block->children.begin()+assign_idx+1, std::move(assign_addr)); - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + auto case_node = std::make_unique(location, AST_CASE, std::make_unique(location, AST_IDENTIFIER)); case_node->children[0]->str = id_addr; for (int i = 0; i < mem_size; i++) { if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i) continue; - AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + auto cond_node = std::make_unique(location, AST_COND, AstNode::mkconst_int(location, i, false, addr_bits), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, type, std::make_unique(location, AST_IDENTIFIER), std::make_unique(location, AST_IDENTIFIER)); if (children[0]->children.size() == 2) assign_reg->children[0]->children.push_back(children[0]->children[1]->clone()); assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i); assign_reg->children[1]->str = id_data; - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(cond_node)); } // fixup on the full hierarchy below case_node case_node->fixup_hierarchy_flags(true); - block->children.insert(block->children.begin()+assign_idx+2, case_node); + block->children.insert(block->children.begin()+assign_idx+2, std::move(case_node)); children[0]->delete_children(); children[0]->range_valid = false; - children[0]->id2ast = NULL; + children[0]->id2ast = nullptr; children[0]->str = id_data; type = AST_ASSIGN_EQ; children[0]->was_checked = true; @@ -5076,7 +5094,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (mem2reg_check(mem2reg_set)) { - AstNode *bit_part_sel = NULL; + std::unique_ptr bit_part_sel = nullptr; if (children.size() == 2) bit_part_sel = children[1]->clone(); @@ -5093,7 +5111,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, str = stringf("%s[%d]", str.c_str(), id); delete_children(); range_valid = false; - id2ast = NULL; + id2ast = nullptr; } else { @@ -5106,7 +5124,6 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, else width = bit_part_sel->children[0]->integer - bit_part_sel->children[1]->integer; - delete bit_part_sel; bit_part_sel = nullptr; } else @@ -5121,69 +5138,68 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, std::vector x_bits; for (int i = 0; i < width; i++) x_bits.push_back(RTLIL::State::Sx); - AstNode *constant = AstNode::mkconst_bits(x_bits, false); - constant->cloneInto(this); - delete constant; + auto constant = AstNode::mkconst_bits(location, x_bits, false); + constant->cloneInto(*this); } } else { std::stringstream sstr; - sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; bool mem_signed = id2ast->is_signed; id2ast->meminfo(mem_width, mem_size, addr_bits); - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); + auto wire_addr = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 0, true))); wire_addr->str = id_addr; wire_addr->is_reg = true; wire_addr->was_checked = true; if (block) - wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - mod->children.push_back(wire_addr); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_addr->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_addr)); - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_data = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); wire_data->str = id_data; wire_data->is_reg = true; wire_data->was_checked = true; wire_data->is_signed = mem_signed; if (block) - wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - mod->children.push_back(wire_data); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_data->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_data)); - AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); + auto assign_addr = std::make_unique(location, block ? AST_ASSIGN_EQ : AST_ASSIGN, std::make_unique(location, AST_IDENTIFIER), children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + auto case_node = std::make_unique(location, AST_CASE, std::make_unique(location, AST_IDENTIFIER)); case_node->children[0]->str = id_addr; for (int i = 0; i < mem_size; i++) { if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) continue; - AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + auto cond_node = std::make_unique(location, AST_COND, AstNode::mkconst_int(location, i, false, addr_bits), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), std::make_unique(location, AST_IDENTIFIER)); assign_reg->children[0]->str = id_data; assign_reg->children[0]->was_checked = true; assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(cond_node)); } std::vector x_bits; for (int i = 0; i < mem_width; i++) x_bits.push_back(RTLIL::State::Sx); - AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); + auto cond_node = std::make_unique(location, AST_COND, std::make_unique(location, AST_DEFAULT), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), AstNode::mkconst_bits(location, x_bits, false)); assign_reg->children[0]->str = id_data; assign_reg->children[0]->was_checked = true; - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(cond_node)); // fixup on the full hierarchy below case_node case_node->fixup_hierarchy_flags(true); @@ -5194,34 +5210,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) assign_idx++; log_assert(assign_idx < block->children.size()); - block->children.insert(block->children.begin()+assign_idx, case_node); - block->children.insert(block->children.begin()+assign_idx, assign_addr); + block->children.insert(block->children.begin()+assign_idx, std::move(case_node)); + block->children.insert(block->children.begin()+assign_idx, std::move(assign_addr)); } else { - AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, case_node)); - mod->children.push_back(proc); - mod->children.push_back(assign_addr); + auto proc = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK, std::move(case_node))); + mod->children.push_back(std::move(proc)); + mod->children.push_back(std::move(assign_addr)); mod->fixup_hierarchy_flags(); } delete_children(); range_valid = false; - id2ast = NULL; + id2ast = nullptr; str = id_data; } if (bit_part_sel) { - children.push_back(bit_part_sel); + children.push_back(std::move(bit_part_sel)); fixup_hierarchy_flags(); } did_something = true; } - log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); + log_assert(id2ast == nullptr || mem2reg_set.count(id2ast) == 0); + + std::vector children_list; + for (auto& child : children) + children_list.push_back(child.get()); - auto children_list = children; for (size_t i = 0; i < children_list.size(); i++) if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block)) did_something = true; @@ -5306,7 +5325,7 @@ bool AstNode::has_const_only_constructs() { if (type == AST_WHILE || type == AST_REPEAT) return true; - for (auto child : children) + for (auto& child : children) if (child->has_const_only_constructs()) return true; return false; @@ -5316,7 +5335,7 @@ bool AstNode::is_simple_const_expr() { if (type == AST_IDENTIFIER) return false; - for (auto child : children) + for (auto& child : children) if (!child->is_simple_const_expr()) return false; return true; @@ -5351,9 +5370,8 @@ bool AstNode::replace_variables(std::map &varia offset = -offset; std::vector &var_bits = variables.at(str).val.bits(); std::vector new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); - AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); - newNode->cloneInto(this); - delete newNode; + auto newNode = mkconst_bits(location, new_bits, variables.at(str).is_signed); + newNode->cloneInto(*this); return true; } @@ -5364,32 +5382,31 @@ bool AstNode::replace_variables(std::map &varia } // attempt to statically evaluate a functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) +std::unique_ptr AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { std::map backup_scope = current_scope; std::map variables; - std::vector to_delete; - AstNode *block = new AstNode(AST_BLOCK); - AstNode *result = nullptr; + auto block = std::make_unique(location, AST_BLOCK); + std::unique_ptr result = nullptr; size_t argidx = 0; - for (auto child : children) + for (auto& child : children) { block->children.push_back(child->clone()); } block->set_in_param_flag(true); + std::vector> temporary_nodes; while (!block->children.empty()) { - AstNode *stmt = block->children.front(); + auto& stmt = block->children.front(); #if 0 log("-----------------------------------\n"); for (auto &it : variables) log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val)); - stmt->dumpAst(NULL, "stmt> "); + stmt->dumpAst(nullptr, "stmt> "); #endif - if (stmt->type == AST_WIRE) { while (stmt->simplify(true, 1, -1, false)) { } @@ -5414,7 +5431,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) stmt->children.back()->type == AST_RANGE; // identify the argument corresponding to this wire, if applicable if (stmt->is_input && argidx < fcall->children.size()) { - variable.arg = fcall->children.at(argidx++); + variable.arg = fcall->children.at(argidx++).get(); } // load the constant arg's value into this variable if (variable.arg) { @@ -5425,10 +5442,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) variable.val = variable.arg->realAsConst(width); } } - current_scope[stmt->str] = stmt; + current_scope[stmt->str] = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - to_delete.push_back(stmt); continue; } @@ -5438,10 +5455,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { while (stmt->simplify(true, 1, -1, false)) { } - current_scope[stmt->str] = stmt; + current_scope[stmt->str] = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - to_delete.push_back(stmt); continue; } @@ -5482,7 +5499,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) if (stmt->children.at(0)->children.empty()) { variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.size()); } else { - AstNode *range = stmt->children.at(0)->children.at(0); + AstNode *range = stmt->children.at(0)->children.at(0).get(); if (!range->range_valid) { if (!must_succeed) goto finished; @@ -5500,24 +5517,28 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) } } - delete block->children.front(); block->children.erase(block->children.begin()); continue; } if (stmt->type == AST_FOR) { - block->children.insert(block->children.begin(), stmt->children.at(0)); - stmt->children.at(3)->children.push_back(stmt->children.at(2)); + stmt->type = AST_WHILE; + log_assert(stmt->children.size() > 2); + auto yoink0 = std::move(stmt->children.at(0)); + log_assert(stmt->children.size() > 2); + auto yoink2 = std::move(stmt->children.at(2)); + stmt->children.at(3)->children.push_back(std::move(yoink2)); stmt->children.erase(stmt->children.begin() + 2); stmt->children.erase(stmt->children.begin()); - stmt->type = AST_WHILE; + block->children.insert(block->children.begin(), std::move(yoink0)); + log_assert(stmt->children.size() == 2); continue; } if (stmt->type == AST_WHILE) { - AstNode *cond = stmt->children.at(0)->clone(); + auto cond = stmt->children.at(0)->clone(); if (!cond->replace_variables(variables, fcall, must_succeed)) goto finished; cond->set_in_param_flag(true); @@ -5533,17 +5554,14 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); } else { - delete block->children.front(); block->children.erase(block->children.begin()); } - - delete cond; continue; } if (stmt->type == AST_REPEAT) { - AstNode *num = stmt->children.at(0)->clone(); + auto num = stmt->children.at(0)->clone(); if (!num->replace_variables(variables, fcall, must_succeed)) goto finished; num->set_in_param_flag(true); @@ -5556,41 +5574,41 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) fcall->loc_string().c_str()); } + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) - block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); + block->children.insert(block->children.begin(), temporary_nodes.back()->children.at(1)->clone()); - delete stmt; - delete num; continue; } if (stmt->type == AST_CASE) { - AstNode *expr = stmt->children.at(0)->clone(); + auto expr = stmt->children.at(0)->clone(); if (!expr->replace_variables(variables, fcall, must_succeed)) goto finished; expr->set_in_param_flag(true); while (expr->simplify(true, 1, -1, false)) { } - AstNode *sel_case = NULL; + AstNode *sel_case = nullptr; + std::unique_ptr sel_case_copy = nullptr; for (size_t i = 1; i < stmt->children.size(); i++) { bool found_match = false; log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ); if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { - sel_case = stmt->children.at(i)->children.back(); + sel_case = stmt->children.at(i)->children.back().get(); continue; } for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) { - AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); + auto cond = stmt->children.at(i)->children.at(j)->clone(); if (!cond->replace_variables(variables, fcall, must_succeed)) goto finished; - cond = new AstNode(AST_EQ, expr->clone(), cond); + cond = std::make_unique(location, AST_EQ, expr->clone(), std::move(cond)); cond->set_in_param_flag(true); while (cond->simplify(true, 1, -1, false)) { } @@ -5602,20 +5620,19 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) } found_match = cond->asBool(); - delete cond; } if (found_match) { - sel_case = stmt->children.at(i)->children.back(); + sel_case = stmt->children.at(i)->children.back().get(); break; } } + if (sel_case) + sel_case_copy = sel_case->clone(); block->children.erase(block->children.begin()); - if (sel_case) - block->children.insert(block->children.begin(), sel_case->clone()); - delete stmt; - delete expr; + if (sel_case_copy) + block->children.insert(block->children.begin(), std::move(sel_case_copy)); continue; } @@ -5623,15 +5640,19 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { if (!stmt->str.empty()) stmt->expand_genblock(stmt->str + "."); - + auto* stmt_leaky = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); - stmt->children.clear(); + block->children.reserve(block->children.size() + stmt_leaky->children.size()); + block->children.insert(block->children.begin(), + std::make_move_iterator(stmt_leaky->children.begin()), + std::make_move_iterator(stmt_leaky->children.end())); + stmt_leaky->children.clear(); block->fixup_hierarchy_flags(); - delete stmt; continue; } + // log("C\n"); if (!must_succeed) goto finished; stmt->input_error("Unsupported language construct in constant function\n%s: ... called from here.\n", @@ -5639,17 +5660,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) log_abort(); } - result = AstNode::mkconst_bits(variables.at(str).val.to_bits(), variables.at(str).is_signed); + result = AstNode::mkconst_bits(location, variables.at(str).val.to_bits(), variables.at(str).is_signed); finished: - delete block; current_scope = backup_scope; - - for (auto it : to_delete) { - delete it; - } - to_delete.clear(); - return result; } @@ -5660,15 +5674,14 @@ void AstNode::allocateDefaultEnumValues() if (children.front()->attributes.count(ID::enum_base_type)) return; // already elaborated int last_enum_int = -1; - for (auto node : children) { + for (auto& node : children) { log_assert(node->type==AST_ENUM_ITEM); - node->set_attribute(ID::enum_base_type, mkconst_str(str)); + node->set_attribute(ID::enum_base_type, mkconst_str(node->location, str)); for (size_t i = 0; i < node->children.size(); i++) { switch (node->children[i]->type) { case AST_NONE: // replace with auto-incremented constant - delete node->children[i]; - node->children[i] = AstNode::mkconst_int(++last_enum_int, true); + node->children[i] = AstNode::mkconst_int(node->location, ++last_enum_int, true); break; case AST_CONSTANT: // explicit constant (or folded expression) @@ -5696,8 +5709,8 @@ bool AstNode::is_recursive_function() const if (it != current_scope.end() && visit(it->second)) return true; } - for (const AstNode *child : node->children) { - if (visit(child)) + for (auto& child : node->children) { + if (visit(child.get())) return true; } return false; @@ -5727,9 +5740,9 @@ std::pair AstNode::get_tern_choice() AstNode *choice = nullptr, *not_choice = nullptr; if (found_sure_true) - choice = children[1], not_choice = children[2]; + choice = children[1].get(), not_choice = children[2].get(); else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; + choice = children[2].get(), not_choice = children[1].get(); return {choice, not_choice}; } diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 2c923f0b7..3f937f3c2 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -10,6 +10,10 @@ frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc +frontends/verilog/verilog_frontend.o: frontends/verilog/verilog_parser.tab.hh +frontends/verilog/preproc.o: frontends/verilog/verilog_parser.tab.hh + +frontends/verilog/verilog_lexer.h: frontends/verilog/verilog_parser.tab.hh frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l frontends/verilog/verilog_parser.tab.cc $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/verilog/verilog_lexer.cc $< @@ -20,5 +24,6 @@ OBJS += frontends/verilog/verilog_parser.tab.o OBJS += frontends/verilog/verilog_lexer.o OBJS += frontends/verilog/preproc.o OBJS += frontends/verilog/verilog_frontend.o +OBJS += frontends/verilog/verilog_error.o OBJS += frontends/verilog/const2ast.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index a4dfbc7ec..9b197b356 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -42,14 +42,23 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; +using namespace VERILOG_FRONTEND; + +void ConstParser::log_maybe_loc_error(std::string msg) { + log_file_error(*loc.begin.filename, loc.begin.line, "%s", msg.c_str()); +} + +void ConstParser::log_maybe_loc_warn(std::string msg) { + log_file_warning(*loc.begin.filename, loc.begin.line, "%s", msg.c_str()); +} // divide an arbitrary length decimal number by two and return the rest -static int my_decimal_div_by_two(std::vector &digits) +int ConstParser::my_decimal_div_by_two(std::vector &digits) { int carry = 0; for (size_t i = 0; i < digits.size(); i++) { if (digits[i] >= 10) - log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n"); + log_maybe_loc_error("Invalid use of [a-fxz?] in decimal constant.\n"); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; @@ -60,7 +69,7 @@ static int my_decimal_div_by_two(std::vector &digits) } // find the number of significant bits in a binary number (not including the sign bit) -static int my_ilog2(int x) +int ConstParser::my_ilog2(int x) { int ret = 0; while (x != 0 && x != -1) { @@ -71,7 +80,7 @@ static int my_ilog2(int x) } // parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') -static void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized) +void ConstParser::my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized) { // all digits in string (MSB at index 0) std::vector digits; @@ -102,8 +111,8 @@ static void my_strtobin(std::vector &data, const char *str, int le int bits_per_digit = my_ilog2(base-1); for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) { if (*it > (base-1) && *it < 0xf0) - log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n", - base-1, base); + log_maybe_loc_error(stringf("Digit larger than %d used in in base-%d constant.\n", + base-1, base)); for (int i = 0; i < bits_per_digit; i++) { int bitmask = 1 << i; if (*it == 0xf0) @@ -126,7 +135,7 @@ static void my_strtobin(std::vector &data, const char *str, int le } if (is_unsized && (len > len_in_bits)) - log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len); + log_maybe_loc_error(stringf("Unsized constant must have width of 1 bit, but have %d bits!\n", len)); for (len = len - 1; len >= 0; len--) if (data[len] == State::S1) @@ -140,21 +149,19 @@ static void my_strtobin(std::vector &data, const char *str, int le } if (len_in_bits == 0) - log_file_error(current_filename, get_line_num(), "Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n"); + log_maybe_loc_error("Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n"); if (len > len_in_bits) - log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n", - len_in_bits, len, current_filename.c_str(), get_line_num()); + log_maybe_loc_warn(stringf("Literal has a width of %d bit, but value requires %d bit.\n", + len_in_bits, len)); } - // convert the Verilog code for a constant to an AST node -AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z) +std::unique_ptr ConstParser::const2ast(std::string code, char case_type, bool warn_z) { if (warn_z) { - AstNode *ret = const2ast(code, case_type); + auto ret = const2ast(code, case_type); if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) - log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", - current_filename.c_str(), get_line_num()); + log_maybe_loc_warn("Yosys has only limited support for tri-state logic at the moment.\n"); return ret; } @@ -172,7 +179,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn ch = ch >> 1; } } - AstNode *ast = AstNode::mkconst_bits(data, false); + auto ast = AstNode::mkconst_bits(loc, data, false); ast->str = code; return ast; } @@ -191,7 +198,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn my_strtobin(data, str, -1, 10, case_type, false); if (data.back() == State::S1) data.push_back(State::S0); - return AstNode::mkconst_bits(data, true); + return AstNode::mkconst_bits(loc, data, true); } // unsized constant @@ -239,10 +246,11 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn if (is_signed && data.back() == State::S1) data.push_back(State::S0); } - return AstNode::mkconst_bits(data, is_signed, is_unsized); + return AstNode::mkconst_bits(loc, data, is_signed, is_unsized); } return NULL; } + YOSYS_NAMESPACE_END diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index e33b0a2c3..6dff1b6fc 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -34,6 +34,7 @@ #include "preproc.h" #include "verilog_frontend.h" +#include "frontends/verilog/verilog_parser.tab.hh" #include "kernel/log.h" #include #include @@ -749,7 +750,9 @@ frontend_verilog_preproc(std::istream &f, std::string filename, const define_map_t &pre_defines, define_map_t &global_defines_cache, - const std::list &include_dirs) + const std::list &include_dirs, + ParseState &parse_state, + ParseMode &parse_mode) { define_map_t defines; defines.merge(pre_defines); @@ -961,11 +964,11 @@ frontend_verilog_preproc(std::istream &f, } if (tok == "`resetall") { - default_nettype_wire = true; + parse_state.default_nettype_wire = true; continue; } - if (tok == "`undefineall" && sv_mode) { + if (tok == "`undefineall" && parse_mode.sv) { defines.clear(); global_defines_cache.clear(); continue; diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h index 330855a92..8333f7661 100644 --- a/frontends/verilog/preproc.h +++ b/frontends/verilog/preproc.h @@ -35,6 +35,11 @@ YOSYS_NAMESPACE_BEGIN struct define_body_t; struct arg_map_t; +namespace VERILOG_FRONTEND { + struct ParseState; + struct ParseMode; +}; + struct define_map_t { define_map_t(); @@ -71,7 +76,9 @@ frontend_verilog_preproc(std::istream &f, std::string filename, const define_map_t &pre_defines, define_map_t &global_defines_cache, - const std::list &include_dirs); + const std::list &include_dirs, + VERILOG_FRONTEND::ParseState &parse_state, + VERILOG_FRONTEND::ParseMode &parse_mode); YOSYS_NAMESPACE_END diff --git a/frontends/verilog/verilog_error.cc b/frontends/verilog/verilog_error.cc new file mode 100644 index 000000000..491b8c7f5 --- /dev/null +++ b/frontends/verilog/verilog_error.cc @@ -0,0 +1,68 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + */ + +#include "kernel/yosys_common.h" +#include "frontends/verilog/verilog_error.h" +#include "frontends/verilog/verilog_location.h" + +USING_YOSYS_NAMESPACE + +/** + * Legacy behavior is to only track lines. Now we have columns too, but we don't + * report them in errors. + * TODO: report columns, too + */ + +[[noreturn]] +static void verr_at(std::string filename, int begin_line, char const *fmt, va_list ap) +{ + char buffer[1024]; + char *p = buffer; + p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); + p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); + YOSYS_NAMESPACE_PREFIX log_file_error(filename, begin_line, "%s", buffer); + exit(1); +} + +static void vwarn_at(std::string filename, int begin_line, char const *fmt, va_list ap) +{ + char buffer[1024]; + char *p = buffer; + p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); + p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); + YOSYS_NAMESPACE_PREFIX log_file_warning(filename, begin_line, "%s", buffer); +} + +[[noreturn]] +void VERILOG_FRONTEND::err_at_loc(Location loc, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + verr_at(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line, fmt, args); +} +void VERILOG_FRONTEND::warn_at_loc(Location loc, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vwarn_at(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line, fmt, args); + va_end(args); +} + diff --git a/frontends/verilog/verilog_error.h b/frontends/verilog/verilog_error.h new file mode 100644 index 000000000..b36de19b8 --- /dev/null +++ b/frontends/verilog/verilog_error.h @@ -0,0 +1,19 @@ +#ifndef VERILOG_ERROR_H +#define VERILOG_ERROR_H + +#include "kernel/yosys_common.h" +#include "frontends/ast/ast.h" +#include "frontends/verilog/verilog_location.h" + +YOSYS_NAMESPACE_BEGIN + +namespace VERILOG_FRONTEND +{ + [[noreturn]] + void err_at_loc(Location loc, char const *fmt, ...); + void warn_at_loc(Location loc, char const *fmt, ...); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 1f272ca4f..4b4f7ad8d 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -31,10 +31,14 @@ #endif #include "verilog_frontend.h" +#include "verilog_lexer.h" +#include "verilog_error.h" +#include "verilog_location.h" #include "preproc.h" #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include +#include YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; @@ -46,13 +50,13 @@ static std::list> verilog_defaults_stack; static void error_on_dpi_function(AST::AstNode *node) { - if (node->type == AST::AST_DPI_FUNCTION) - log_file_error(node->filename, node->location.first_line, "Found DPI function %s.\n", node->str.c_str()); - for (auto child : node->children) - error_on_dpi_function(child); + if (node->type == AST::AST_DPI_FUNCTION) + err_at_loc(node->location, "Found DPI function %s.\n", node->str.c_str()); + for (auto& child : node->children) + error_on_dpi_function(child.get()); } -static void add_package_types(dict &user_types, std::vector &package_list) +static void add_package_types(dict &user_types, std::vector> &package_list) { // prime the parser's user type lookup table with the package qualified names // of typedefed names in the packages seen so far. @@ -61,7 +65,7 @@ static void add_package_types(dict &user_types, std for (const auto &node: pkg->children) { if (node->type == AST::AST_TYPEDEF) { std::string s = pkg->str + "::" + node->str.substr(1); - user_types[s] = node; + user_types[s] = node.get(); } } } @@ -250,6 +254,8 @@ struct VerilogFrontend : public Frontend { bool flag_dump_vlog1 = false; bool flag_dump_vlog2 = false; bool flag_dump_rtlil = false; + bool flag_debug_lexer = false; + bool flag_debug_parser = false; bool flag_nolatches = false; bool flag_nomeminit = false; bool flag_nomem2reg = false; @@ -266,22 +272,24 @@ struct VerilogFrontend : public Frontend { bool flag_noblackbox = false; bool flag_nowb = false; bool flag_nosynthesis = false; + bool flag_yydebug = false; define_map_t defines_map; std::list include_dirs; std::list attributes; - frontend_verilog_yydebug = false; - sv_mode = false; - formal_mode = false; - noassert_mode = false; - noassume_mode = false; - norestrict_mode = false; - assume_asserts_mode = false; - assert_assumes_mode = false; - lib_mode = false; - specify_mode = false; - default_nettype_wire = true; + ParseMode parse_mode = {}; + ParseState parse_state = {}; + parse_mode.sv = false; + parse_mode.formal = false; + parse_mode.noassert = false; + parse_mode.noassume = false; + parse_mode.norestrict = false; + parse_mode.assume_asserts = false; + parse_mode.assert_assumes = false; + parse_mode.lib = false; + parse_mode.specify = false; + parse_state.default_nettype_wire = true; args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); @@ -289,11 +297,11 @@ struct VerilogFrontend : public Frontend { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-sv") { - sv_mode = true; + parse_mode.sv = true; continue; } if (arg == "-formal") { - formal_mode = true; + parse_mode.formal = true; continue; } if (arg == "-nosynthesis") { @@ -301,23 +309,23 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-noassert") { - noassert_mode = true; + parse_mode.noassert = true; continue; } if (arg == "-noassume") { - noassume_mode = true; + parse_mode.noassume = true; continue; } if (arg == "-norestrict") { - norestrict_mode = true; + parse_mode.norestrict = true; continue; } if (arg == "-assume-asserts") { - assume_asserts_mode = true; + parse_mode.assume_asserts = true; continue; } if (arg == "-assert-assumes") { - assert_assumes_mode = true; + parse_mode.assert_assumes = true; continue; } if (arg == "-nodisplay") { @@ -329,7 +337,8 @@ struct VerilogFrontend : public Frontend { flag_dump_ast2 = true; flag_dump_vlog1 = true; flag_dump_vlog2 = true; - frontend_verilog_yydebug = true; + flag_debug_lexer = true; + flag_debug_parser = true; continue; } if (arg == "-dump_ast1") { @@ -357,7 +366,9 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-yydebug") { - frontend_verilog_yydebug = true; + flag_yydebug = true; + flag_debug_lexer = true; + flag_debug_parser = true; continue; } if (arg == "-nolatches") { @@ -393,7 +404,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-lib") { - lib_mode = true; + parse_mode.lib = true; defines_map.add("BLACKBOX", ""); continue; } @@ -402,7 +413,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-specify") { - specify_mode = true; + parse_mode.specify = true; continue; } if (arg == "-noopt") { @@ -432,7 +443,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-noautowire") { - default_nettype_wire = false; + parse_state.default_nettype_wire = false; continue; } if (arg == "-setattr" && argidx+1 < args.size()) { @@ -469,76 +480,82 @@ struct VerilogFrontend : public Frontend { break; } - if (formal_mode || !flag_nosynthesis) - defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); + if (parse_mode.formal || !flag_nosynthesis) + defines_map.add(parse_mode.formal ? "FORMAL" : "SYNTHESIS", "1"); extra_args(f, filename, args, argidx); log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); log("Parsing %s%s input from `%s' to AST representation.\n", - formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); + parse_mode.formal ? "formal " : "", parse_mode.sv ? "SystemVerilog" : "Verilog", filename.c_str()); - AST::current_filename = filename; - AST::set_line_num = &frontend_verilog_yyset_lineno; - AST::get_line_num = &frontend_verilog_yyget_lineno; - - current_ast = new AST::AstNode(AST::AST_DESIGN); - - lexin = f; + AST::sv_mode_but_global_and_used_for_literally_one_condition = parse_mode.sv; std::string code_after_preproc; + parse_state.lexin = f; if (!flag_nopp) { - code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs); + code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs, parse_state, parse_mode); if (flag_ppdump) log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); - lexin = new std::istringstream(code_after_preproc); + parse_state.lexin = new std::istringstream(code_after_preproc); } + auto filename_shared = std::make_shared(filename); + auto top_loc = Location(); + top_loc.begin.filename = filename_shared; + parse_state.current_ast = new AST::AstNode(top_loc, AST::AST_DESIGN); + VerilogLexer lexer(&parse_state, &parse_mode, filename_shared); + frontend_verilog_yy::parser parser(&lexer, &parse_state, &parse_mode); + lexer.set_debug(flag_debug_lexer); + parser.set_debug_level(flag_debug_parser ? 1 : 0); + // make package typedefs available to parser - add_package_types(pkg_user_types, design->verilog_packages); + add_package_types(parse_state.pkg_user_types, design->verilog_packages); UserTypeMap global_types_map; - for (auto def : design->verilog_globals) { + for (auto& def : design->verilog_globals) { if (def->type == AST::AST_TYPEDEF) { - global_types_map[def->str] = def; + global_types_map[def->str] = def.get(); } } - log_assert(user_type_stack.empty()); + log_assert(parse_state.user_type_stack.empty()); // use previous global typedefs as bottom level of user type stack - user_type_stack.push_back(std::move(global_types_map)); + parse_state.user_type_stack.push_back(std::move(global_types_map)); // add a new empty type map to allow overriding existing global definitions - user_type_stack.push_back(UserTypeMap()); + parse_state.user_type_stack.push_back(UserTypeMap()); - frontend_verilog_yyset_lineno(1); - frontend_verilog_yyrestart(NULL); - frontend_verilog_yyparse(); - frontend_verilog_yylex_destroy(); + if (flag_yydebug) { + lexer.set_debug(true); + parser.set_debug_level(1); + } + parser.parse(); + // frontend_verilog_yyset_lineno(1); - for (auto &child : current_ast->children) { + for (auto &child : parse_state.current_ast->children) { if (child->type == AST::AST_MODULE) for (auto &attr : attributes) if (child->attributes.count(attr) == 0) - child->attributes[attr] = AST::AstNode::mkconst_int(1, false); + child->attributes[attr] = AST::AstNode::mkconst_int(top_loc, 1, false); } if (flag_nodpi) - error_on_dpi_function(current_ast); + error_on_dpi_function(parse_state.current_ast); - AST::process(design, current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, - flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + AST::process(design, parse_state.current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, + flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, parse_mode.lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, parse_state.default_nettype_wire); if (!flag_nopp) - delete lexin; + delete parse_state.lexin; // only the previous and new global type maps remain - log_assert(user_type_stack.size() == 2); - user_type_stack.clear(); + log_assert(parse_state.user_type_stack.size() == 2); + parse_state.user_type_stack.clear(); - delete current_ast; - current_ast = NULL; + delete parse_state.current_ast; + parse_state.current_ast = NULL; log("Successfully finished Verilog frontend.\n"); } @@ -760,18 +777,3 @@ struct VerilogFileList : public Pass { #endif YOSYS_NAMESPACE_END - -// the yyerror function used by bison to report parser errors -void frontend_verilog_yyerror(char const *fmt, ...) -{ - va_list ap; - char buffer[1024]; - char *p = buffer; - va_start(ap, fmt); - p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); - va_end(ap); - p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); - YOSYS_NAMESPACE_PREFIX log_file_error(YOSYS_NAMESPACE_PREFIX AST::current_filename, frontend_verilog_yyget_lineno(), - "%s", buffer); - exit(1); -} diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 8454e7999..83c0e37a1 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -31,70 +31,33 @@ #include "kernel/yosys.h" #include "frontends/ast/ast.h" + #include #include -#include YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { - // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser - extern struct AST::AstNode *current_ast; + /* Ephemeral context class */ + struct ConstParser { + AST::AstSrcLocType loc; + private: + void log_maybe_loc_error(std::string msg); + void log_maybe_loc_warn(std::string msg); + // divide an arbitrary length decimal number by two and return the rest + int my_decimal_div_by_two(std::vector &digits); + // find the number of significant bits in a binary number (not including the sign bit) + int my_ilog2(int x); + // parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') + void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized); + public: + // convert the Verilog code for a constant to an AST node + std::unique_ptr const2ast(std::string code, char case_type = 0, bool warn_z = false); - // this function converts a Verilog constant to an AST_CONSTANT node - AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false); - - // names of locally typedef'ed types in a stack - typedef std::map UserTypeMap; - extern std::vector user_type_stack; - - // names of package typedef'ed types - extern dict pkg_user_types; - - // state of `default_nettype - extern bool default_nettype_wire; - - // running in SystemVerilog mode - extern bool sv_mode; - - // running in -formal mode - extern bool formal_mode; - - // running in -noassert mode - extern bool noassert_mode; - - // running in -noassume mode - extern bool noassume_mode; - - // running in -norestrict mode - extern bool norestrict_mode; - - // running in -assume-asserts mode - extern bool assume_asserts_mode; - - // running in -assert-assumes mode - extern bool assert_assumes_mode; - - // running in -lib mode - extern bool lib_mode; - - // running in -specify mode - extern bool specify_mode; - - // lexer input stream - extern std::istream *lexin; -} + }; +}; YOSYS_NAMESPACE_END -// the usual bison/flex stuff -extern int frontend_verilog_yydebug; -void frontend_verilog_yyerror(char const *fmt, ...); -void frontend_verilog_yyrestart(FILE *f); -int frontend_verilog_yyparse(void); -int frontend_verilog_yylex_destroy(void); -int frontend_verilog_yyget_lineno(void); -void frontend_verilog_yyset_lineno (int); - #endif diff --git a/frontends/verilog/verilog_lexer.h b/frontends/verilog/verilog_lexer.h new file mode 100644 index 000000000..363c17b76 --- /dev/null +++ b/frontends/verilog/verilog_lexer.h @@ -0,0 +1,48 @@ +#ifndef VERILOG_LEXER_H +#define VERILOG_LEXER_H + +#include "kernel/yosys.h" +#include "frontends/ast/ast.h" +#include "frontends/verilog/verilog_parser.tab.hh" +#include +#include + +#if ! defined(yyFlexLexerOnce) +#define yyFlexLexer frontend_verilog_yyFlexLexer +#include +#endif + +YOSYS_NAMESPACE_BEGIN + +namespace VERILOG_FRONTEND { + using parser = frontend_verilog_yy::parser; + class VerilogLexer : public frontend_verilog_yyFlexLexer { + ParseState* extra; + ParseMode* mode; + parser::location_type out_loc; + public: + VerilogLexer(ParseState* e, ParseMode* m, std::shared_ptr filename) : frontend_verilog_yyFlexLexer(e->lexin), extra(e), mode(m) { + out_loc.begin.filename = filename; + } + ~VerilogLexer() override {} + // autogenerated body due to YY_DECL + parser::symbol_type nextToken(); + // get rid of override virtual function warning + using FlexLexer::yylex; + parser::symbol_type terminate() { + return parser::make_FRONTEND_VERILOG_YYEOF(out_loc); + } + private: + std::shared_ptr current_filename; + std::vector> fn_stack; + std::vector ln_stack; + int LexerInput(char* buf, int max_size) override { + return readsome(*extra->lexin, buf, max_size); + } + }; + +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index e2d7a2cd9..8280c0067 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -32,6 +32,13 @@ * */ +%option c++ +%option yyclass="VerilogLexer" +%option noyywrap +%option nounput +%option yylineno +%option prefix="frontend_verilog_yy" + %{ #ifdef __clang__ @@ -41,78 +48,109 @@ #pragma clang diagnostic ignored "-Wmisleading-indentation" #endif -#include "kernel/log.h" -#include "frontends/verilog/verilog_frontend.h" +#include "frontends/verilog/verilog_lexer.h" #include "frontends/ast/ast.h" -#include "verilog_parser.tab.hh" +#include "frontends/verilog/verilog_location.h" +#include "kernel/log.h" +#include +#include USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; - -#define YYSTYPE FRONTEND_VERILOG_YYSTYPE -#define YYLTYPE FRONTEND_VERILOG_YYLTYPE +using parser = frontend_verilog_yy::parser; YOSYS_NAMESPACE_BEGIN -namespace VERILOG_FRONTEND { - std::vector fn_stack; - std::vector ln_stack; - YYLTYPE real_location; - YYLTYPE old_location; -} +#undef YY_DECL +#define YY_DECL parser::symbol_type VerilogLexer::nextToken() + +#undef yyterminate +#define yyterminate() return terminate() + YOSYS_NAMESPACE_END #define SV_KEYWORD(_tok) \ - if (sv_mode) return _tok; \ + if (mode->sv) return _tok; \ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ - "recognized unless read_verilog is called with -sv!\n", yytext, \ - AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \ - yylval->string = new std::string(std::string("\\") + yytext); \ - return TOK_ID; + "recognized unless read_verilog is called with -sv!\n", YYText(), \ + current_filename->c_str(), yylineno); \ + string_t val = std::make_unique(std::string("\\") + YYText()); \ + return parser::make_TOK_ID(std::move(val), out_loc); #define NON_KEYWORD() \ - yylval->string = new std::string(std::string("\\") + yytext); \ - return TOK_ID; + string_t val = std::make_unique(std::string("\\") + YYText()); \ + return parser::make_TOK_ID(std::move(val), out_loc); -#define YY_INPUT(buf,result,max_size) \ - result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) #define YY_USER_ACTION \ - old_location = real_location; \ - real_location.first_line = real_location.last_line; \ - real_location.first_column = real_location.last_column; \ - for(int i = 0; yytext[i] != '\0'; ++i){ \ - if(yytext[i] == '\n') { \ - real_location.last_line++; \ - real_location.last_column = 1; \ - } \ - else { \ - real_location.last_column++; \ - } \ - } \ - (*yylloc) = real_location; + out_loc.step(); \ + for(int i = 0; YYText()[i] != '\0'; ++i){ \ + if(YYText()[i] == '\n') { \ + out_loc.lines(); \ + } \ + else { \ + out_loc.columns(); \ + } \ + } \ + out_loc.begin.filename = current_filename; \ + out_loc.end.filename = current_filename; #define YY_BREAK \ - (*yylloc) = old_location; \ break; #undef YY_BUF_SIZE #define YY_BUF_SIZE 65536 -extern int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); - -static bool isUserType(std::string &s) +static bool isUserType(ParseState* extra, std::string &s) { // check current scope then outer scopes for a name - for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) { + for (auto it = extra->user_type_stack.rbegin(); it != extra->user_type_stack.rend(); ++it) { if (it->count(s) > 0) { - return true; + return true; } } return false; } -static bool is_hex_dig(char c, int *val) +parser::symbol_type char_tok(char c, parser::location_type loc) { + switch (c) { + case '!': return parser::make_TOK_EXCL(loc); + case '#': return parser::make_TOK_HASH(loc); + case '%': return parser::make_TOK_PERC(loc); + case '&': return parser::make_TOK_AMP(loc); + case '(': return parser::make_TOK_LPAREN(loc); + case ')': return parser::make_TOK_RPAREN(loc); + case '*': return parser::make_TOK_ASTER(loc); + case '+': return parser::make_TOK_PLUS(loc); + case ',': return parser::make_TOK_COMMA(loc); + case '-': return parser::make_TOK_MINUS(loc); + case '.': return parser::make_TOK_DOT(loc); + case '/': return parser::make_TOK_SLASH(loc); + case ':': return parser::make_TOK_COL(loc); + case ';': return parser::make_TOK_SEMICOL(loc); + case '<': return parser::make_TOK_LT(loc); + case '=': return parser::make_TOK_EQ(loc); + case '>': return parser::make_TOK_GT(loc); + case '?': return parser::make_TOK_QUE(loc); + case '@': return parser::make_TOK_AT(loc); + case '[': return parser::make_TOK_LBRA(loc); + case ']': return parser::make_TOK_RBRA(loc); + case '^': return parser::make_TOK_CARET(loc); + case '_': return parser::make_TOK_UNDER(loc); + case '{': return parser::make_TOK_LCURL(loc); + case '|': return parser::make_TOK_PIPE(loc); + case '}': return parser::make_TOK_RCURL(loc); + case '~': return parser::make_TOK_TILDE(loc); + case 'n': return parser::make_TOK_n(loc); + case 'p': return parser::make_TOK_p(loc); + case 'x': return parser::make_TOK_x(loc); + case 'z': return parser::make_TOK_z(loc); + case 0: return parser::make_FRONTEND_VERILOG_YYEOF(loc); + default: + return parser::make_ch_t(c, loc); + } +} +static bool is_hex_dig(char c, int *val, parser::location_type loc) { if ('0' <= c && c <= '9') { *val = c - '0'; @@ -124,7 +162,7 @@ static bool is_hex_dig(char c, int *val) *val = c - 'A' + 0xA; return true; } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { - log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "'%c' not a valid digit in hex escape sequence.\n", c); + log_file_warning(loc.begin.filename->c_str(), loc.begin.line, "'%c' not a valid digit in hex escape sequence.\n", c); *val = 0; // not semantically valid in hex escape... return true; // ...but still processed as part of hex token } @@ -132,13 +170,13 @@ static bool is_hex_dig(char c, int *val) return false; } -static bool is_oct_dig(char c, int *val) +static bool is_oct_dig(char c, int *val, parser::location_type loc) { if ('0' <= c && c <= '7') { *val = c - '0'; return true; } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { - log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "'%c' not a valid digit in octal escape sequence.\n", c); + log_file_warning(loc.begin.filename->c_str(), loc.begin.line, "'%c' not a valid digit in octal escape sequence.\n", c); *val = 0; // not semantically valid in octal escape... return true; // ...but still processed as part of octal token } @@ -146,7 +184,7 @@ static bool is_oct_dig(char c, int *val) return false; } -static std::string *process_str(char *str, int len, bool triple) +static parser::symbol_type process_str(char *str, int len, bool triple, parser::location_type loc) { char *in, *out; // Overwrite input buffer: flex manual states "Actions // are free to modify 'yytext' except for lengthening it". @@ -158,7 +196,7 @@ static std::string *process_str(char *str, int len, bool triple) if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r')) in++; if (!triple) - log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "Multi-line string literals should be triple-quoted or escaped.\n"); + log_file_warning(loc.begin.filename->c_str(), loc.begin.line, "Multi-line string literals should be triple-quoted or escaped.\n"); *out++ = '\n'; break; case '\\': @@ -186,16 +224,16 @@ static std::string *process_str(char *str, int len, bool triple) break; case 'x': int val; - if (in + 1 < str + len && is_hex_dig(in[1], &val)) { + if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) { *out = val; in++; - if (in + 1 < str + len && is_hex_dig(in[1], &val)) { + if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) { *out = *out * 0x10 + val; in++; } out++; } else - log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "ignoring invalid hex escape.\n"); + log_file_warning(loc.begin.filename->c_str(), loc.begin.line, "ignoring invalid hex escape.\n"); break; case '\\': *out++ = '\\'; @@ -213,12 +251,12 @@ static std::string *process_str(char *str, int len, bool triple) int val; *out = *in - '0'; - if (in + 1 < str + len && is_oct_dig(in[1], &val)) { + if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) { *out = *out * 010 + val; in++; - if (in + 1 < str + len && is_oct_dig(in[1], &val)) { + if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) { if (*out >= 040) - log_file_warning(AST::current_filename.c_str(), frontend_verilog_yyget_lineno(), "octal escape exceeds \\377\n"); + log_file_warning(loc.begin.filename->c_str(), loc.begin.line, "octal escape exceeds \\377\n"); *out = *out * 010 + val; in++; } @@ -232,18 +270,11 @@ static std::string *process_str(char *str, int len, bool triple) *out++ = *in; } - return new std::string(str, out - str); + return parser::make_TOK_STRING(std::make_unique(str, out - str), loc); } %} -%option yylineno -%option noyywrap -%option nounput -%option bison-locations -%option bison-bridge -%option prefix="frontend_verilog_yy" - %x COMMENT %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS @@ -256,47 +287,46 @@ FIXED_POINT_NUMBER_NO_DEC [0-9][0-9_]*[eE][-+]?[0-9_]+ TIME_SCALE_SUFFIX [munpf]?s %% + // Initialise comment_caller to something to avoid a "maybe undefined" // warning from GCC. int comment_caller = INITIAL; "`file_push "[^\n]* { fn_stack.push_back(current_filename); - ln_stack.push_back(frontend_verilog_yyget_lineno()); - current_filename = yytext+11; - if (!current_filename.empty() && current_filename.front() == '"') - current_filename = current_filename.substr(1); - if (!current_filename.empty() && current_filename.back() == '"') - current_filename = current_filename.substr(0, current_filename.size()-1); - frontend_verilog_yyset_lineno(0); - yylloc->first_line = yylloc->last_line = 0; - real_location.first_line = real_location.last_line = 0; + ln_stack.push_back(yylineno); + std::string filename = YYText()+11; + if (!filename.empty() && filename.front() == '"') + filename = filename.substr(1); + if (!filename.empty() && filename.back() == '"') + filename = filename.substr(0, filename.size()-1); + current_filename = std::make_shared(filename); + yylineno = (0); + out_loc.begin.line = out_loc.end.line = 0; } "`file_pop"[^\n]*\n { current_filename = fn_stack.back(); fn_stack.pop_back(); - frontend_verilog_yyset_lineno(ln_stack.back()); - yylloc->first_line = yylloc->last_line = ln_stack.back(); - real_location.first_line = real_location.last_line = ln_stack.back(); + yylineno = (ln_stack.back()); + out_loc.begin.line = out_loc.end.line = ln_stack.back(); ln_stack.pop_back(); } "`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { - char *p = yytext + 5; + const char *p = YYText() + 5; while (*p == ' ' || *p == '\t') p++; - frontend_verilog_yyset_lineno(atoi(p)); - yylloc->first_line = yylloc->last_line = atoi(p); - real_location.first_line = real_location.last_line = atoi(p); + yylineno = (atoi(p)); + out_loc.begin.line = out_loc.end.line = atoi(p); while (*p && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; - char *q = *p ? p + 1 : p; + const char *q = *p ? p + 1 : p; while (*q && *q != '"') q++; - current_filename = std::string(p).substr(1, q-p-1); + current_filename = std::make_shared(std::string(p).substr(1, q-p-1)); } "`file_notfound "[^\n]* { - log_error("Can't open include file `%s'!\n", yytext + 15); + log_error("Can't open include file `%s'!\n", YYText() + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ @@ -305,222 +335,223 @@ TIME_SCALE_SUFFIX [munpf]?s "`endcelldefine"[^\n]* /* ignore `endcelldefine */ "`default_nettype"[ \t]+[^ \t\r\n/]+ { - char *p = yytext; + const char *p = YYText(); while (*p != 0 && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; if (!strcmp(p, "none")) - VERILOG_FRONTEND::default_nettype_wire = false; + extra->default_nettype_wire = false; else if (!strcmp(p, "wire")) - VERILOG_FRONTEND::default_nettype_wire = true; + extra->default_nettype_wire = true; else - frontend_verilog_yyerror("Unsupported default nettype: %s", p); + err_at_loc(out_loc, "Unsupported default nettype: %s", p); } "`protect"[^\n]* /* ignore `protect*/ "`endprotect"[^\n]* /* ignore `endprotect*/ "`"[a-zA-Z_$][a-zA-Z0-9_$]* { - frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); + err_at_loc(out_loc, "Unimplemented compiler directive or undefined macro %s.", YYText()); } -"module" { return TOK_MODULE; } -"endmodule" { return TOK_ENDMODULE; } -"function" { return TOK_FUNCTION; } -"endfunction" { return TOK_ENDFUNCTION; } -"task" { return TOK_TASK; } -"endtask" { return TOK_ENDTASK; } -"specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; } -"endspecify" { return TOK_ENDSPECIFY; } -"specparam" { return TOK_SPECPARAM; } -"package" { SV_KEYWORD(TOK_PACKAGE); } -"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } -"interface" { SV_KEYWORD(TOK_INTERFACE); } -"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); } -"modport" { SV_KEYWORD(TOK_MODPORT); } -"parameter" { return TOK_PARAMETER; } -"localparam" { return TOK_LOCALPARAM; } -"defparam" { return TOK_DEFPARAM; } -"assign" { return TOK_ASSIGN; } -"always" { return TOK_ALWAYS; } -"initial" { return TOK_INITIAL; } -"begin" { return TOK_BEGIN; } -"end" { return TOK_END; } -"if" { return TOK_IF; } -"else" { return TOK_ELSE; } -"for" { return TOK_FOR; } -"posedge" { return TOK_POSEDGE; } -"negedge" { return TOK_NEGEDGE; } -"or" { return TOK_OR; } -"case" { return TOK_CASE; } -"casex" { return TOK_CASEX; } -"casez" { return TOK_CASEZ; } -"endcase" { return TOK_ENDCASE; } -"default" { return TOK_DEFAULT; } -"generate" { return TOK_GENERATE; } -"endgenerate" { return TOK_ENDGENERATE; } -"while" { return TOK_WHILE; } -"repeat" { return TOK_REPEAT; } -"automatic" { return TOK_AUTOMATIC; } +"module" { return parser::make_TOK_MODULE(out_loc); } +"endmodule" { return parser::make_TOK_ENDMODULE(out_loc); } +"function" { return parser::make_TOK_FUNCTION(out_loc); } +"endfunction" { return parser::make_TOK_ENDFUNCTION(out_loc); } +"task" { return parser::make_TOK_TASK(out_loc); } +"endtask" { return parser::make_TOK_ENDTASK(out_loc); } +"specify" { return mode->specify ? parser::make_TOK_SPECIFY(out_loc) : parser::make_TOK_IGNORED_SPECIFY(out_loc); } +"endspecify" { return parser::make_TOK_ENDSPECIFY(out_loc); } +"specparam" { return parser::make_TOK_SPECPARAM(out_loc); } +"package" { SV_KEYWORD(parser::make_TOK_PACKAGE(out_loc)); } +"endpackage" { SV_KEYWORD(parser::make_TOK_ENDPACKAGE(out_loc)); } +"import" { SV_KEYWORD(parser::make_TOK_IMPORT(out_loc)); } +"interface" { SV_KEYWORD(parser::make_TOK_INTERFACE(out_loc)); } +"endinterface" { SV_KEYWORD(parser::make_TOK_ENDINTERFACE(out_loc)); } +"modport" { SV_KEYWORD(parser::make_TOK_MODPORT(out_loc)); } +"parameter" { return parser::make_TOK_PARAMETER(out_loc); } +"localparam" { return parser::make_TOK_LOCALPARAM(out_loc); } +"defparam" { return parser::make_TOK_DEFPARAM(out_loc); } +"assign" { return parser::make_TOK_ASSIGN(out_loc); } +"always" { return parser::make_TOK_ALWAYS(out_loc); } +"initial" { return parser::make_TOK_INITIAL(out_loc); } +"begin" { return parser::make_TOK_BEGIN(out_loc); } +"end" { return parser::make_TOK_END(out_loc); } +"if" { return parser::make_TOK_IF(out_loc); } +"else" { return parser::make_TOK_ELSE(out_loc); } +"for" { return parser::make_TOK_FOR(out_loc); } +"posedge" { return parser::make_TOK_POSEDGE(out_loc); } +"negedge" { return parser::make_TOK_NEGEDGE(out_loc); } +"or" { return parser::make_TOK_OR(out_loc); } +"case" { return parser::make_TOK_CASE(out_loc); } +"casex" { return parser::make_TOK_CASEX(out_loc); } +"casez" { return parser::make_TOK_CASEZ(out_loc); } +"endcase" { return parser::make_TOK_ENDCASE(out_loc); } +"default" { return parser::make_TOK_DEFAULT(out_loc); } +"generate" { return parser::make_TOK_GENERATE(out_loc); } +"endgenerate" { return parser::make_TOK_ENDGENERATE(out_loc); } +"while" { return parser::make_TOK_WHILE(out_loc); } +"repeat" { return parser::make_TOK_REPEAT(out_loc); } +"automatic" { return parser::make_TOK_AUTOMATIC(out_loc); } -"unique" { SV_KEYWORD(TOK_UNIQUE); } -"unique0" { SV_KEYWORD(TOK_UNIQUE0); } -"priority" { SV_KEYWORD(TOK_PRIORITY); } +"unique" { SV_KEYWORD(parser::make_TOK_UNIQUE(out_loc)); } +"unique0" { SV_KEYWORD(parser::make_TOK_UNIQUE0(out_loc)); } +"priority" { SV_KEYWORD(parser::make_TOK_PRIORITY(out_loc)); } -"always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } -"always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); } -"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); } +"always_comb" { SV_KEYWORD(parser::make_TOK_ALWAYS_COMB(out_loc)); } +"always_ff" { SV_KEYWORD(parser::make_TOK_ALWAYS_FF(out_loc)); } +"always_latch" { SV_KEYWORD(parser::make_TOK_ALWAYS_LATCH(out_loc)); } /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some global state.. its a mess) */ [a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] { - if (!strcmp(yytext, "default")) - return TOK_DEFAULT; - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_SVA_LABEL; + if (!strcmp(YYText(), "default")) + return parser::make_TOK_DEFAULT(out_loc); + string_t val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_SVA_LABEL(std::move(val), out_loc); } -"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } -"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } -"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); } -"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); } -"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } -"rand" { if (formal_mode) return TOK_RAND; SV_KEYWORD(TOK_RAND); } -"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } -"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } -"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } -"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); } -"final" { SV_KEYWORD(TOK_FINAL); } -"logic" { SV_KEYWORD(TOK_LOGIC); } -"var" { SV_KEYWORD(TOK_VAR); } -"bit" { SV_KEYWORD(TOK_LOGIC); } -"int" { SV_KEYWORD(TOK_INT); } -"byte" { SV_KEYWORD(TOK_BYTE); } -"shortint" { SV_KEYWORD(TOK_SHORTINT); } -"longint" { SV_KEYWORD(TOK_LONGINT); } -"void" { SV_KEYWORD(TOK_VOID); } +"assert" { if (mode->formal) return parser::make_TOK_ASSERT(out_loc); SV_KEYWORD(parser::make_TOK_ASSERT(out_loc)); } +"assume" { if (mode->formal) return parser::make_TOK_ASSUME(out_loc); SV_KEYWORD(parser::make_TOK_ASSUME(out_loc)); } +"cover" { if (mode->formal) return parser::make_TOK_COVER(out_loc); SV_KEYWORD(parser::make_TOK_COVER(out_loc)); } +"restrict" { if (mode->formal) return parser::make_TOK_RESTRICT(out_loc); SV_KEYWORD(parser::make_TOK_RESTRICT(out_loc)); } +"property" { if (mode->formal) return parser::make_TOK_PROPERTY(out_loc); SV_KEYWORD(parser::make_TOK_PROPERTY(out_loc)); } +"rand" { if (mode->formal) return parser::make_TOK_RAND(out_loc); SV_KEYWORD(parser::make_TOK_RAND(out_loc)); } +"const" { if (mode->formal) return parser::make_TOK_CONST(out_loc); SV_KEYWORD(parser::make_TOK_CONST(out_loc)); } +"checker" { if (mode->formal) return parser::make_TOK_CHECKER(out_loc); SV_KEYWORD(parser::make_TOK_CHECKER(out_loc)); } +"endchecker" { if (mode->formal) return parser::make_TOK_ENDCHECKER(out_loc); SV_KEYWORD(parser::make_TOK_ENDCHECKER(out_loc)); } +"bind" { if (mode->formal) return parser::make_TOK_BIND(out_loc); SV_KEYWORD(parser::make_TOK_BIND(out_loc)); } +"final" { SV_KEYWORD(parser::make_TOK_FINAL(out_loc)); } +"logic" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); } +"var" { SV_KEYWORD(parser::make_TOK_VAR(out_loc)); } +"bit" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); } +"int" { SV_KEYWORD(parser::make_TOK_INT(out_loc)); } +"byte" { SV_KEYWORD(parser::make_TOK_BYTE(out_loc)); } +"shortint" { SV_KEYWORD(parser::make_TOK_SHORTINT(out_loc)); } +"longint" { SV_KEYWORD(parser::make_TOK_LONGINT(out_loc)); } +"void" { SV_KEYWORD(parser::make_TOK_VOID(out_loc)); } -"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } -"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } +"eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); } +"s_eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); } -"input" { return TOK_INPUT; } -"output" { return TOK_OUTPUT; } -"inout" { return TOK_INOUT; } -"wire" { return TOK_WIRE; } -"tri" { return TOK_WIRE; } -"wor" { return TOK_WOR; } -"trior" { return TOK_WOR; } -"wand" { return TOK_WAND; } -"triand" { return TOK_WAND; } -"reg" { return TOK_REG; } -"integer" { return TOK_INTEGER; } -"signed" { return TOK_SIGNED; } -"unsigned" { SV_KEYWORD(TOK_UNSIGNED); } -"genvar" { return TOK_GENVAR; } -"real" { return TOK_REAL; } +"input" { return parser::make_TOK_INPUT(out_loc); } +"output" { return parser::make_TOK_OUTPUT(out_loc); } +"inout" { return parser::make_TOK_INOUT(out_loc); } +"wire" { return parser::make_TOK_WIRE(out_loc); } +"tri" { return parser::make_TOK_WIRE(out_loc); } +"wor" { return parser::make_TOK_WOR(out_loc); } +"trior" { return parser::make_TOK_WOR(out_loc); } +"wand" { return parser::make_TOK_WAND(out_loc); } +"triand" { return parser::make_TOK_WAND(out_loc); } +"reg" { return parser::make_TOK_REG(out_loc); } +"integer" { return parser::make_TOK_INTEGER(out_loc); } +"signed" { return parser::make_TOK_SIGNED(out_loc); } +"unsigned" { SV_KEYWORD(parser::make_TOK_UNSIGNED(out_loc)); } +"genvar" { return parser::make_TOK_GENVAR(out_loc); } +"real" { return parser::make_TOK_REAL(out_loc); } -"enum" { SV_KEYWORD(TOK_ENUM); } -"typedef" { SV_KEYWORD(TOK_TYPEDEF); } -"struct" { SV_KEYWORD(TOK_STRUCT); } -"union" { SV_KEYWORD(TOK_UNION); } -"packed" { SV_KEYWORD(TOK_PACKED); } +"enum" { SV_KEYWORD(parser::make_TOK_ENUM(out_loc)); } +"typedef" { SV_KEYWORD(parser::make_TOK_TYPEDEF(out_loc)); } +"struct" { SV_KEYWORD(parser::make_TOK_STRUCT(out_loc)); } +"union" { SV_KEYWORD(parser::make_TOK_UNION(out_loc)); } +"packed" { SV_KEYWORD(parser::make_TOK_PACKED(out_loc)); } {UNSIGNED_NUMBER} { - yylval->string = new std::string(yytext); - return TOK_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_CONSTVAL(std::move(val), out_loc); } \'[01zxZX] { - yylval->string = new std::string(yytext); - return TOK_UNBASED_UNSIZED_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_UNBASED_UNSIZED_CONSTVAL(std::move(val), out_loc); } \'[sS]?[bodhBODH] { BEGIN(BASED_CONST); - yylval->string = new std::string(yytext); - return TOK_BASE; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_BASE(std::move(val), out_loc); } [0-9a-fA-FzxZX?][0-9a-fA-FzxZX?_]* { BEGIN(0); - yylval->string = new std::string(yytext); - return TOK_BASED_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_BASED_CONSTVAL(std::move(val), out_loc); } {FIXED_POINT_NUMBER_DEC} { - yylval->string = new std::string(yytext); - return TOK_REALVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_REALVAL(std::move(val), out_loc); } {FIXED_POINT_NUMBER_NO_DEC} { - yylval->string = new std::string(yytext); - return TOK_REALVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_REALVAL(std::move(val), out_loc); } -\"([^\\"]|\\.|\\\n)*\" { yylval->string = process_str(yytext + 1, yyleng - 2, false); return TOK_STRING; } +\"([^\\"]|\\.|\\\n)*\" { return process_str(yytext + 1, yyleng - 2, false, out_loc); } -\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { yylval->string = process_str(yytext + 3, yyleng - 6, true); return TOK_STRING; } +\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { return process_str(yytext + 3, yyleng - 6, true, out_loc); } and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { - yylval->string = new std::string(yytext); - return TOK_PRIMITIVE; + auto val = std::make_unique(YYText()); + return parser::make_TOK_PRIMITIVE(std::move(val), out_loc); } -supply0 { return TOK_SUPPLY0; } -supply1 { return TOK_SUPPLY1; } +supply0 { return parser::make_TOK_SUPPLY0(out_loc); } +supply1 { return parser::make_TOK_SUPPLY1(out_loc); } "$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { - yylval->string = new std::string(yytext); - return TOK_ID; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) { - if (!specify_mode) REJECT; - yylval->string = new std::string(yytext); - return TOK_ID; + if (!mode->specify) REJECT; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "$"(info|warning|error|fatal) { - yylval->string = new std::string(yytext); - return TOK_MSG_TASKS; + auto val = std::make_unique(YYText()); + return parser::make_TOK_MSG_TASKS(std::move(val), out_loc); } -"$signed" { return TOK_TO_SIGNED; } -"$unsigned" { return TOK_TO_UNSIGNED; } +"$signed" { return parser::make_TOK_TO_SIGNED(out_loc); } +"$unsigned" { return parser::make_TOK_TO_UNSIGNED(out_loc); } [a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_$][a-zA-Z0-9_$]* { // package qualifier - auto s = std::string("\\") + yytext; - if (pkg_user_types.count(s) > 0) { + auto s = std::string("\\") + YYText(); + if (extra->pkg_user_types.count(s) > 0) { // package qualified typedefed name - yylval->string = new std::string(s); - return TOK_PKG_USER_TYPE; + auto val = std::make_unique(s); + return parser::make_TOK_PKG_USER_TYPE(std::move(val), out_loc); } else { // backup before :: just return first part - size_t len = strchr(yytext, ':') - yytext; + size_t len = strchr(YYText(), ':') - YYText(); yyless(len); - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } } [a-zA-Z_$][a-zA-Z0-9_$]* { - auto s = std::string("\\") + yytext; - if (isUserType(s)) { + auto s = std::string("\\") + YYText(); + if (isUserType(extra, s)) { // previously typedefed name - yylval->string = new std::string(s); - return TOK_USER_TYPE; + auto val = std::make_unique(s); + return parser::make_TOK_USER_TYPE(std::move(val), out_loc); } else { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } } [a-zA-Z_$][a-zA-Z0-9_$\.]* { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { @@ -556,7 +587,7 @@ supply1 { return TOK_SUPPLY1; } ); printed_warning = true; } - return TOK_SYNOPSYS_FULL_CASE; + return parser::make_TOK_SYNOPSYS_FULL_CASE(out_loc); } parallel_case { static bool printed_warning = false; @@ -570,119 +601,115 @@ supply1 { return TOK_SUPPLY1; } ); printed_warning = true; } - return TOK_SYNOPSYS_PARALLEL_CASE; + return parser::make_TOK_SYNOPSYS_PARALLEL_CASE(out_loc); } . /* ignore everything else */ "*/" { BEGIN(0); } import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { BEGIN(IMPORT_DPI); - return TOK_DPI_FUNCTION; + return parser::make_TOK_DPI_FUNCTION(out_loc); } [a-zA-Z_$][a-zA-Z0-9_$]* { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } [ \t\r\n] /* ignore whitespaces */ ";" { BEGIN(0); - return *yytext; + return char_tok(*YYText(), out_loc); } . { - return *yytext; + return char_tok(*YYText(), out_loc); } "\\"[^ \t\r\n]+ { - yylval->string = new std::string(yytext); - return TOK_ID; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } -"(*" { return ATTR_BEGIN; } -"*)" { return ATTR_END; } +"(*" { return parser::make_ATTR_BEGIN(out_loc); } +"*)" { return parser::make_ATTR_END(out_loc); } -"{*" { return DEFATTR_BEGIN; } -"*}" { return DEFATTR_END; } +"{*" { return parser::make_DEFATTR_BEGIN(out_loc); } +"*}" { return parser::make_DEFATTR_END(out_loc); } -"**" { return OP_POW; } -"||" { return OP_LOR; } -"&&" { return OP_LAND; } -"==" { return OP_EQ; } -"!=" { return OP_NE; } -"<=" { return OP_LE; } -">=" { return OP_GE; } +"**" { return parser::make_OP_POW(out_loc); } +"||" { return parser::make_OP_LOR(out_loc); } +"&&" { return parser::make_OP_LAND(out_loc); } +"==" { return parser::make_OP_EQ(out_loc); } +"!=" { return parser::make_OP_NE(out_loc); } +"<=" { return parser::make_OP_LE(out_loc); } +">=" { return parser::make_OP_GE(out_loc); } -"===" { return OP_EQX; } -"!==" { return OP_NEX; } +"===" { return parser::make_OP_EQX(out_loc); } +"!==" { return parser::make_OP_NEX(out_loc); } -"~&" { return OP_NAND; } -"~|" { return OP_NOR; } -"~^" { return OP_XNOR; } -"^~" { return OP_XNOR; } +"~&" { return parser::make_OP_NAND(out_loc); } +"~|" { return parser::make_OP_NOR(out_loc); } +"~^" { return parser::make_OP_XNOR(out_loc); } +"^~" { return parser::make_OP_XNOR(out_loc); } -"<<" { return OP_SHL; } -">>" { return OP_SHR; } -"<<<" { return OP_SSHL; } -">>>" { return OP_SSHR; } +"<<" { return parser::make_OP_SHL(out_loc); } +">>" { return parser::make_OP_SHR(out_loc); } +"<<<" { return parser::make_OP_SSHL(out_loc); } +">>>" { return parser::make_OP_SSHR(out_loc); } -"'" { return OP_CAST; } +"'" { return parser::make_OP_CAST(out_loc); } -"::" { return TOK_PACKAGESEP; } -"++" { return TOK_INCREMENT; } -"--" { return TOK_DECREMENT; } +"::" { return parser::make_TOK_PACKAGESEP(out_loc); } +"++" { return parser::make_TOK_INCREMENT(out_loc); } +"--" { return parser::make_TOK_DECREMENT(out_loc); } -"+:" { return TOK_POS_INDEXED; } -"-:" { return TOK_NEG_INDEXED; } +"+:" { return parser::make_TOK_POS_INDEXED(out_loc); } +"-:" { return parser::make_TOK_NEG_INDEXED(out_loc); } -".*" { return TOK_WILDCARD_CONNECT; } +".*" { return parser::make_TOK_WILDCARD_CONNECT(out_loc); } -"|=" { SV_KEYWORD(TOK_BIT_OR_ASSIGN); } -"&=" { SV_KEYWORD(TOK_BIT_AND_ASSIGN); } -"+=" { SV_KEYWORD(TOK_ADD_ASSIGN); } -"-=" { SV_KEYWORD(TOK_SUB_ASSIGN); } -"^=" { SV_KEYWORD(TOK_BIT_XOR_ASSIGN); } -"/=" { SV_KEYWORD(TOK_DIV_ASSIGN); } -"%=" { SV_KEYWORD(TOK_MOD_ASSIGN); } -"*=" { SV_KEYWORD(TOK_MUL_ASSIGN); } -"<<=" { SV_KEYWORD(TOK_SHL_ASSIGN); } -">>=" { SV_KEYWORD(TOK_SHR_ASSIGN); } -"<<<=" { SV_KEYWORD(TOK_SSHL_ASSIGN); } -">>>=" { SV_KEYWORD(TOK_SSHR_ASSIGN); } +"|=" { SV_KEYWORD(parser::make_TOK_BIT_OR_ASSIGN(out_loc)); } +"&=" { SV_KEYWORD(parser::make_TOK_BIT_AND_ASSIGN(out_loc)); } +"+=" { SV_KEYWORD(parser::make_TOK_ADD_ASSIGN(out_loc)); } +"-=" { SV_KEYWORD(parser::make_TOK_SUB_ASSIGN(out_loc)); } +"^=" { SV_KEYWORD(parser::make_TOK_BIT_XOR_ASSIGN(out_loc)); } +"/=" { SV_KEYWORD(parser::make_TOK_DIV_ASSIGN(out_loc)); } +"%=" { SV_KEYWORD(parser::make_TOK_MOD_ASSIGN(out_loc)); } +"*=" { SV_KEYWORD(parser::make_TOK_MUL_ASSIGN(out_loc)); } +"<<=" { SV_KEYWORD(parser::make_TOK_SHL_ASSIGN(out_loc)); } +">>=" { SV_KEYWORD(parser::make_TOK_SHR_ASSIGN(out_loc)); } +"<<<=" { SV_KEYWORD(parser::make_TOK_SSHL_ASSIGN(out_loc)); } +">>>=" { SV_KEYWORD(parser::make_TOK_SSHR_ASSIGN(out_loc)); } [-+]?[=*]> { - if (!specify_mode) REJECT; - yylval->string = new std::string(yytext); - return TOK_SPECIFY_OPER; + if (!mode->specify) REJECT; + auto val = std::make_unique(YYText()); + return parser::make_TOK_SPECIFY_OPER(std::move(val), out_loc); } "&&&" { - if (!specify_mode) return TOK_IGNORED_SPECIFY_AND; - return TOK_SPECIFY_AND; + if (!mode->specify) return parser::make_TOK_IGNORED_SPECIFY_AND(out_loc); + return parser::make_TOK_SPECIFY_AND(out_loc); } -{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } -{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } -{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } +{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } +{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } +{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } "/*" { comment_caller=YY_START; BEGIN(COMMENT); } . /* ignore comment body */ \n /* ignore comment body */ "*/" { BEGIN(comment_caller); } + [ \t\r\n] /* ignore whitespaces */ \\[\r\n] /* ignore continuation sequence */ "//"[^\r\n]* /* ignore one-line comments */ -. { return *yytext; } -<*>. { BEGIN(0); return *yytext; } +. { return char_tok(*YYText(), out_loc); } +<*>. { BEGIN(0); return char_tok(*YYText(), out_loc); } %% -// this is a hack to avoid the 'yyinput defined but not used' error msgs -void *frontend_verilog_avoid_input_warnings() { - return (void*)&yyinput; -} - diff --git a/frontends/verilog/verilog_location.h b/frontends/verilog/verilog_location.h new file mode 100644 index 000000000..8ffe0552d --- /dev/null +++ b/frontends/verilog/verilog_location.h @@ -0,0 +1,97 @@ +#ifndef VERILOG_LOCATION_H +#define VERILOG_LOCATION_H + +#include +#include +#include +#include +#include + +/** + * Provide frontend-wide location tracking like what bison generates + * but using shared_ptr for filename + */ + +struct Position { + std::shared_ptr filename; + int line; + int column; + + Position(std::shared_ptr filename, int line = 1, int column = 1) + : filename(filename), line(line), column(column) {} + Position() = default; + Position(const Position& other) = default; + Position& operator=(const Position& other) = default; + + void advance() { ++column; } + void columns(int count = 1) { + column += count; + } + + void lines(int count = 1) { + line += count; + column = 1; + } + std::string to_string() const { + std::ostringstream oss; + if (filename && !filename->empty()) { + oss << *filename << ":"; + } + oss << line << ":" << column; + return oss.str(); + } +}; + +struct Location { + Position begin; + Position end; + + Location() = default; + Location(const Position& b, const Position& e) + : begin(b), end(e) {} + Location(const Location& other) = default; + Location& operator=(const Location& other) = default; + + void step() { begin = end; } + + void columns(int count = 1) { + end.columns(count); + } + + void lines(int count = 1) { + end.lines(count); + } + std::string to_string() const { + std::ostringstream oss; + bool same_file = (!begin.filename && !end.filename) || + (begin.filename && end.filename && + *begin.filename == *end.filename); + + if (same_file) { + if (begin.filename && !begin.filename->empty()) + oss << *begin.filename << ":"; + + if (begin.line == end.line) { + if (begin.column == end.column) { + oss << begin.line << ":" << begin.column; + } else { + oss << begin.line << ":" << begin.column + << "-" << end.column; + } + } else { + oss << begin.line << ":" << begin.column + << "-" << end.line << ":" << end.column; + } + } else { + oss << begin.to_string() << "-" << end.to_string(); + } + + return oss.str(); + } +}; + +static inline std::ostream& operator<<(std::ostream& os, const Location& loc) { + return os << loc.to_string(); +} + +#endif diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 17edc357d..392d8921a 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -33,367 +33,458 @@ * */ -%require "3.0" +%require "3.6" +%language "c++" +%define api.value.type variant +%define api.prefix {frontend_verilog_yy} +%define api.token.constructor +%define api.location.type {Location} -%{ -#include -#include -#include -#include "frontends/verilog/verilog_frontend.h" -#include "frontends/verilog/verilog_parser.tab.hh" -#include "kernel/log.h" +%param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::VerilogLexer* lexer } +%parse-param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::ParseState* extra } +%parse-param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::ParseMode* mode } -#define YYLEX_PARAM &yylval, &yylloc -USING_YOSYS_NAMESPACE -using namespace AST; -using namespace VERILOG_FRONTEND; - -YOSYS_NAMESPACE_BEGIN -namespace VERILOG_FRONTEND { - int port_counter; - dict port_stubs; - dict *attr_list, default_attr_list; - std::stack *> attr_list_stack; - dict *albuf; - std::vector user_type_stack; - dict pkg_user_types; - std::vector ast_stack; - struct AstNode *astbuf1, *astbuf2, *astbuf3; - struct AstNode *current_function_or_task; - struct AstNode *current_ast, *current_ast_mod; - int current_function_or_task_port_id; - std::vector case_type_stack; - bool do_not_require_port_stubs; - bool default_nettype_wire; - bool sv_mode, formal_mode, lib_mode, specify_mode; - bool noassert_mode, noassume_mode, norestrict_mode; - bool assume_asserts_mode, assert_assumes_mode; - bool current_wire_rand, current_wire_const; - bool current_modport_input, current_modport_output; - std::istream *lexin; +%code requires { + #include "kernel/yosys_common.h" + #include "frontends/verilog/verilog_error.h" + #include "frontends/verilog/verilog_location.h" + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { + struct ParseState; + struct ParseMode; + class VerilogLexer; + }; + YOSYS_NAMESPACE_END } -YOSYS_NAMESPACE_END -#define SET_AST_NODE_LOC(WHICH, BEGIN, END) \ - do { (WHICH)->location.first_line = (BEGIN).first_line; \ - (WHICH)->location.first_column = (BEGIN).first_column; \ - (WHICH)->location.last_line = (END).last_line; \ - (WHICH)->location.last_column = (END).last_column; } while(0) +%code provides { + USING_YOSYS_NAMESPACE; + using namespace AST; + using namespace VERILOG_FRONTEND; + using parser = frontend_verilog_yy::parser; + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { + typedef std::map UserTypeMap; + struct ParseState { + int port_counter; + dict port_stubs; + dict> *attr_list, default_attr_list; + std::stack> *> attr_list_stack; + dict> *albuf; + std::vector user_type_stack; + dict pkg_user_types; + std::vector ast_stack; + std::unique_ptr astbuf1, astbuf2, astbuf3; + AstNode* cell_hack; + AstNode* member_hack; + struct AstNode *current_function_or_task; + struct AstNode *current_ast, *current_ast_mod; + int current_function_or_task_port_id; + std::vector case_type_stack; + bool do_not_require_port_stubs; + bool current_wire_rand, current_wire_const; + bool current_modport_input, current_modport_output; + bool default_nettype_wire = true; + std::istream* lexin; -#define SET_RULE_LOC(LHS, BEGIN, END) \ - do { (LHS).first_line = (BEGIN).first_line; \ - (LHS).first_column = (BEGIN).first_column; \ - (LHS).last_line = (END).last_line; \ - (LHS).last_column = (END).last_column; } while(0) + AstNode* saveChild(std::unique_ptr child); + AstNode* pushChild(std::unique_ptr child); + void addWiretypeNode(std::string *name, AstNode* node); + void addTypedefNode(std::string *name, std::unique_ptr node); + void enterTypeScope(); + void exitTypeScope(); + bool isInLocalScope(const std::string *name); + void rewriteGenForDeclInit(AstNode *loop); + void ensureAsgnExprAllowed(const parser::location_type loc, bool sv_mode); + const AstNode *addIncOrDecStmt(dict> *stmt_attr, + std::unique_ptr lhs, + dict> *op_attr, AST::AstNodeType op, + parser::location_type loc); + std::unique_ptr addIncOrDecExpr(std::unique_ptr lhs, dict> *attr, AST::AstNodeType op, parser::location_type loc, bool undo, bool sv_mode); + // add a binary operator assignment statement, e.g., a += b + std::unique_ptr addAsgnBinopStmt(dict> *attr, std::unique_ptr eq_lhs, AST::AstNodeType op, std::unique_ptr rhs); + }; + struct ParseMode { + bool noassert = false; + bool noassume = false; + bool norestrict = false; + bool sv = false; + bool formal = false; + bool lib = false; + bool specify = false; + bool assume_asserts = false; + bool assert_assumes = false; + }; + }; + YOSYS_NAMESPACE_END +} -int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); +%code { + #include + #include + #include + #include + #include "kernel/log.h" + #include "frontends/verilog/verilog_lexer.h" -static void append_attr(AstNode *ast, dict *al) -{ - for (auto &it : *al) { - if (ast->attributes.count(it.first) > 0) - delete ast->attributes[it.first]; - ast->attributes[it.first] = it.second; + USING_YOSYS_NAMESPACE + using namespace AST; + using namespace VERILOG_FRONTEND; + + // Silly little C adapter between C++ bison and C++ flex + auto frontend_verilog_yylex(YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::VerilogLexer* lexer) { + return lexer->nextToken(); } - delete al; -} -static void append_attr_clone(AstNode *ast, dict *al) -{ - for (auto &it : *al) { - if (ast->attributes.count(it.first) > 0) - delete ast->attributes[it.first]; - ast->attributes[it.first] = it.second->clone(); - } -} + #define SET_AST_NODE_LOC(WHICH, BEGIN, END) (WHICH)->location = location_range(BEGIN, END) -static void free_attr(dict *al) -{ - for (auto &it : *al) - delete it.second; - delete al; -} + #define SET_RULE_LOC(LHS, BEGIN, END) \ + do { (LHS).begin = BEGIN.begin; \ + (LHS).end = (END).end; } while(0) -struct specify_target { - char polarity_op; - AstNode *dst, *dat; -}; + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { -struct specify_triple { - AstNode *t_min, *t_avg, *t_max; -}; - -struct specify_rise_fall { - specify_triple rise; - specify_triple fall; -}; - -static void addWiretypeNode(std::string *name, AstNode *node) -{ - log_assert(node); - node->is_custom_type = true; - node->children.push_back(new AstNode(AST_WIRETYPE)); - node->children.back()->str = *name; - delete name; -} - -static void addTypedefNode(std::string *name, AstNode *node) -{ - log_assert(node); - auto *tnode = new AstNode(AST_TYPEDEF, node); - tnode->str = *name; - auto &user_types = user_type_stack.back(); - user_types[*name] = tnode; - if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) { - // typedef inside a package so we need the qualified name - auto qname = current_ast_mod->str + "::" + (*name).substr(1); - pkg_user_types[qname] = tnode; - } - delete name; - ast_stack.back()->children.push_back(tnode); -} - -static void enterTypeScope() -{ - user_type_stack.push_back(UserTypeMap()); -} - -static void exitTypeScope() -{ - user_type_stack.pop_back(); -} - -static bool isInLocalScope(const std::string *name) -{ - // tests if a name was declared in the current block scope - auto &user_types = user_type_stack.back(); - return (user_types.count(*name) > 0); -} - -static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true) -{ - auto range = new AstNode(AST_RANGE); - range->children.push_back(AstNode::mkconst_int(msb, true)); - range->children.push_back(AstNode::mkconst_int(lsb, true)); - range->is_signed = isSigned; - return range; -} - -static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true) -{ - auto range = makeRange(msb, lsb, isSigned); - parent->children.push_back(range); -} - -static AstNode *checkRange(AstNode *type_node, AstNode *range_node) -{ - if (type_node->range_left >= 0 && type_node->range_right >= 0) { - // type already restricts the range - if (range_node) { - frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); + static Location location_range(Location begin, Location end) { + return Location(begin.begin, end.end); } - else { - range_node = makeRange(type_node->range_left, type_node->range_right, false); - } - } - if (range_node) { - bool valid = true; - if (range_node->type == AST_RANGE) { - valid = range_node->children.size() == 2; - } else { // AST_MULTIRANGE - for (auto child : range_node->children) { - valid = valid && child->children.size() == 2; + static void append_attr(AstNode *ast, dict> *al) + { + for (auto &it : *al) { + ast->attributes[it.first] = std::move(it.second); + } + delete al; + } + + static void append_attr_clone(AstNode *ast, dict> *al) + { + for (auto &it : *al) { + ast->attributes[it.first] = it.second->clone(); } } - if (!valid) - frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [:]"); - } - return range_node; -} + static void free_attr(dict> *al) + { + delete al; + } -static void rewriteRange(AstNode *rangeNode) -{ - if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { - // SV array size [n], rewrite as [0:n-1] - rangeNode->children.push_back(new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true))); - rangeNode->children[0] = AstNode::mkconst_int(0, false); - } -} + static std::unique_ptr makeRange(parser::location_type loc, int msb = 31, int lsb = 0, bool isSigned = true) + { + auto range = std::make_unique(loc, AST_RANGE); + range->children.push_back(AstNode::mkconst_int(loc, msb, true)); + range->children.push_back(AstNode::mkconst_int(loc, lsb, true)); + range->is_signed = isSigned; + return range; + } -static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) -{ - node->type = AST_MEMORY; - if (rangeNode->type == AST_MULTIRANGE) { - for (auto *itr : rangeNode->children) - rewriteRange(itr); - } else - rewriteRange(rangeNode); - node->children.push_back(rangeNode); -} + static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true) + { + auto range = makeRange(parent->location, msb, lsb, isSigned); + parent->children.push_back(std::move(range)); + } -static void checkLabelsMatch(const char *element, const std::string *before, const std::string *after) -{ - if (!before && after) - frontend_verilog_yyerror("%s missing where end label (%s) was given.", - element, after->c_str() + 1); - if (before && after && *before != *after) - frontend_verilog_yyerror("%s (%s) and end label (%s) don't match.", - element, before->c_str() + 1, after->c_str() + 1); -} + static std::unique_ptr checkRange(AstNode *type_node, std::unique_ptr range_node) + { + if (type_node->range_left >= 0 && type_node->range_right >= 0) { + // type already restricts the range + if (range_node) { + err_at_loc(type_node->location, "integer/genvar types cannot have packed dimensions."); + } + else { + range_node = makeRange(type_node->location, type_node->range_left, type_node->range_right, false); + } + } -// This transforms a loop like -// for (genvar i = 0; i < 10; i++) begin : blk -// to -// genvar _i; -// for (_i = 0; _i < 10; _i++) begin : blk -// localparam i = _i; -// where `_i` is actually some auto-generated name. -static void rewriteGenForDeclInit(AstNode *loop) -{ - // check if this generate for loop contains an inline declaration - log_assert(loop->type == AST_GENFOR); - AstNode *decl = loop->children[0]; - if (decl->type == AST_ASSIGN_EQ) - return; - log_assert(decl->type == AST_GENVAR); - log_assert(loop->children.size() == 5); + if (range_node) { + bool valid = true; + if (range_node->type == AST_RANGE) { + valid = range_node->children.size() == 2; + } else { // AST_MULTIRANGE + for (auto& child : range_node->children) { + valid = valid && child->children.size() == 2; + } + } + if (!valid) + err_at_loc(type_node->location, "wire/reg/logic packed dimension must be of the form [:]"); + } - // identify each component of the loop - AstNode *init = loop->children[1]; - AstNode *cond = loop->children[2]; - AstNode *incr = loop->children[3]; - AstNode *body = loop->children[4]; - log_assert(init->type == AST_ASSIGN_EQ); - log_assert(incr->type == AST_ASSIGN_EQ); - log_assert(body->type == AST_GENBLOCK); + return range_node; + } - // create a unique name for the genvar - std::string old_str = decl->str; - std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str()); + static void rewriteRange(AstNode *rangeNode) + { + if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { + // SV array size [n], rewrite as [0:n-1] + rangeNode->children.push_back(std::make_unique(rangeNode->location, AST_SUB, std::move(rangeNode->children[0]), AstNode::mkconst_int(rangeNode->location, 1, true))); + rangeNode->children[0] = AstNode::mkconst_int(rangeNode->location, 0, false); + } + } - // rename and move the genvar declaration to the containing description - decl->str = new_str; - loop->children.erase(loop->children.begin()); - log_assert(current_ast_mod != nullptr); - current_ast_mod->children.push_back(decl); + static void rewriteAsMemoryNode(AstNode *node, std::unique_ptr rangeNode) + { + node->type = AST_MEMORY; + if (rangeNode->type == AST_MULTIRANGE) { + for (auto& child : rangeNode->children) + rewriteRange(child.get()); + } else + rewriteRange(rangeNode.get()); + node->children.push_back(std::move(rangeNode)); + } - // create a new localparam with old name so that the items in the loop - // can simply use the old name and shadow it as necessary - AstNode *indirect = new AstNode(AST_LOCALPARAM); - indirect->str = old_str; - AstNode *ident = new AstNode(AST_IDENTIFIER); - ident->str = new_str; - indirect->children.push_back(ident); + static void checkLabelsMatch(const frontend_verilog_yy::parser::location_type& loc, const char *element, const std::string* before, const std::string *after) + { + if (!before && after) + err_at_loc(loc, "%s missing where end label (%s) was given.", + element, after->c_str() + 1); + if (before && after && *before != *after) + err_at_loc(loc, "%s (%s) and end label (%s) don't match.", + element, before->c_str() + 1, after->c_str() + 1); + } - body->children.insert(body->children.begin(), indirect); + AstNode* ParseState::saveChild(std::unique_ptr child) { + auto* child_leaky = child.get(); + ast_stack.back()->children.push_back(std::move(child)); + return child_leaky; + } + AstNode* ParseState::pushChild(std::unique_ptr child) { + auto* child_leaky = saveChild(std::move(child)); + ast_stack.push_back(child_leaky); + return child_leaky; + } - // only perform the renaming for the initialization, guard, and - // incrementation to enable proper shadowing of the synthetic localparam - std::function substitute = [&](AstNode *node) { - if (node->type == AST_IDENTIFIER && node->str == old_str) - node->str = new_str; - for (AstNode *child : node->children) - substitute(child); + void ParseState::addWiretypeNode(std::string *name, AstNode* node) + { + log_assert(node); + node->is_custom_type = true; + node->children.push_back(std::make_unique(node->location, AST_WIRETYPE)); + node->children.back()->str = *name; + } + + void ParseState::addTypedefNode(std::string *name, std::unique_ptr node) + { + log_assert((bool)node); + AstNode* tnode = saveChild(std::make_unique(node->location, AST_TYPEDEF, std::move(node))); + log_assert((bool)name); + tnode->str = *name; + auto &user_types = user_type_stack.back(); + user_types[*name] = tnode; + if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) { + // typedef inside a package so we need the qualified name + auto qname = current_ast_mod->str + "::" + (*name).substr(1); + pkg_user_types[qname] = tnode; + } + } + + void ParseState::enterTypeScope() + { + user_type_stack.push_back(UserTypeMap()); + } + + void ParseState::exitTypeScope() + { + user_type_stack.pop_back(); + } + + bool ParseState::isInLocalScope(const std::string *name) + { + // tests if a name was declared in the current block scope + auto &user_types = user_type_stack.back(); + return (user_types.count(*name) > 0); + } + + // This transforms a loop like + // for (genvar i = 0; i < 10; i++) begin : blk + // to + // genvar _i; + // for (_i = 0; _i < 10; _i++) begin : blk + // localparam i = _i; + // where `_i` is actually some auto-generated name. + void ParseState::rewriteGenForDeclInit(AstNode *loop) + { + // check if this generate for loop contains an inline declaration + log_assert(loop->type == AST_GENFOR); + auto& decl = loop->children[0]; + if (decl->type == AST_ASSIGN_EQ) + return; + + log_assert(decl->type == AST_GENVAR); + log_assert(loop->children.size() == 5); + + // identify each component of the loop + AstNode *init = loop->children[1].get(); + AstNode *cond = loop->children[2].get(); + AstNode *incr = loop->children[3].get(); + AstNode *body = loop->children[4].get(); + log_assert(init->type == AST_ASSIGN_EQ); + log_assert(incr->type == AST_ASSIGN_EQ); + log_assert(body->type == AST_GENBLOCK); + + // create a unique name for the genvar + std::string old_str = decl->str; + std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str()); + + // rename and move the genvar declaration to the containing description + decl->str = new_str; + log_assert(current_ast_mod != nullptr); + current_ast_mod->children.push_back(std::move(decl)); + + // create a new localparam with old name so that the items in the loop + // can simply use the old name and shadow it as necessary + auto indirect = std::make_unique(loop->location, AST_LOCALPARAM); + indirect->str = old_str; + auto ident = std::make_unique(loop->location, AST_IDENTIFIER); + ident->str = new_str; + indirect->children.push_back(std::move(ident)); + + body->children.insert(body->children.begin(), std::move(indirect)); + + // only perform the renaming for the initialization, guard, and + // incrementation to enable proper shadowing of the synthetic localparam + std::function substitute = [&](AstNode *node) { + if (node->type == AST_IDENTIFIER && node->str == old_str) + node->str = new_str; + for (auto& child : node->children) + substitute(child.get()); + }; + substitute(init); + substitute(cond); + substitute(incr); + loop->children.erase(loop->children.begin()); + } + + void ParseState::ensureAsgnExprAllowed(const parser::location_type loc, bool sv_mode) + { + if (!sv_mode) + err_at_loc(loc, "Assignments within expressions are only supported in SystemVerilog mode."); + if (ast_stack.back()->type != AST_BLOCK) + err_at_loc(loc, "Assignments within expressions are only permitted within procedures."); + } + + // add a pre/post-increment/decrement statement + const AstNode *ParseState::addIncOrDecStmt(dict> *stmt_attr, + std::unique_ptr lhs, + dict> *op_attr, AST::AstNodeType op, + Location loc) + { + auto one = AstNode::mkconst_int(loc, 1, true); + auto rhs = std::make_unique(loc, op, lhs->clone(), std::move(one)); + if (op_attr != nullptr) + append_attr(rhs.get(), op_attr); + auto stmt_owned = std::make_unique(loc, AST_ASSIGN_EQ, std::move(lhs), std::move(rhs)); + auto* stmt = stmt_owned.get(); + ast_stack.back()->children.push_back(std::move(stmt_owned)); + if (stmt_attr != nullptr) + append_attr(stmt, stmt_attr); + return stmt; + } + + // create a pre/post-increment/decrement expression, and add the corresponding statement + std::unique_ptr ParseState::addIncOrDecExpr(std::unique_ptr lhs, dict> *attr, AST::AstNodeType op, Location loc, bool undo, bool sv_mode) + { + ensureAsgnExprAllowed(loc, sv_mode); + const AstNode *stmt = addIncOrDecStmt(nullptr, std::move(lhs), attr, op, loc); + log_assert(stmt->type == AST_ASSIGN_EQ); + auto expr = stmt->children[0]->clone(); + if (undo) { + auto one = AstNode::mkconst_int(loc, 1, false, 1); + auto minus_one = std::make_unique(loc, AST_NEG, std::move(one)); + expr = std::make_unique(loc, op, std::move(expr), std::move(minus_one)); + } + return expr; + } + + // add a binary operator assignment statement, e.g., a += b + std::unique_ptr ParseState::addAsgnBinopStmt(dict> *attr, std::unique_ptr eq_lhs, AST::AstNodeType op, std::unique_ptr rhs) + { + Location loc = location_range(eq_lhs->location, rhs->location); + if (op == AST_SHIFT_LEFT || op == AST_SHIFT_RIGHT || + op == AST_SHIFT_SLEFT || op == AST_SHIFT_SRIGHT) { + rhs = std::make_unique(rhs->location, AST_TO_UNSIGNED, std::move(rhs)); + } + auto binop_lhs = eq_lhs->clone(); + auto eq_rhs_owned = std::make_unique(loc, op, std::move(binop_lhs), std::move(rhs)); + auto ret_lhs = eq_lhs->clone(); + auto stmt_owned = std::make_unique(loc, AST_ASSIGN_EQ, std::move(eq_lhs), std::move(eq_rhs_owned)); + auto* stmt = stmt_owned.get(); + ast_stack.back()->children.push_back(std::move(stmt_owned)); + if (attr != nullptr) + append_attr(stmt, attr); + return ret_lhs; + } }; - substitute(init); - substitute(cond); - substitute(incr); -} + YOSYS_NAMESPACE_END -static void ensureAsgnExprAllowed() -{ - if (!sv_mode) - frontend_verilog_yyerror("Assignments within expressions are only supported in SystemVerilog mode."); - if (ast_stack.back()->type != AST_BLOCK) - frontend_verilog_yyerror("Assignments within expressions are only permitted within procedures."); -} - -// add a pre/post-increment/decrement statement -static const AstNode *addIncOrDecStmt(dict *stmt_attr, AstNode *lhs, - dict *op_attr, AST::AstNodeType op, - YYLTYPE begin, YYLTYPE end) -{ - AstNode *one = AstNode::mkconst_int(1, true); - AstNode *rhs = new AstNode(op, lhs->clone(), one); - if (op_attr != nullptr) - append_attr(rhs, op_attr); - AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs); - SET_AST_NODE_LOC(stmt, begin, end); - if (stmt_attr != nullptr) - append_attr(stmt, stmt_attr); - ast_stack.back()->children.push_back(stmt); - return stmt; -} - -// create a pre/post-increment/decrement expression, and add the corresponding statement -static AstNode *addIncOrDecExpr(AstNode *lhs, dict *attr, AST::AstNodeType op, YYLTYPE begin, YYLTYPE end, bool undo) -{ - ensureAsgnExprAllowed(); - const AstNode *stmt = addIncOrDecStmt(nullptr, lhs, attr, op, begin, end); - log_assert(stmt->type == AST_ASSIGN_EQ); - AstNode *expr = stmt->children[0]->clone(); - if (undo) { - AstNode *one = AstNode::mkconst_int(1, false, 1); - AstNode *minus_one = new AstNode(AST_NEG, one); - expr = new AstNode(op, expr, minus_one); + void frontend_verilog_yy::parser::error(const frontend_verilog_yy::parser::location_type& loc, const std::string& msg) + { + err_at_loc(loc, "%s", msg.c_str()); } - SET_AST_NODE_LOC(expr, begin, end); - return expr; } -// add a binary operator assignment statement, e.g., a += b -static const AstNode *addAsgnBinopStmt(dict *attr, AstNode *lhs, AST::AstNodeType op, AstNode *rhs, YYLTYPE begin, YYLTYPE end) -{ - SET_AST_NODE_LOC(rhs, end, end); - if (op == AST_SHIFT_LEFT || op == AST_SHIFT_RIGHT || - op == AST_SHIFT_SLEFT || op == AST_SHIFT_SRIGHT) { - rhs = new AstNode(AST_TO_UNSIGNED, rhs); - SET_AST_NODE_LOC(rhs, end, end); - } - rhs = new AstNode(op, lhs->clone(), rhs); - AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs); - SET_AST_NODE_LOC(rhs, begin, end); - SET_AST_NODE_LOC(stmt, begin, end); - ast_stack.back()->children.push_back(stmt); - if (attr != nullptr) - append_attr(stmt, attr); - return lhs; -} - -%} - -%define api.prefix {frontend_verilog_yy} -%define api.pure - -/* The union is defined in the header, so we need to provide all the - * includes it requires - */ %code requires { -#include -#include -#include "frontends/verilog/verilog_frontend.h" + #include + #include + #include + #include "frontends/verilog/verilog_frontend.h" + + struct specify_target { + char polarity_op; + std::unique_ptr dst, dat; + specify_target& operator=(specify_target&& other) noexcept { + if (this != &other) { + dst = std::move(other.dst); + dat = std::move(other.dat); + polarity_op = other.polarity_op; + } + return *this; + } + }; + + struct specify_triple { + std::unique_ptr t_min, t_avg, t_max; + specify_triple& operator=(specify_triple&& other) noexcept { + if (this != &other) { + t_min = std::move(other.t_min); + t_avg = std::move(other.t_avg); + t_max = std::move(other.t_max); + } + return *this; + } + }; + + struct specify_rise_fall { + specify_triple rise; + specify_triple fall; + }; + + using string_t = std::unique_ptr; + using ast_t = std::unique_ptr; + using al_t = YOSYS_NAMESPACE_PREFIX dict>*; + using specify_target_ptr_t = std::unique_ptr; + using specify_triple_ptr_t = std::unique_ptr; + using specify_rise_fall_ptr_t = std::unique_ptr; + using boolean_t = bool; + using ch_t = char; + using integer_t = int; + using ast_node_type_t = YOSYS_NAMESPACE_PREFIX AST::AstNodeType; } -%union { - std::string *string; - struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; - YOSYS_NAMESPACE_PREFIX dict *al; - struct specify_target *specify_target_ptr; - struct specify_triple *specify_triple_ptr; - struct specify_rise_fall *specify_rise_fall_ptr; - bool boolean; - char ch; - int integer; - YOSYS_NAMESPACE_PREFIX AST::AstNodeType ast_node_type; -} +%token string_t "string" +%token ast_t +%token al_t +%token specify_target_ptr_t "specify target" +%token specify_triple_ptr_t "specify triple" +%token specify_rise_fall_ptr_t "specify rise and fall" +%token boolean_t "boolean" +%token ch_t "invalid token" +%token integer_t "integer" +%token ast_node_type_t -%token TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE -%token TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS -%token TOK_BASE TOK_BASED_CONSTVAL TOK_UNBASED_UNSIZED_CONSTVAL -%token TOK_USER_TYPE TOK_PKG_USER_TYPE +%token TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE +%token TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS +%token TOK_BASE TOK_BASED_CONSTVAL TOK_UNBASED_UNSIZED_CONSTVAL +%token TOK_USER_TYPE TOK_PKG_USER_TYPE %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM @@ -418,42 +509,75 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN %token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN %token TOK_BIND TOK_TIME_SCALE +%token TOK_IMPORT -%type range range_or_multirange non_opt_range non_opt_multirange -%type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type -%type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number -%type type_name -%type opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type -%type opt_property always_comb_or_latch always_or_always_ff -%type opt_signedness_default_signed opt_signedness_default_unsigned -%type integer_atom_type integer_vector_type -%type attr if_attr case_attr -%type struct_union -%type asgn_binop inc_or_dec_op -%type genvar_identifier +%token TOK_EXCL "'!'" +%token TOK_HASH "'#'" +%token TOK_PERC "'%'" +%token TOK_AMP "'&'" +%token TOK_LPAREN "'('" +%token TOK_RPAREN "')'" +%token TOK_ASTER "'*'" +%token TOK_PLUS "'+'" +%token TOK_COMMA "','" +%token TOK_MINUS "'-'" +%token TOK_DOT "'.'" +%token TOK_SLASH "'/'" +%token TOK_COL "':'" +%token TOK_SEMICOL "';'" +%token TOK_LT "'<'" +%token TOK_EQ "'='" +%token TOK_GT "'>'" +%token TOK_QUE "'?'" +%token TOK_AT "'@'" +%token TOK_LBRA "'['" +%token TOK_RBRA "']'" +%token TOK_CARET "'^'" +%token TOK_UNDER "'_'" +%token TOK_LCURL "'{'" +%token TOK_PIPE "'|'" +%token TOK_RCURL "'}'" +%token TOK_TILDE "'~'" +%token TOK_n "'n'" +%token TOK_p "'p'" +%token TOK_x "'x'" +%token TOK_z "'z'" -%type specify_target -%type specify_triple specify_opt_triple -%type specify_rise_fall -%type specify_if specify_condition -%type specify_edge +%type range range_or_multirange non_opt_range non_opt_multirange +%type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type +%type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number +%type type_name +%type opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type +%type opt_property always_comb_or_latch always_or_always_ff +%type opt_signedness_default_signed opt_signedness_default_unsigned +%type integer_atom_type integer_vector_type +%type attr if_attr case_attr +%type struct_union +%type asgn_binop inc_or_dec_op +%type genvar_identifier + +%type specify_target +%type specify_triple specify_opt_triple +%type specify_rise_fall +%type specify_if specify_condition +%type specify_edge // operator precedence from low to high %left OP_LOR %left OP_LAND -%left '|' OP_NOR -%left '^' OP_XNOR -%left '&' OP_NAND +%left TOK_PIPE OP_NOR +%left TOK_CARET OP_XNOR +%left TOK_AMP OP_NAND %left OP_EQ OP_NE OP_EQX OP_NEX -%left '<' OP_LE OP_GE '>' +%left TOK_LT OP_LE OP_GE TOK_GT %left OP_SHL OP_SHR OP_SSHL OP_SSHR -%left '+' '-' -%left '*' '/' '%' +%left TOK_PLUS TOK_MINUS +%left TOK_ASTER TOK_SLASH TOK_PERC %left OP_POW %precedence OP_CAST %precedence UNARY_OPS -%define parse.error verbose +%define parse.error detailed %define parse.lac full %precedence FAKE_THEN @@ -465,15 +589,13 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %% input: { - (void)frontend_verilog_yynerrs; - ast_stack.clear(); - ast_stack.push_back(current_ast); + (void)yynerrs_; + extra->ast_stack.clear(); + extra->ast_stack.push_back(extra->current_ast); } design { - ast_stack.pop_back(); - log_assert(GetSize(ast_stack) == 0); - for (auto &it : default_attr_list) - delete it.second; - default_attr_list.clear(); + extra->ast_stack.pop_back(); + log_assert(GetSize(extra->ast_stack) == 0); + extra->default_attr_list.clear(); }; design: @@ -484,24 +606,25 @@ design: localparam_decl design | typedef_decl design | package design | + import_stmt design | interface design | bind_directive design | %empty; attr: { - if (attr_list != nullptr) - attr_list_stack.push(attr_list); - attr_list = new dict; - for (auto &it : default_attr_list) - (*attr_list)[it.first] = it.second->clone(); + if (extra->attr_list != nullptr) + extra->attr_list_stack.push(extra->attr_list); + extra->attr_list = new dict>; + for (auto &it : extra->default_attr_list) + (*extra->attr_list)[it.first] = it.second->clone(); } attr_opt { - $$ = attr_list; - if (!attr_list_stack.empty()) { - attr_list = attr_list_stack.top(); - attr_list_stack.pop(); + $$ = extra->attr_list; + if (!extra->attr_list_stack.empty()) { + extra->attr_list = extra->attr_list_stack.top(); + extra->attr_list_stack.pop(); } else - attr_list = nullptr; + extra->attr_list = nullptr; }; attr_opt: @@ -512,20 +635,18 @@ attr_opt: defattr: DEFATTR_BEGIN { - if (attr_list != nullptr) - attr_list_stack.push(attr_list); - attr_list = new dict; - for (auto &it : default_attr_list) - delete it.second; - default_attr_list.clear(); + if (extra->attr_list != nullptr) + extra->attr_list_stack.push(extra->attr_list); + extra->attr_list = new dict>; + extra->default_attr_list.clear(); } opt_attr_list { - attr_list->swap(default_attr_list); - delete attr_list; - if (!attr_list_stack.empty()) { - attr_list = attr_list_stack.top(); - attr_list_stack.pop(); + extra->attr_list->swap(extra->default_attr_list); + delete extra->attr_list; + if (!extra->attr_list_stack.empty()) { + extra->attr_list = extra->attr_list_stack.top(); + extra->attr_list_stack.pop(); } else - attr_list = nullptr; + extra->attr_list = nullptr; } DEFATTR_END; opt_attr_list: @@ -533,195 +654,170 @@ opt_attr_list: attr_list: attr_assign | - attr_list ',' attr_assign; + attr_list TOK_COMMA attr_assign; attr_assign: hierarchical_id { - if (attr_list->count(*$1) != 0) - delete (*attr_list)[*$1]; - (*attr_list)[*$1] = AstNode::mkconst_int(1, false); - delete $1; + (*extra->attr_list)[*$1] = AstNode::mkconst_int(@1, 1, false); } | - hierarchical_id '=' expr { - if (attr_list->count(*$1) != 0) - delete (*attr_list)[*$1]; - (*attr_list)[*$1] = $3; - delete $1; + hierarchical_id TOK_EQ expr { + (*extra->attr_list)[*$1] = std::move($3); }; hierarchical_id: TOK_ID { - $$ = $1; + $$ = std::move($1); } | hierarchical_id TOK_PACKAGESEP TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "::" + $3->substr(1); else *$1 += "::" + *$3; - delete $3; - $$ = $1; + $$ = std::move($1); } | - hierarchical_id '.' TOK_ID { + hierarchical_id TOK_DOT TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "." + $3->substr(1); else *$1 += "." + *$3; - delete $3; - $$ = $1; + $$ = std::move($1); }; hierarchical_type_id: - TOK_USER_TYPE - | TOK_PKG_USER_TYPE // package qualified type name - | '(' TOK_USER_TYPE ')' { $$ = $2; } // non-standard grammar + TOK_USER_TYPE {$$ = std::move($1); } + | TOK_PKG_USER_TYPE {$$ = std::move($1); } // package qualified type name + | TOK_LPAREN TOK_USER_TYPE TOK_RPAREN { $$ = std::move($2); } // non-standard grammar ; module: attr TOK_MODULE { - enterTypeScope(); + extra->enterTypeScope(); } TOK_ID { - do_not_require_port_stubs = false; - AstNode *mod = new AstNode(AST_MODULE); - ast_stack.back()->children.push_back(mod); - ast_stack.push_back(mod); - current_ast_mod = mod; - port_stubs.clear(); - port_counter = 0; + extra->do_not_require_port_stubs = false; + AstNode* mod = extra->pushChild(std::make_unique(@$, AST_MODULE)); + extra->current_ast_mod = mod; + extra->port_stubs.clear(); + extra->port_counter = 0; mod->str = *$4; append_attr(mod, $1); - } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE opt_label { - if (port_stubs.size() != 0) - frontend_verilog_yyerror("Missing details for module port `%s'.", - port_stubs.begin()->first.c_str()); - SET_AST_NODE_LOC(ast_stack.back(), @2, @$); - ast_stack.pop_back(); - log_assert(ast_stack.size() == 1); - checkLabelsMatch("Module name", $4, $11); - current_ast_mod = NULL; - delete $4; - delete $11; - exitTypeScope(); + } module_para_opt module_args_opt TOK_SEMICOL module_body TOK_ENDMODULE opt_label { + if (extra->port_stubs.size() != 0) + err_at_loc(@7, "Missing details for module port `%s'.", + extra->port_stubs.begin()->first.c_str()); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @$); + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 1); + checkLabelsMatch(@11, "Module name", $4.get(), $11.get()); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; module_para_opt: - '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | %empty; + TOK_HASH TOK_LPAREN module_para_list TOK_RPAREN | %empty; module_para_list: - single_module_para | module_para_list ',' single_module_para; + single_module_para | module_para_list TOK_COMMA single_module_para; single_module_para: %empty | attr TOK_PARAMETER { - if (astbuf1) delete astbuf1; - astbuf1 = new AstNode(AST_PARAMETER); - astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - append_attr(astbuf1, $1); + extra->astbuf1 = std::make_unique(@$, AST_PARAMETER); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@2, 0, true)); + append_attr(extra->astbuf1.get(), $1); } param_type single_param_decl | attr TOK_LOCALPARAM { - if (astbuf1) delete astbuf1; - astbuf1 = new AstNode(AST_LOCALPARAM); - astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - append_attr(astbuf1, $1); + extra->astbuf1 = std::make_unique(@$, AST_LOCALPARAM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@2, 0, true)); + append_attr(extra->astbuf1.get(), $1); } param_type single_param_decl | single_param_decl; module_args_opt: - '(' ')' | %empty | '(' module_args optional_comma ')'; + TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN module_args optional_comma TOK_RPAREN; module_args: - module_arg | module_args ',' module_arg; + module_arg | module_args TOK_COMMA module_arg; optional_comma: - ',' | %empty; + TOK_COMMA | %empty; module_arg_opt_assignment: - '=' expr { - if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { - if (ast_stack.back()->children.back()->is_input) { - AstNode *n = ast_stack.back()->children.back(); - if (n->attributes.count(ID::defaultvalue)) - delete n->attributes.at(ID::defaultvalue); - n->attributes[ID::defaultvalue] = $2; + TOK_EQ expr { + if (extra->ast_stack.back()->children.size() > 0 && extra->ast_stack.back()->children.back()->type == AST_WIRE) { + if (extra->ast_stack.back()->children.back()->is_input) { + auto& n = extra->ast_stack.back()->children.back(); + n->attributes[ID::defaultvalue] = std::move($2); } else { - AstNode *wire = new AstNode(AST_IDENTIFIER); - wire->str = ast_stack.back()->children.back()->str; - if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) - ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); + auto wire = std::make_unique(@$, AST_IDENTIFIER); + wire->str = extra->ast_stack.back()->children.back()->str; + if (extra->ast_stack.back()->children.back()->is_reg || extra->ast_stack.back()->children.back()->is_logic) + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_INITIAL, std::make_unique(@$, AST_BLOCK, std::make_unique(@$, AST_ASSIGN_LE, std::move(wire), std::move($2))))); else - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move($2))); } } else - frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); + err_at_loc(@2, "SystemVerilog interface in module port list cannot have a default value."); } | %empty; module_arg: TOK_ID { - if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { - AstNode *node = ast_stack.back()->children.back()->clone(); + if (extra->ast_stack.back()->children.size() > 0 && extra->ast_stack.back()->children.back()->type == AST_WIRE) { + auto node = extra->ast_stack.back()->children.back()->clone(); node->str = *$1; - node->port_id = ++port_counter; - ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @1); + node->port_id = ++extra->port_counter; + SET_AST_NODE_LOC(node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(node)); } else { - if (port_stubs.count(*$1) != 0) - frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str()); - port_stubs[*$1] = ++port_counter; + if (extra->port_stubs.count(*$1) != 0) + err_at_loc(@1, "Duplicate module port `%s'.", $1->c_str()); + extra->port_stubs[*$1] = ++extra->port_counter; } - delete $1; } module_arg_opt_assignment | TOK_ID { - astbuf1 = new AstNode(AST_INTERFACEPORT); - astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE)); - astbuf1->children[0]->str = *$1; - delete $1; + extra->astbuf1 = std::make_unique(@$, AST_INTERFACEPORT); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_INTERFACEPORTTYPE)); + extra->astbuf1->children[0]->str = *$1; } TOK_ID { /* SV interfaces */ - if (!sv_mode) - frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); - astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type. - astbuf2->str = *$3; - delete $3; - astbuf2->port_id = ++port_counter; - ast_stack.back()->children.push_back(astbuf2); - delete astbuf1; // really only needed if multiple instances of same type. + if (!mode->sv) + err_at_loc(@3, "Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); + extra->astbuf2 = extra->astbuf1->clone(); // really only needed if multiple instances of same type. + extra->astbuf2->str = *$3; + extra->astbuf2->port_id = ++extra->port_counter; + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); } module_arg_opt_assignment | attr wire_type range_or_multirange TOK_ID { - AstNode *node = $2; + auto node = std::move($2); node->str = *$4; - SET_AST_NODE_LOC(node, @4, @4); - node->port_id = ++port_counter; - AstNode *range = checkRange(node, $3); - if (range != NULL) - node->children.push_back(range); + SET_AST_NODE_LOC(node.get(), @4, @4); + node->port_id = ++extra->port_counter; + auto range = checkRange(node.get(), std::move($3)); + if (range != nullptr) + node->children.push_back(std::move(range)); if (!node->is_input && !node->is_output) - frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); - if (node->is_reg && node->is_input && !node->is_output && !sv_mode) - frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str()); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); - delete $4; + err_at_loc(@4, "Module port `%s' is neither input nor output.", $4->c_str()); + if (node->is_reg && node->is_input && !node->is_output && !mode->sv) + err_at_loc(@4, "Input port `%s' is declared as register.", $4->c_str()); + append_attr(node.get(), $1); + extra->ast_stack.back()->children.push_back(std::move(node)); } module_arg_opt_assignment | - '.' '.' '.' { - do_not_require_port_stubs = true; + TOK_DOT TOK_DOT TOK_DOT { + extra->do_not_require_port_stubs = true; }; package: attr TOK_PACKAGE { - enterTypeScope(); + extra->enterTypeScope(); } TOK_ID { - AstNode *mod = new AstNode(AST_PACKAGE); - ast_stack.back()->children.push_back(mod); - ast_stack.push_back(mod); - current_ast_mod = mod; + AstNode* mod = extra->pushChild(std::make_unique(@$, AST_PACKAGE)); + extra->current_ast_mod = mod; mod->str = *$4; append_attr(mod, $1); - } ';' package_body TOK_ENDPACKAGE opt_label { - ast_stack.pop_back(); - checkLabelsMatch("Package name", $4, $9); - current_ast_mod = NULL; - delete $4; - delete $9; - exitTypeScope(); + } TOK_SEMICOL package_body TOK_ENDPACKAGE opt_label { + extra->ast_stack.pop_back(); + checkLabelsMatch(@9, "Package name", $4.get(), $9.get()); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; package_body: @@ -730,27 +826,32 @@ package_body: package_body_stmt: typedef_decl | localparam_decl | param_decl | task_func_decl; +import_stmt: + TOK_IMPORT hierarchical_id TOK_PACKAGESEP TOK_ASTER TOK_SEMICOL { + // Create an import node to track package imports + auto import_node = std::make_unique(@$, AST_IMPORT); + import_node->str = *$2; + extra->ast_stack.back()->children.push_back(std::move(import_node)); + }; + interface: TOK_INTERFACE { - enterTypeScope(); + extra->enterTypeScope(); } TOK_ID { - do_not_require_port_stubs = false; - AstNode *intf = new AstNode(AST_INTERFACE); - ast_stack.back()->children.push_back(intf); - ast_stack.push_back(intf); - current_ast_mod = intf; - port_stubs.clear(); - port_counter = 0; + extra->do_not_require_port_stubs = false; + AstNode* intf = extra->pushChild(std::make_unique(@$, AST_INTERFACE)); + extra->current_ast_mod = intf; + extra->port_stubs.clear(); + extra->port_counter = 0; intf->str = *$3; - delete $3; - } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { - if (port_stubs.size() != 0) - frontend_verilog_yyerror("Missing details for module port `%s'.", - port_stubs.begin()->first.c_str()); - ast_stack.pop_back(); - log_assert(ast_stack.size() == 1); - current_ast_mod = NULL; - exitTypeScope(); + } module_para_opt module_args_opt TOK_SEMICOL interface_body TOK_ENDINTERFACE { + if (extra->port_stubs.size() != 0) + err_at_loc(@6, "Missing details for module port `%s'.", + extra->port_stubs.begin()->first.c_str()); + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 1); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; interface_body: @@ -762,27 +863,24 @@ interface_body_stmt: bind_directive: TOK_BIND { - AstNode *bnode = new AstNode(AST_BIND); - ast_stack.back()->children.push_back(bnode); - ast_stack.push_back(bnode); + (void)extra->pushChild(std::make_unique(@$, AST_BIND)); } bind_target { // bind_target should have added at least one child - log_assert(ast_stack.back()->children.size() >= 1); + log_assert(extra->ast_stack.back()->children.size() >= 1); } TOK_ID { - // The single_cell parser in cell_list_no_array uses astbuf1 as + // The single_cell parser in cell_list_no_array uses extra->astbuf1 as // a sort of template for constructing cells. - astbuf1 = new AstNode(AST_CELL); - astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); - astbuf1->children[0]->str = *$5; - delete $5; + extra->astbuf1 = std::make_unique(@$, AST_CELL); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_CELLTYPE)); + extra->astbuf1->children[0]->str = *$5; } - cell_parameter_list_opt cell_list_no_array ';' { + cell_parameter_list_opt cell_list_no_array TOK_SEMICOL { // cell_list should have added at least one more child - log_assert(ast_stack.back()->children.size() >= 2); - delete astbuf1; - ast_stack.pop_back(); + log_assert(extra->ast_stack.back()->children.size() >= 2); + (void)extra->astbuf1.reset(); + extra->ast_stack.pop_back(); }; // bind_target matches the target of the bind (everything before @@ -791,7 +889,7 @@ bind_directive: // We can't use the BNF from the spec directly because it's ambiguous: // something like "bind foo bar_i (.*)" can either be interpreted with "foo" as // a module or interface identifier (matching bind_target_scope in the spec) or -// by considering foo as a degenerate hierarchical identifier with no '.' +// by considering foo as a degenerate hierarchical identifier with no TOK_DOT // characters, followed by no bit select (which matches bind_target_instance in // the spec). // @@ -803,82 +901,81 @@ bind_target: // An optional list of target instances for a bind statement, introduced by a // colon. opt_bind_target_instance_list: - ':' bind_target_instance_list | + TOK_COL bind_target_instance_list | %empty; bind_target_instance_list: bind_target_instance | - bind_target_instance_list ',' bind_target_instance; + bind_target_instance_list TOK_COMMA bind_target_instance; -// A single target instance for a bind statement. The top of ast_stack will be +// A single target instance for a bind statement. The top of extra->ast_stack will be // the bind node where we should add it. bind_target_instance: hierarchical_id { - auto *node = new AstNode(AST_IDENTIFIER); + auto node = std::make_unique(@$, AST_IDENTIFIER); node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); + extra->ast_stack.back()->children.push_back(std::move(node)); }; mintypmax_expr: - expr { delete $1; } | - expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; + expr { } | + expr TOK_COL expr TOK_COL expr { }; non_opt_delay: - '#' TOK_ID { delete $2; } | - '#' TOK_CONSTVAL { delete $2; } | - '#' TOK_REALVAL { delete $2; } | + TOK_HASH TOK_ID { } | + TOK_HASH TOK_CONSTVAL { } | + TOK_HASH TOK_REALVAL { } | // our `expr` doesn't have time_scale, so we need the parenthesized variant - '#' TOK_TIME_SCALE | - '#' '(' TOK_TIME_SCALE ')' | - '#' '(' mintypmax_expr ')' | - '#' '(' mintypmax_expr ',' mintypmax_expr ')' | - '#' '(' mintypmax_expr ',' mintypmax_expr ',' mintypmax_expr ')'; + TOK_HASH TOK_TIME_SCALE | + TOK_HASH TOK_LPAREN TOK_TIME_SCALE TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_COMMA mintypmax_expr TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_COMMA mintypmax_expr TOK_COMMA mintypmax_expr TOK_RPAREN; delay: non_opt_delay | %empty; io_wire_type: - { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + { extra->astbuf3 = std::make_unique(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; } wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness - { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + { $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); }; non_io_wire_type: - { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + { extra->astbuf3 = std::make_unique(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; } wire_type_const_rand wire_type_token wire_type_signedness - { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + { $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); }; wire_type: - io_wire_type | - non_io_wire_type; + io_wire_type { $$ = std::move($1); } | + non_io_wire_type { $$ = std::move($1); }; wire_type_token_io: TOK_INPUT { - astbuf3->is_input = true; + extra->astbuf3->is_input = true; } | TOK_OUTPUT { - astbuf3->is_output = true; + extra->astbuf3->is_output = true; } | TOK_INOUT { - astbuf3->is_input = true; - astbuf3->is_output = true; + extra->astbuf3->is_input = true; + extra->astbuf3->is_output = true; }; wire_type_signedness: - TOK_SIGNED { astbuf3->is_signed = true; } | - TOK_UNSIGNED { astbuf3->is_signed = false; } | + TOK_SIGNED { extra->astbuf3->is_signed = true; } | + TOK_UNSIGNED { extra->astbuf3->is_signed = false; } | %empty; wire_type_const_rand: TOK_RAND TOK_CONST { - current_wire_rand = true; - current_wire_const = true; + extra->current_wire_rand = true; + extra->current_wire_const = true; } | TOK_CONST { - current_wire_const = true; + extra->current_wire_const = true; } | TOK_RAND { - current_wire_rand = true; + extra->current_wire_rand = true; } | %empty; @@ -893,35 +990,35 @@ wire_type_token: } | // regs TOK_REG { - astbuf3->is_reg = true; + extra->astbuf3->is_reg = true; } | TOK_VAR TOK_REG { - astbuf3->is_reg = true; + extra->astbuf3->is_reg = true; } | // logics TOK_VAR { - astbuf3->is_logic = true; + extra->astbuf3->is_logic = true; } | TOK_VAR logic_type { - astbuf3->is_logic = true; + extra->astbuf3->is_logic = true; } | logic_type { - astbuf3->is_logic = true; + extra->astbuf3->is_logic = true; } | TOK_GENVAR { - astbuf3->type = AST_GENVAR; - astbuf3->is_reg = true; - astbuf3->is_signed = true; - astbuf3->range_left = 31; - astbuf3->range_right = 0; + extra->astbuf3->type = AST_GENVAR; + extra->astbuf3->is_reg = true; + extra->astbuf3->is_signed = true; + extra->astbuf3->range_left = 31; + extra->astbuf3->range_right = 0; }; net_type: TOK_WOR { - astbuf3->is_wor = true; + extra->astbuf3->is_wor = true; } | TOK_WAND { - astbuf3->is_wand = true; + extra->astbuf3->is_wand = true; } | TOK_WIRE; @@ -929,12 +1026,12 @@ logic_type: TOK_LOGIC { } | integer_atom_type { - astbuf3->range_left = $1 - 1; - astbuf3->range_right = 0; - astbuf3->is_signed = true; + extra->astbuf3->range_left = $1 - 1; + extra->astbuf3->range_right = 0; + extra->astbuf3->is_signed = true; } | hierarchical_type_id { - addWiretypeNode($1, astbuf3); + extra->addWiretypeNode($1.get(), extra->astbuf3.get()); }; integer_atom_type: @@ -945,59 +1042,59 @@ integer_atom_type: TOK_BYTE { $$ = 8; } ; integer_vector_type: - TOK_LOGIC { $$ = TOK_LOGIC; } | - TOK_REG { $$ = TOK_REG; } ; + TOK_LOGIC { $$ = token::TOK_LOGIC; } | + TOK_REG { $$ = token::TOK_REG; } ; non_opt_range: - '[' expr ':' expr ']' { - $$ = new AstNode(AST_RANGE); - $$->children.push_back($2); - $$->children.push_back($4); + TOK_LBRA expr TOK_COL expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + $$->children.push_back(std::move($2)); + $$->children.push_back(std::move($4)); } | - '[' expr TOK_POS_INDEXED expr ']' { - $$ = new AstNode(AST_RANGE); - AstNode *expr = new AstNode(AST_SELFSZ, $2); - $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), $4), AstNode::mkconst_int(1, true))); - $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); + TOK_LBRA expr TOK_POS_INDEXED expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + auto expr = std::make_unique(@$, AST_SELFSZ, std::move($2)); + $$->children.push_back(std::make_unique(@$, AST_SUB, std::make_unique(@$, AST_ADD, expr->clone(), std::move($4)), AstNode::mkconst_int(@$, 1, true))); + $$->children.push_back(std::make_unique(@$, AST_ADD, std::move(expr), AstNode::mkconst_int(@$, 0, true))); } | - '[' expr TOK_NEG_INDEXED expr ']' { - $$ = new AstNode(AST_RANGE); - AstNode *expr = new AstNode(AST_SELFSZ, $2); - $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); - $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), AstNode::mkconst_int(1, true)), $4)); + TOK_LBRA expr TOK_NEG_INDEXED expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + auto expr = std::make_unique(@$, AST_SELFSZ, std::move($2)); + $$->children.push_back(std::make_unique(@$, AST_ADD, expr->clone(), AstNode::mkconst_int(@$, 0, true))); + $$->children.push_back(std::make_unique(@$, AST_SUB, std::make_unique(@$, AST_ADD, std::move(expr), AstNode::mkconst_int(@$, 1, true)), std::move($4))); } | - '[' expr ']' { - $$ = new AstNode(AST_RANGE); - $$->children.push_back($2); + TOK_LBRA expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + $$->children.push_back(std::move($2)); }; non_opt_multirange: non_opt_range non_opt_range { - $$ = new AstNode(AST_MULTIRANGE, $1, $2); + $$ = std::make_unique(@$, AST_MULTIRANGE, std::move($1), std::move($2)); } | non_opt_multirange non_opt_range { - $$ = $1; - $$->children.push_back($2); + $$ = std::move($1); + $$->children.push_back(std::move($2)); }; range: non_opt_range { - $$ = $1; + $$ = std::move($1); } | %empty { - $$ = NULL; + $$ = nullptr; }; range_or_multirange: - range { $$ = $1; } | - non_opt_multirange { $$ = $1; }; + range { $$ = std::move($1); } | + non_opt_multirange { $$ = std::move($1); }; module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | module_body gen_block | - module_body ';' | + module_body TOK_SEMICOL | %empty; module_body_stmt: @@ -1006,61 +1103,43 @@ module_body_stmt: always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: - TOK_CHECKER TOK_ID ';' { - AstNode *node = new AstNode(AST_GENBLOCK); + TOK_CHECKER TOK_ID TOK_SEMICOL { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_GENBLOCK)); node->str = *$2; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); } module_body TOK_ENDCHECKER { - delete $2; - ast_stack.pop_back(); + extra->ast_stack.pop_back(); }; task_func_decl: attr TOK_DPI_FUNCTION TOK_ID TOK_ID { - current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4)); - current_function_or_task->str = *$4; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - delete $3; - delete $4; - } opt_dpi_function_args ';' { - current_function_or_task = NULL; + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@3, *$3), AstNode::mkconst_str(@4, *$4))); + extra->current_function_or_task->str = *$4; + append_attr(extra->current_function_or_task, $1); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | - attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID { - current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3)); - current_function_or_task->str = *$6; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - delete $3; - delete $5; - delete $6; - } opt_dpi_function_args ';' { - current_function_or_task = NULL; + attr TOK_DPI_FUNCTION TOK_ID TOK_EQ TOK_ID TOK_ID { + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@5, *$5), AstNode::mkconst_str(@3, *$3))); + extra->current_function_or_task->str = *$6; + append_attr(extra->current_function_or_task, $1); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | - attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID { - current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5))); - current_function_or_task->str = *$8; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - delete $3; - delete $5; - delete $7; - delete $8; - } opt_dpi_function_args ';' { - current_function_or_task = NULL; + attr TOK_DPI_FUNCTION TOK_ID TOK_COL TOK_ID TOK_EQ TOK_ID TOK_ID { + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@7, *$7), AstNode::mkconst_str(location_range(@3, @5), *$3 + ":" + RTLIL::unescape_id(*$5)))); + extra->current_function_or_task->str = *$8; + append_attr(extra->current_function_or_task, $1); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | attr TOK_TASK opt_automatic TOK_ID { - current_function_or_task = new AstNode(AST_TASK); - current_function_or_task->str = *$4; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - ast_stack.push_back(current_function_or_task); - current_function_or_task_port_id = 1; - delete $4; - } task_func_args_opt ';' task_func_body TOK_ENDTASK { - current_function_or_task = NULL; - ast_stack.pop_back(); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_TASK)); + extra->current_function_or_task->str = *$4; + append_attr(extra->current_function_or_task, $1); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDTASK { + extra->current_function_or_task = nullptr; + extra->ast_stack.pop_back(); } | attr TOK_FUNCTION opt_automatic TOK_VOID TOK_ID { // The difference between void functions and tasks is that @@ -1068,56 +1147,49 @@ task_func_decl: // inlined, but ignores signals read only in tasks. This only matters // for event based simulation, and for synthesis we can treat a void // function like a task. - current_function_or_task = new AstNode(AST_TASK); - current_function_or_task->str = *$5; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - ast_stack.push_back(current_function_or_task); - current_function_or_task_port_id = 1; - delete $5; - } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { - current_function_or_task = NULL; - ast_stack.pop_back(); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_TASK)); + extra->current_function_or_task->str = *$5; + append_attr(extra->current_function_or_task, $1); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDFUNCTION { + extra->current_function_or_task = nullptr; + extra->ast_stack.pop_back(); } | attr TOK_FUNCTION opt_automatic func_return_type TOK_ID { - current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$5; - append_attr(current_function_or_task, $1); - ast_stack.back()->children.push_back(current_function_or_task); - ast_stack.push_back(current_function_or_task); - AstNode *outreg = new AstNode(AST_WIRE); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_FUNCTION)); + extra->current_function_or_task->str = *$5; + append_attr(extra->current_function_or_task, $1); + auto outreg = std::make_unique(@$, AST_WIRE); outreg->str = *$5; outreg->is_signed = false; outreg->is_reg = true; - if ($4 != NULL) { - outreg->children.push_back($4); + if ($4 != nullptr) { outreg->is_signed = $4->is_signed; $4->is_signed = false; outreg->is_custom_type = $4->type == AST_WIRETYPE; + outreg->children.push_back(std::move($4)); } - current_function_or_task->children.push_back(outreg); - current_function_or_task_port_id = 1; - delete $5; - } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { - current_function_or_task = NULL; - ast_stack.pop_back(); + extra->current_function_or_task->children.push_back(std::move(outreg)); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDFUNCTION { + extra->current_function_or_task = nullptr; + extra->ast_stack.pop_back(); }; func_return_type: hierarchical_type_id { - $$ = new AstNode(AST_WIRETYPE); + $$ = std::make_unique(@$, AST_WIRETYPE); $$->str = *$1; - delete $1; } | opt_type_vec opt_signedness_default_unsigned { - $$ = makeRange(0, 0, $2); + $$ = makeRange(@$, 0, 0, $2); } | opt_type_vec opt_signedness_default_unsigned non_opt_range { - $$ = $3; + $$ = std::move($3); $$->is_signed = $2; } | integer_atom_type opt_signedness_default_signed { - $$ = makeRange($1 - 1, 0, $2); + $$ = makeRange(@$, $1 - 1, 0, $2); }; opt_type_vec: @@ -1139,22 +1211,19 @@ opt_signedness_default_unsigned: dpi_function_arg: TOK_ID TOK_ID { - current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); - delete $1; - delete $2; + extra->current_function_or_task->children.push_back(AstNode::mkconst_str(@1, *$1)); } | TOK_ID { - current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); - delete $1; + extra->current_function_or_task->children.push_back(AstNode::mkconst_str(@1, *$1)); }; opt_dpi_function_args: - '(' dpi_function_args ')' | + TOK_LPAREN dpi_function_args TOK_RPAREN | %empty; dpi_function_args: - dpi_function_args ',' dpi_function_arg | - dpi_function_args ',' | + dpi_function_args TOK_COMMA dpi_function_arg | + dpi_function_args TOK_COMMA | dpi_function_arg | %empty; @@ -1163,52 +1232,52 @@ opt_automatic: %empty; task_func_args_opt: - '(' ')' | %empty | '(' { - albuf = nullptr; - astbuf1 = nullptr; - astbuf2 = nullptr; + TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN { + extra->albuf = nullptr; + extra->astbuf1 = nullptr; + extra->astbuf2 = nullptr; } task_func_args optional_comma { - delete astbuf1; - if (astbuf2 != NULL) - delete astbuf2; - free_attr(albuf); - } ')'; + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + free_attr(extra->albuf); + } TOK_RPAREN; task_func_args: - task_func_port | task_func_args ',' task_func_port; + task_func_port | task_func_args TOK_COMMA task_func_port; task_func_port: attr wire_type range_or_multirange { bool prev_was_input = true; bool prev_was_output = false; - if (albuf) { - prev_was_input = astbuf1->is_input; - prev_was_output = astbuf1->is_output; - delete astbuf1; - if (astbuf2 != NULL) - delete astbuf2; - free_attr(albuf); + if (extra->albuf) { + prev_was_input = extra->astbuf1->is_input; + prev_was_output = extra->astbuf1->is_output; + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + free_attr(extra->albuf); } - albuf = $1; - astbuf1 = $2; - astbuf2 = checkRange(astbuf1, $3); - if (!astbuf1->is_input && !astbuf1->is_output) { - if (!sv_mode) - frontend_verilog_yyerror("task/function argument direction missing"); - astbuf1->is_input = prev_was_input; - astbuf1->is_output = prev_was_output; + extra->albuf = $1; + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); + if (!extra->astbuf1->is_input && !extra->astbuf1->is_output) { + if (!mode->sv) + err_at_loc(@2, "task/function argument direction missing"); + extra->astbuf1->is_input = prev_was_input; + extra->astbuf1->is_output = prev_was_output; } } wire_name | { - if (!astbuf1) { - if (!sv_mode) - frontend_verilog_yyerror("task/function argument direction missing"); - albuf = new dict; - astbuf1 = new AstNode(AST_WIRE); - current_wire_rand = false; - current_wire_const = false; - astbuf1->is_input = true; - astbuf2 = NULL; + if (!extra->astbuf1) { + if (!mode->sv) + err_at_loc(@$, "task/function argument direction missing"); + extra->albuf = new dict>; + extra->astbuf1 = std::make_unique(@$, AST_WIRE); + extra->current_wire_rand = false; + extra->current_wire_const = false; + extra->astbuf1->is_input = true; + extra->astbuf2 = nullptr; } } wire_name; @@ -1226,23 +1295,24 @@ specify_item_list: %empty; specify_item: - specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' { - AstNode *en_expr = $1; + specify_if TOK_LPAREN specify_edge expr TOK_SPECIFY_OPER specify_target TOK_RPAREN TOK_EQ specify_rise_fall TOK_SEMICOL { + auto en_expr = std::move($1); char specify_edge = $3; - AstNode *src_expr = $4; - string *oper = $5; - specify_target *target = $6; - specify_rise_fall *timing = $9; + auto src_expr = std::move($4); + string *oper = $5.get(); + specify_target_ptr_t target = std::move($6); + specify_rise_fall_ptr_t timing = std::move($9); if (specify_edge != 0 && target->dat == nullptr) - frontend_verilog_yyerror("Found specify edge but no data spec.\n"); + err_at_loc(@3, "Found specify edge but no data spec."); - AstNode *cell = new AstNode(AST_CELL); - ast_stack.back()->children.push_back(cell); + auto cell_owned = std::make_unique(@$, AST_CELL); + auto cell = cell_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(cell_owned)); cell->str = stringf("$specify$%d", autoidx++); - cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.push_back(std::make_unique(@$, AST_CELLTYPE)); cell->children.back()->str = target->dat ? "$specify3" : "$specify2"; - SET_AST_NODE_LOC(cell, en_expr ? @1 : @2, @10); + SET_AST_NODE_LOC(cell, en_expr.get() ? @1 : @2, @10); char oper_polarity = 0; char oper_type = oper->at(0); @@ -1252,148 +1322,141 @@ specify_item: oper_type = oper->at(1); } - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1))); + cell->children.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, oper_type == '*', false, 1))); cell->children.back()->str = "\\FULL"; - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity != 0, false, 1))); + cell->children.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, oper_polarity != 0, false, 1))); cell->children.back()->str = "\\SRC_DST_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity == '+', false, 1))); + cell->children.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, oper_polarity == '+', false, 1))); cell->children.back()->str = "\\SRC_DST_POL"; - cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_min)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->rise.t_min))); cell->children.back()->str = "\\T_RISE_MIN"; - cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_avg)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->rise.t_avg))); cell->children.back()->str = "\\T_RISE_TYP"; - cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_max)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->rise.t_max))); cell->children.back()->str = "\\T_RISE_MAX"; - cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_min)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->fall.t_min))); cell->children.back()->str = "\\T_FALL_MIN"; - cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_avg)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->fall.t_avg))); cell->children.back()->str = "\\T_FALL_TYP"; - cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_max)); + cell->children.push_back(std::make_unique(@9, AST_PARASET, std::move(timing->fall.t_max))); cell->children.back()->str = "\\T_FALL_MAX"; - cell->children.push_back(new AstNode(AST_ARGUMENT, en_expr ? en_expr : AstNode::mkconst_int(1, false, 1))); + cell->children.push_back(std::make_unique(@1, AST_ARGUMENT, en_expr ? std::move(en_expr) : AstNode::mkconst_int(@1, 1, false, 1))); cell->children.back()->str = "\\EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.push_back(std::make_unique(@4, AST_ARGUMENT, std::move(src_expr))); cell->children.back()->str = "\\SRC"; - cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst)); + cell->children.push_back(std::make_unique(@6, AST_ARGUMENT, std::move(target->dst))); cell->children.back()->str = "\\DST"; if (target->dat) { - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge != 0, false, 1))); + cell->children.push_back(std::make_unique(@3, AST_PARASET, AstNode::mkconst_int(@3, specify_edge != 0, false, 1))); cell->children.back()->str = "\\EDGE_EN"; - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge == 'p', false, 1))); + cell->children.push_back(std::make_unique(@3, AST_PARASET, AstNode::mkconst_int(@3, specify_edge == 'p', false, 1))); cell->children.back()->str = "\\EDGE_POL"; - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op != 0, false, 1))); + cell->children.push_back(std::make_unique(@6, AST_PARASET, AstNode::mkconst_int(@6, target->polarity_op != 0, false, 1))); cell->children.back()->str = "\\DAT_DST_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op == '+', false, 1))); + cell->children.push_back(std::make_unique(@6, AST_PARASET, AstNode::mkconst_int(@6, target->polarity_op == '+', false, 1))); cell->children.back()->str = "\\DAT_DST_POL"; - cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat)); + cell->children.push_back(std::make_unique(@6, AST_ARGUMENT, std::move(target->dat))); cell->children.back()->str = "\\DAT"; } - - delete oper; - delete target; - delete timing; } | - TOK_ID '(' specify_edge expr specify_condition ',' specify_edge expr specify_condition ',' specify_triple specify_opt_triple ')' ';' { + TOK_ID TOK_LPAREN specify_edge expr specify_condition TOK_COMMA specify_edge expr specify_condition TOK_COMMA specify_triple specify_opt_triple TOK_RPAREN TOK_SEMICOL { if (*$1 != "$setup" && *$1 != "$hold" && *$1 != "$setuphold" && *$1 != "$removal" && *$1 != "$recovery" && *$1 != "$recrem" && *$1 != "$skew" && *$1 != "$timeskew" && *$1 != "$fullskew" && *$1 != "$nochange") - frontend_verilog_yyerror("Unsupported specify rule type: %s\n", $1->c_str()); + err_at_loc(@1, "Unsupported specify rule type: %s", $1->c_str()); - AstNode *src_pen = AstNode::mkconst_int($3 != 0, false, 1); - AstNode *src_pol = AstNode::mkconst_int($3 == 'p', false, 1); - AstNode *src_expr = $4, *src_en = $5 ? $5 : AstNode::mkconst_int(1, false, 1); + auto src_pen = AstNode::mkconst_int(@3, $3 != 0, false, 1); + auto src_pol = AstNode::mkconst_int(@3, $3 == 'p', false, 1); + auto src_expr = std::move($4), src_en = $5 ? std::move($5) : AstNode::mkconst_int(@5, 1, false, 1); - AstNode *dst_pen = AstNode::mkconst_int($7 != 0, false, 1); - AstNode *dst_pol = AstNode::mkconst_int($7 == 'p', false, 1); - AstNode *dst_expr = $8, *dst_en = $9 ? $9 : AstNode::mkconst_int(1, false, 1); + auto dst_pen = AstNode::mkconst_int(@7, $7 != 0, false, 1); + auto dst_pol = AstNode::mkconst_int(@7, $7 == 'p', false, 1); + auto dst_expr = std::move($8), dst_en = $9 ? std::move($9) : AstNode::mkconst_int(@5, 1, false, 1); - specify_triple *limit = $11; - specify_triple *limit2 = $12; + specify_triple_ptr_t limit = std::move($11); + specify_triple_ptr_t limit2 = std::move($12); - AstNode *cell = new AstNode(AST_CELL); - ast_stack.back()->children.push_back(cell); + auto cell_owned = std::make_unique(@$, AST_CELL); + auto cell = cell_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(cell_owned)); cell->str = stringf("$specify$%d", autoidx++); - cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.push_back(std::make_unique(@$, AST_CELLTYPE)); cell->children.back()->str = "$specrule"; SET_AST_NODE_LOC(cell, @1, @14); - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1))); + cell->children.push_back(std::make_unique(@1, AST_PARASET, AstNode::mkconst_str(@1, *$1))); cell->children.back()->str = "\\TYPE"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_min)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(limit->t_min))); cell->children.back()->str = "\\T_LIMIT_MIN"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_avg)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(limit->t_avg))); cell->children.back()->str = "\\T_LIMIT_TYP"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_max)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(limit->t_max))); cell->children.back()->str = "\\T_LIMIT_MAX"; - cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_min : AstNode::mkconst_int(0, true))); + cell->children.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_min) : AstNode::mkconst_int(@12, 0, true))); cell->children.back()->str = "\\T_LIMIT2_MIN"; - cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_avg : AstNode::mkconst_int(0, true))); + cell->children.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_avg) : AstNode::mkconst_int(@12, 0, true))); cell->children.back()->str = "\\T_LIMIT2_TYP"; - cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_max : AstNode::mkconst_int(0, true))); + cell->children.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_max) : AstNode::mkconst_int(@12, 0, true))); cell->children.back()->str = "\\T_LIMIT2_MAX"; - cell->children.push_back(new AstNode(AST_PARASET, src_pen)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(src_pen))); cell->children.back()->str = "\\SRC_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, src_pol)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(src_pol))); cell->children.back()->str = "\\SRC_POL"; - cell->children.push_back(new AstNode(AST_PARASET, dst_pen)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(dst_pen))); cell->children.back()->str = "\\DST_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, dst_pol)); + cell->children.push_back(std::make_unique(@7, AST_PARASET, std::move(dst_pol))); cell->children.back()->str = "\\DST_POL"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_en)); + cell->children.push_back(std::make_unique(@5, AST_ARGUMENT, std::move(src_en))); cell->children.back()->str = "\\SRC_EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.push_back(std::make_unique(@4, AST_ARGUMENT, std::move(src_expr))); cell->children.back()->str = "\\SRC"; - cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en)); + cell->children.push_back(std::make_unique(@9, AST_ARGUMENT, std::move(dst_en))); cell->children.back()->str = "\\DST_EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr)); + cell->children.push_back(std::make_unique(@8, AST_ARGUMENT, std::move(dst_expr))); cell->children.back()->str = "\\DST"; - - delete $1; - delete limit; - delete limit2; }; specify_opt_triple: - ',' specify_triple { - $$ = $2; + TOK_COMMA specify_triple { + $$ = std::move($2); } | %empty { $$ = nullptr; }; specify_if: - TOK_IF '(' expr ')' { - $$ = $3; + TOK_IF TOK_LPAREN expr TOK_RPAREN { + $$ = std::move($3); } | %empty { $$ = nullptr; @@ -1401,7 +1464,7 @@ specify_if: specify_condition: TOK_SPECIFY_AND expr { - $$ = $2; + $$ = std::move($2); } | %empty { $$ = nullptr; @@ -1409,28 +1472,28 @@ specify_condition: specify_target: expr { - $$ = new specify_target; + $$ = std::make_unique(); $$->polarity_op = 0; - $$->dst = $1; + $$->dst = std::move($1); $$->dat = nullptr; } | - '(' expr ':' expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_COL expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = 0; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); } | - '(' expr TOK_NEG_INDEXED expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_NEG_INDEXED expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = '-'; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); } | - '(' expr TOK_POS_INDEXED expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_POS_INDEXED expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = '+'; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); }; specify_edge: @@ -1440,72 +1503,48 @@ specify_edge: specify_rise_fall: specify_triple { - $$ = new specify_rise_fall; - $$->rise = *$1; + $$ = std::make_unique(); $$->fall.t_min = $1->t_min->clone(); $$->fall.t_avg = $1->t_avg->clone(); $$->fall.t_max = $1->t_max->clone(); - delete $1; + $$->rise = std::move(*$1); } | - '(' specify_triple ',' specify_triple ')' { - $$ = new specify_rise_fall; - $$->rise = *$2; - $$->fall = *$4; - delete $2; - delete $4; + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); } | - '(' specify_triple ',' specify_triple ',' specify_triple ')' { - $$ = new specify_rise_fall; - $$->rise = *$2; - $$->fall = *$4; - delete $2; - delete $4; - delete $6; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } | - '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { - $$ = new specify_rise_fall; - $$->rise = *$2; - $$->fall = *$4; - delete $2; - delete $4; - delete $6; - delete $8; - delete $10; - delete $12; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } | - '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { - $$ = new specify_rise_fall; - $$->rise = *$2; - $$->fall = *$4; - delete $2; - delete $4; - delete $6; - delete $8; - delete $10; - delete $12; - delete $14; - delete $16; - delete $18; - delete $20; - delete $22; - delete $24; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } specify_triple: expr { - $$ = new specify_triple; - $$->t_min = $1; + $$ = std::make_unique(); $$->t_avg = $1->clone(); $$->t_max = $1->clone(); + $$->t_min = std::move($1); } | - expr ':' expr ':' expr { - $$ = new specify_triple; - $$->t_min = $1; - $$->t_avg = $3; - $$->t_max = $5; + expr TOK_COL expr TOK_COL expr { + $$ = std::make_unique(); + $$->t_min = std::move($1); + $$->t_avg = std::move($3); + $$->t_max = std::move($5); }; /******************** ignored specify parser **************************/ @@ -1527,58 +1566,58 @@ ignored_specify_item: ; specparam_declaration: - TOK_SPECPARAM list_of_specparam_assignments ';' | - TOK_SPECPARAM specparam_range list_of_specparam_assignments ';' ; + TOK_SPECPARAM list_of_specparam_assignments TOK_SEMICOL | + TOK_SPECPARAM specparam_range list_of_specparam_assignments TOK_SEMICOL ; // IEEE 1364-2005 calls this sinmply 'range' but the current 'range' rule allows empty match // and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005 // exxxxtending this for SV specparam would change this anyhow specparam_range: - '[' ignspec_constant_expression ':' ignspec_constant_expression ']' ; + TOK_LBRA ignspec_constant_expression TOK_COL ignspec_constant_expression TOK_RBRA ; list_of_specparam_assignments: - specparam_assignment | list_of_specparam_assignments ',' specparam_assignment; + specparam_assignment | list_of_specparam_assignments TOK_COMMA specparam_assignment; specparam_assignment: - ignspec_id '=' ignspec_expr ; + ignspec_id TOK_EQ ignspec_expr ; ignspec_opt_cond: - TOK_IF '(' ignspec_expr ')' | %empty; + TOK_IF TOK_LPAREN ignspec_expr TOK_RPAREN | %empty; path_declaration : - simple_path_declaration ';' + simple_path_declaration TOK_SEMICOL // | edge_sensitive_path_declaration // | state_dependent_path_declaration ; simple_path_declaration : - ignspec_opt_cond parallel_path_description '=' path_delay_value | - ignspec_opt_cond full_path_description '=' path_delay_value + ignspec_opt_cond parallel_path_description TOK_EQ path_delay_value | + ignspec_opt_cond full_path_description TOK_EQ path_delay_value ; path_delay_value : - '(' ignspec_expr list_of_path_delay_extra_expressions ')' + TOK_LPAREN ignspec_expr list_of_path_delay_extra_expressions TOK_RPAREN | ignspec_expr | ignspec_expr list_of_path_delay_extra_expressions ; list_of_path_delay_extra_expressions : - ',' ignspec_expr - | ',' ignspec_expr list_of_path_delay_extra_expressions + TOK_COMMA ignspec_expr + | TOK_COMMA ignspec_expr list_of_path_delay_extra_expressions ; specify_edge_identifier : TOK_POSEDGE | TOK_NEGEDGE ; parallel_path_description : - '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' | - '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor opt_polarity_operator ':' ignspec_expr ')' ')' | - '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr ')' ')' ; + TOK_LPAREN specify_input_terminal_descriptor opt_polarity_operator TOK_EQ TOK_GT specify_output_terminal_descriptor TOK_RPAREN | + TOK_LPAREN specify_edge_identifier specify_input_terminal_descriptor TOK_EQ TOK_GT TOK_LPAREN specify_output_terminal_descriptor opt_polarity_operator TOK_COL ignspec_expr TOK_RPAREN TOK_RPAREN | + TOK_LPAREN specify_edge_identifier specify_input_terminal_descriptor TOK_EQ TOK_GT TOK_LPAREN specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr TOK_RPAREN TOK_RPAREN ; full_path_description : - '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' | - '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs opt_polarity_operator ':' ignspec_expr ')' ')' | - '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs TOK_POS_INDEXED ignspec_expr ')' ')' ; + TOK_LPAREN list_of_path_inputs TOK_ASTER TOK_GT list_of_path_outputs TOK_RPAREN | + TOK_LPAREN specify_edge_identifier list_of_path_inputs TOK_ASTER TOK_GT TOK_LPAREN list_of_path_outputs opt_polarity_operator TOK_COL ignspec_expr TOK_RPAREN TOK_RPAREN | + TOK_LPAREN specify_edge_identifier list_of_path_inputs TOK_ASTER TOK_GT TOK_LPAREN list_of_path_outputs TOK_POS_INDEXED ignspec_expr TOK_RPAREN TOK_RPAREN ; // This was broken into 2 rules to solve shift/reduce conflicts list_of_path_inputs : @@ -1586,15 +1625,15 @@ list_of_path_inputs : specify_input_terminal_descriptor more_path_inputs opt_polarity_operator ; more_path_inputs : - ',' specify_input_terminal_descriptor | - more_path_inputs ',' specify_input_terminal_descriptor ; + TOK_COMMA specify_input_terminal_descriptor | + more_path_inputs TOK_COMMA specify_input_terminal_descriptor ; list_of_path_outputs : specify_output_terminal_descriptor | - list_of_path_outputs ',' specify_output_terminal_descriptor ; + list_of_path_outputs TOK_COMMA specify_output_terminal_descriptor ; opt_polarity_operator : - '+' | '-' | %empty; + TOK_PLUS | TOK_MINUS | %empty; // Good enough for the time being specify_input_terminal_descriptor : @@ -1605,7 +1644,7 @@ specify_output_terminal_descriptor : ignspec_id ; system_timing_declaration : - ignspec_id '(' system_timing_args ')' ';' ; + ignspec_id TOK_LPAREN system_timing_args TOK_RPAREN TOK_SEMICOL ; system_timing_arg : TOK_POSEDGE ignspec_id | @@ -1615,140 +1654,135 @@ system_timing_arg : system_timing_args : system_timing_arg | system_timing_args TOK_IGNORED_SPECIFY_AND system_timing_arg | - system_timing_args ',' system_timing_arg ; + system_timing_args TOK_COMMA system_timing_arg ; // for the time being this is OK, but we may write our own expr here. // as I'm not sure it is legal to use a full expr here (probably not) // On the other hand, other rules requiring constant expressions also use 'expr' // (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness ignspec_constant_expression: - expr { delete $1; }; + expr { }; ignspec_expr: - expr { delete $1; } | - expr ':' expr ':' expr { - delete $1; - delete $3; - delete $5; + expr { } | + expr TOK_COL expr TOK_COL expr { }; ignspec_id: - TOK_ID { delete $1; } - range_or_multirange { delete $3; }; + TOK_ID { } + range_or_multirange { }; /**********************************************************************/ param_signed: TOK_SIGNED { - astbuf1->is_signed = true; + extra->astbuf1->is_signed = true; } | TOK_UNSIGNED { - astbuf1->is_signed = false; + extra->astbuf1->is_signed = false; } | %empty; param_integer: type_atom { - astbuf1->is_reg = false; + extra->astbuf1->is_reg = false; }; param_real: TOK_REAL { - astbuf1->children.push_back(new AstNode(AST_REALVALUE)); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_REALVALUE)); }; param_range: range { - if ($1 != NULL) { - astbuf1->children.push_back($1); + if ($1 != nullptr) { + extra->astbuf1->children.push_back(std::move($1)); } }; param_integer_type: param_integer param_signed; param_range_type: type_vec param_signed { - addRange(astbuf1, 0, 0); + addRange(extra->astbuf1.get(), 0, 0); } | type_vec param_signed non_opt_range { - astbuf1->children.push_back($3); + extra->astbuf1->children.push_back(std::move($3)); }; param_implicit_type: param_signed param_range; param_type: param_integer_type | param_real | param_range_type | param_implicit_type | hierarchical_type_id { - addWiretypeNode($1, astbuf1); + extra->addWiretypeNode($1.get(), extra->astbuf1.get()); }; param_decl: attr TOK_PARAMETER { - astbuf1 = new AstNode(AST_PARAMETER); - astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - append_attr(astbuf1, $1); - } param_type param_decl_list ';' { - delete astbuf1; + extra->astbuf1 = std::make_unique(@$, AST_PARAMETER); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + append_attr(extra->astbuf1.get(), $1); + } param_type param_decl_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; localparam_decl: attr TOK_LOCALPARAM { - astbuf1 = new AstNode(AST_LOCALPARAM); - astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - append_attr(astbuf1, $1); - } param_type param_decl_list ';' { - delete astbuf1; + extra->astbuf1 = std::make_unique(@$, AST_LOCALPARAM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + append_attr(extra->astbuf1.get(), $1); + } param_type param_decl_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; param_decl_list: - single_param_decl | param_decl_list ',' single_param_decl; + single_param_decl | param_decl_list TOK_COMMA single_param_decl; single_param_decl: - single_param_decl_ident '=' expr { - AstNode *decl = ast_stack.back()->children.back(); + single_param_decl_ident TOK_EQ expr { + AstNode *decl = extra->ast_stack.back()->children.back().get(); log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM); - delete decl->children[0]; - decl->children[0] = $3; + decl->children[0] = std::move($3); } | single_param_decl_ident { - AstNode *decl = ast_stack.back()->children.back(); + AstNode *decl = extra->ast_stack.back()->children.back().get(); if (decl->type != AST_PARAMETER) { log_assert(decl->type == AST_LOCALPARAM); - frontend_verilog_yyerror("localparam initialization is missing!"); + err_at_loc(@1, "localparam initialization is missing!"); } - if (!sv_mode) - frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!"); - delete decl->children[0]; + if (!mode->sv) + err_at_loc(@1, "Parameter defaults can only be omitted in SystemVerilog mode!"); decl->children.erase(decl->children.begin()); }; single_param_decl_ident: TOK_ID { - AstNode *node; - if (astbuf1 == nullptr) { - if (!sv_mode) - frontend_verilog_yyerror("In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); - node = new AstNode(AST_PARAMETER); - node->children.push_back(AstNode::mkconst_int(0, true)); + std::unique_ptr node_owned; + if (extra->astbuf1 == nullptr) { + if (!mode->sv) + err_at_loc(@1, "In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); + node_owned = std::make_unique(@$, AST_PARAMETER); + node_owned->children.push_back(AstNode::mkconst_int(@$, 0, true)); } else { - node = astbuf1->clone(); + node_owned = extra->astbuf1->clone(); } - node->str = *$1; - ast_stack.back()->children.push_back(node); - delete $1; + node_owned->str = *$1; + auto node = node_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(node_owned)); SET_AST_NODE_LOC(node, @1, @1); }; defparam_decl: - TOK_DEFPARAM defparam_decl_list ';'; + TOK_DEFPARAM defparam_decl_list TOK_SEMICOL; defparam_decl_list: - single_defparam_decl | defparam_decl_list ',' single_defparam_decl; + single_defparam_decl | defparam_decl_list TOK_COMMA single_defparam_decl; single_defparam_decl: - range rvalue '=' expr { - AstNode *node = new AstNode(AST_DEFPARAM); - node->children.push_back($2); - node->children.push_back($4); - if ($1 != NULL) - node->children.push_back($1); - ast_stack.back()->children.push_back(node); + range rvalue TOK_EQ expr { + auto node = std::make_unique(@$, AST_DEFPARAM); + node->children.push_back(std::move($2)); + node->children.push_back(std::move($4)); + if ($1 != nullptr) + node->children.push_back(std::move($1)); + extra->ast_stack.back()->children.push_back(std::move(node)); }; ///////// @@ -1758,90 +1792,89 @@ single_defparam_decl: enum_type: TOK_ENUM { static int enum_count; // create parent node for the enum - astbuf2 = new AstNode(AST_ENUM); - ast_stack.back()->children.push_back(astbuf2); - astbuf2->str = std::string("$enum"); - astbuf2->str += std::to_string(enum_count++); + extra->astbuf2 = std::make_unique(@$, AST_ENUM); + extra->astbuf2->str = std::string("$enum"); + extra->astbuf2->str += std::to_string(enum_count++); + log_assert(!extra->cell_hack); + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); // create the template for the names - astbuf1 = new AstNode(AST_ENUM_ITEM); - astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } enum_base_type '{' enum_name_list optional_comma '}' { + extra->astbuf1 = std::make_unique(@$, AST_ENUM_ITEM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + } enum_base_type TOK_LCURL enum_name_list optional_comma TOK_RCURL { // create template for the enum vars - auto tnode = astbuf1->clone(); - delete astbuf1; - astbuf1 = tnode; + log_assert(extra->cell_hack); + auto tnode_owned = extra->astbuf1->clone(); + auto* tnode = tnode_owned.get(); + extra->astbuf1 = std::move(tnode_owned); tnode->type = AST_WIRE; - tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(@$, extra->cell_hack->str); + extra->cell_hack = nullptr; // drop constant but keep any range - delete tnode->children[0]; tnode->children.erase(tnode->children.begin()); - $$ = astbuf1; + $$ = extra->astbuf1->clone(); }; enum_base_type: type_atom type_signing - | type_vec type_signing range { if ($3) astbuf1->children.push_back($3); } - | %empty { astbuf1->is_reg = true; addRange(astbuf1); } + | type_vec type_signing range { if ($3) extra->astbuf1->children.push_back(std::move($3)); } + | %empty { extra->astbuf1->is_reg = true; addRange(extra->astbuf1.get()); } ; type_atom: integer_atom_type { - astbuf1->is_reg = true; - astbuf1->is_signed = true; - addRange(astbuf1, $1 - 1, 0); + extra->astbuf1->is_reg = true; + extra->astbuf1->is_signed = true; + addRange(extra->astbuf1.get(), $1 - 1, 0); }; -type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned - | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned +type_vec: TOK_REG { extra->astbuf1->is_reg = true; } // unsigned + | TOK_LOGIC { extra->astbuf1->is_logic = true; } // unsigned ; type_signing: - TOK_SIGNED { astbuf1->is_signed = true; } - | TOK_UNSIGNED { astbuf1->is_signed = false; } + TOK_SIGNED { extra->astbuf1->is_signed = true; } + | TOK_UNSIGNED { extra->astbuf1->is_signed = false; } | %empty ; enum_name_list: enum_name_decl - | enum_name_list ',' enum_name_decl + | enum_name_list TOK_COMMA enum_name_decl ; enum_name_decl: TOK_ID opt_enum_init { // put in fn - log_assert(astbuf1); - log_assert(astbuf2); - auto node = astbuf1->clone(); + log_assert((bool)extra->astbuf1); + log_assert((bool)extra->cell_hack); + auto node = extra->astbuf1->clone(); node->str = *$1; - delete $1; - SET_AST_NODE_LOC(node, @1, @1); - delete node->children[0]; - node->children[0] = $2 ? $2 : new AstNode(AST_NONE); - astbuf2->children.push_back(node); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children[0] = $2 ? std::move($2) : std::make_unique(@$, AST_NONE); + extra->cell_hack->children.push_back(std::move(node)); } ; opt_enum_init: - '=' basic_expr { $$ = $2; } // TODO: restrict this - | %empty { $$ = NULL; } + TOK_EQ basic_expr { $$ = std::move($2); } // TODO: restrict this + | %empty { $$ = nullptr; } ; enum_var_list: enum_var - | enum_var_list ',' enum_var + | enum_var_list TOK_COMMA enum_var ; enum_var: TOK_ID { - log_assert(astbuf1); - log_assert(astbuf2); - auto node = astbuf1->clone(); - ast_stack.back()->children.push_back(node); + log_assert((bool)extra->astbuf1); + auto node = extra->astbuf1->clone(); node->str = *$1; - delete $1; - SET_AST_NODE_LOC(node, @1, @1); + SET_AST_NODE_LOC(node.get(), @1, @1); node->is_enum = true; + extra->ast_stack.back()->children.push_back(std::move(node)); } ; -enum_decl: enum_type enum_var_list ';' { delete $1; } +enum_decl: enum_type enum_var_list TOK_SEMICOL { } ; ////////////////// @@ -1850,30 +1883,36 @@ enum_decl: enum_type enum_var_list ';' { delete $1; } struct_decl: attr struct_type { - append_attr($2, $1); - } struct_var_list ';' { - delete astbuf2; + append_attr(extra->astbuf2.get(), $1); + } struct_var_list TOK_SEMICOL { + (void)extra->astbuf2.reset(); } ; -struct_type: struct_union { astbuf2 = $1; astbuf2->is_custom_type = true; } struct_body { $$ = astbuf2; } +struct_type: + struct_union { + extra->astbuf2 = std::move($1); + extra->astbuf2->is_custom_type = true; + } struct_body { + $$ = extra->astbuf2->clone(); + } ; struct_union: - TOK_STRUCT { $$ = new AstNode(AST_STRUCT); } - | TOK_UNION { $$ = new AstNode(AST_UNION); } + TOK_STRUCT { $$ = std::make_unique(@$, AST_STRUCT); } + | TOK_UNION { $$ = std::make_unique(@$, AST_UNION); } ; -struct_body: opt_packed '{' struct_member_list '}' +struct_body: opt_packed TOK_LCURL struct_member_list TOK_RCURL ; opt_packed: TOK_PACKED opt_signed_struct | - %empty { frontend_verilog_yyerror("Only PACKED supported at this time"); }; + %empty { err_at_loc(@$, "Only PACKED supported at this time"); }; opt_signed_struct: - TOK_SIGNED { astbuf2->is_signed = true; } - | TOK_UNSIGNED { astbuf2->is_signed = false; } + TOK_SIGNED { extra->astbuf2->is_signed = true; } + | TOK_UNSIGNED { extra->astbuf2->is_signed = false; } | %empty // default is unsigned ; @@ -1881,62 +1920,68 @@ struct_member_list: struct_member | struct_member_list struct_member ; -struct_member: struct_member_type member_name_list ';' { delete astbuf1; } +struct_member: struct_member_type member_name_list TOK_SEMICOL { (void)extra->astbuf1.reset(); } ; member_name_list: member_name - | member_name_list ',' member_name + | member_name_list TOK_COMMA member_name ; member_name: TOK_ID { - astbuf1->str = $1->substr(1); - delete $1; - astbuf3 = astbuf1->clone(); - SET_AST_NODE_LOC(astbuf3, @1, @1); - astbuf2->children.push_back(astbuf3); - } range { if ($3) astbuf3->children.push_back($3); } + extra->astbuf1->str = $1->substr(1); + extra->astbuf3 = extra->astbuf1->clone(); + log_assert(!extra->member_hack); + extra->member_hack = extra->astbuf3.get(); + SET_AST_NODE_LOC(extra->member_hack, @1, @1); + extra->astbuf2->children.push_back(std::move(extra->astbuf3)); + } range { + log_assert((bool)extra->member_hack); + if ($3) extra->member_hack->children.push_back(std::move($3)); + extra->member_hack = nullptr; + } ; -struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token +struct_member_type: { extra->astbuf1 = std::make_unique(@$, AST_STRUCT_ITEM); } member_type_token ; member_type_token: member_type range_or_multirange { - AstNode *range = checkRange(astbuf1, $2); + auto range = checkRange(extra->astbuf1.get(), std::move($2)); if (range) - astbuf1->children.push_back(range); + extra->astbuf1->children.push_back(std::move(range)); } | { - delete astbuf1; + (void)extra->astbuf1.reset(); } struct_union { - // stash state on ast_stack - ast_stack.push_back(astbuf2); - astbuf2 = $2; + // stash state on extra->ast_stack + // sketchy! + extra->ast_stack.push_back(extra->astbuf2.release()); + extra->astbuf2 = std::move($2); } struct_body { - astbuf1 = astbuf2; + extra->astbuf1 = std::move(extra->astbuf2); // recover state - astbuf2 = ast_stack.back(); - ast_stack.pop_back(); + extra->astbuf2.reset(extra->ast_stack.back()); + extra->ast_stack.pop_back(); } ; member_type: type_atom type_signing | type_vec type_signing - | hierarchical_type_id { addWiretypeNode($1, astbuf1); } + | hierarchical_type_id { extra->addWiretypeNode($1.get(), extra->astbuf1.get()); } ; struct_var_list: struct_var - | struct_var_list ',' struct_var + | struct_var_list TOK_COMMA struct_var ; -struct_var: TOK_ID { auto *var_node = astbuf2->clone(); - var_node->str = *$1; - delete $1; - SET_AST_NODE_LOC(var_node, @1, @1); - ast_stack.back()->children.push_back(var_node); - } - ; +struct_var: + TOK_ID { + auto var_node = extra->astbuf2->clone(); + var_node->str = *$1; + SET_AST_NODE_LOC(var_node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(var_node)); + }; ///////// // wire @@ -1944,46 +1989,43 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone(); wire_decl: attr wire_type range_or_multirange { - albuf = $1; - astbuf1 = $2; - astbuf2 = checkRange(astbuf1, $3); + extra->albuf = $1; + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); } delay wire_name_list { - delete astbuf1; - if (astbuf2 != NULL) - delete astbuf2; - free_attr(albuf); - } ';' | + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + free_attr(extra->albuf); + } TOK_SEMICOL | attr TOK_SUPPLY0 TOK_ID { - ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); - ast_stack.back()->children.back()->str = *$3; - append_attr(ast_stack.back()->children.back(), $1); - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))); - ast_stack.back()->children.back()->children[0]->str = *$3; - delete $3; - } opt_supply_wires ';' | + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_WIRE)); + extra->ast_stack.back()->children.back()->str = *$3; + append_attr(extra->ast_stack.back()->children.back().get(), $1); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::make_unique(@$, AST_IDENTIFIER), AstNode::mkconst_int(@$, 0, false, 1))); + extra->ast_stack.back()->children.back()->children[0]->str = *$3; + } opt_supply_wires TOK_SEMICOL | attr TOK_SUPPLY1 TOK_ID { - ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); - ast_stack.back()->children.back()->str = *$3; - append_attr(ast_stack.back()->children.back(), $1); - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1))); - ast_stack.back()->children.back()->children[0]->str = *$3; - delete $3; - } opt_supply_wires ';'; + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_WIRE)); + extra->ast_stack.back()->children.back()->str = *$3; + append_attr(extra->ast_stack.back()->children.back().get(), $1); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::make_unique(@$, AST_IDENTIFIER), AstNode::mkconst_int(@$, 1, false, 1))); + extra->ast_stack.back()->children.back()->children[0]->str = *$3; + } opt_supply_wires TOK_SEMICOL; opt_supply_wires: %empty | - opt_supply_wires ',' TOK_ID { - AstNode *wire_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-2)->clone(); - AstNode *assign_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-1)->clone(); + opt_supply_wires TOK_COMMA TOK_ID { + auto wire_node = extra->ast_stack.back()->children.at(GetSize(extra->ast_stack.back()->children)-2)->clone(); + auto assign_node = extra->ast_stack.back()->children.at(GetSize(extra->ast_stack.back()->children)-1)->clone(); wire_node->str = *$3; assign_node->children[0]->str = *$3; - ast_stack.back()->children.push_back(wire_node); - ast_stack.back()->children.push_back(assign_node); - delete $3; + extra->ast_stack.back()->children.push_back(std::move(wire_node)); + extra->ast_stack.back()->children.push_back(std::move(assign_node)); }; wire_name_list: - wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign; + wire_name_and_opt_assign | wire_name_list TOK_COMMA wire_name_and_opt_assign; wire_name_and_opt_assign: wire_name { @@ -1991,31 +2033,27 @@ wire_name_and_opt_assign: bool attr_anyseq = false; bool attr_allconst = false; bool attr_allseq = false; - if (ast_stack.back()->children.back()->get_bool_attribute(ID::anyconst)) { - delete ast_stack.back()->children.back()->attributes.at(ID::anyconst); - ast_stack.back()->children.back()->attributes.erase(ID::anyconst); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::anyconst)) { + extra->ast_stack.back()->children.back()->attributes.erase(ID::anyconst); attr_anyconst = true; } - if (ast_stack.back()->children.back()->get_bool_attribute(ID::anyseq)) { - delete ast_stack.back()->children.back()->attributes.at(ID::anyseq); - ast_stack.back()->children.back()->attributes.erase(ID::anyseq); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::anyseq)) { + extra->ast_stack.back()->children.back()->attributes.erase(ID::anyseq); attr_anyseq = true; } - if (ast_stack.back()->children.back()->get_bool_attribute(ID::allconst)) { - delete ast_stack.back()->children.back()->attributes.at(ID::allconst); - ast_stack.back()->children.back()->attributes.erase(ID::allconst); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::allconst)) { + extra->ast_stack.back()->children.back()->attributes.erase(ID::allconst); attr_allconst = true; } - if (ast_stack.back()->children.back()->get_bool_attribute(ID::allseq)) { - delete ast_stack.back()->children.back()->attributes.at(ID::allseq); - ast_stack.back()->children.back()->attributes.erase(ID::allseq); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::allseq)) { + extra->ast_stack.back()->children.back()->attributes.erase(ID::allseq); attr_allseq = true; } - if (current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) { - AstNode *wire = new AstNode(AST_IDENTIFIER); - AstNode *fcall = new AstNode(AST_FCALL); - wire->str = ast_stack.back()->children.back()->str; - fcall->str = current_wire_const ? "\\$anyconst" : "\\$anyseq"; + if (extra->current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) { + auto wire = std::make_unique(@$, AST_IDENTIFIER); + auto fcall = std::make_unique(@$, AST_FCALL); + wire->str = extra->ast_stack.back()->children.back()->str; + fcall->str = extra->current_wire_const ? "\\$anyconst" : "\\$anyseq"; if (attr_anyconst) fcall->str = "\\$anyconst"; if (attr_anyseq) @@ -2024,126 +2062,123 @@ wire_name_and_opt_assign: fcall->str = "\\$allconst"; if (attr_allseq) fcall->str = "\\$allseq"; - fcall->attributes[ID::reg] = AstNode::mkconst_str(RTLIL::unescape_id(wire->str)); - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, fcall)); + fcall->attributes[ID::reg] = AstNode::mkconst_str(@$, RTLIL::unescape_id(wire->str)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move(fcall))); } } | - wire_name '=' expr { - AstNode *wire = new AstNode(AST_IDENTIFIER); - wire->str = ast_stack.back()->children.back()->str; - if (astbuf1->is_input) { - if (astbuf1->attributes.count(ID::defaultvalue)) - delete astbuf1->attributes.at(ID::defaultvalue); - astbuf1->attributes[ID::defaultvalue] = $3; + wire_name TOK_EQ expr { + auto wire = std::make_unique(@$, AST_IDENTIFIER); + wire->str = extra->ast_stack.back()->children.back()->str; + if (extra->astbuf1->is_input) { + extra->astbuf1->attributes[ID::defaultvalue] = std::move($3); } - else if (astbuf1->is_reg || astbuf1->is_logic){ - AstNode *assign = new AstNode(AST_ASSIGN_LE, wire, $3); - AstNode *block = new AstNode(AST_BLOCK, assign); - AstNode *init = new AstNode(AST_INITIAL, block); + else if (extra->astbuf1->is_reg || extra->astbuf1->is_logic){ + auto assign = std::make_unique(@$, AST_ASSIGN_LE, std::move(wire), std::move($3)); + SET_AST_NODE_LOC(assign.get(), @1, @3); + auto block = std::make_unique(@$, AST_BLOCK, std::move(assign)); + SET_AST_NODE_LOC(block.get(), @1, @3); + auto init = std::make_unique(@$, AST_INITIAL, std::move(block)); + SET_AST_NODE_LOC(init.get(), @1, @3); - SET_AST_NODE_LOC(assign, @1, @3); - SET_AST_NODE_LOC(block, @1, @3); - SET_AST_NODE_LOC(init, @1, @3); - - ast_stack.back()->children.push_back(init); + extra->ast_stack.back()->children.push_back(std::move(init)); } else { - AstNode *assign = new AstNode(AST_ASSIGN, wire, $3); - SET_AST_NODE_LOC(assign, @1, @3); - ast_stack.back()->children.push_back(assign); + auto assign = std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move($3)); + SET_AST_NODE_LOC(assign.get(), @1, @3); + extra->ast_stack.back()->children.push_back(std::move(assign)); } }; wire_name: TOK_ID range_or_multirange { - if (astbuf1 == nullptr) - frontend_verilog_yyerror("Internal error - should not happen - no AST_WIRE node."); - AstNode *node = astbuf1->clone(); + if (extra->astbuf1 == nullptr) + err_at_loc(@1, "Internal error - should not happen - no AST_WIRE node."); + auto node = extra->astbuf1->clone(); node->str = *$1; - append_attr_clone(node, albuf); - if (astbuf2 != NULL) - node->children.push_back(astbuf2->clone()); - if ($2 != NULL) { + append_attr_clone(node.get(), extra->albuf); + if (extra->astbuf2 != nullptr) + node->children.push_back(extra->astbuf2->clone()); + if ($2 != nullptr) { if (node->is_input || node->is_output) - frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); - if (!astbuf2 && !node->is_custom_type) { - addRange(node, 0, 0, false); + err_at_loc(@2, "input/output/inout ports cannot have unpacked dimensions."); + if (!extra->astbuf2 && !node->is_custom_type) { + addRange(node.get(), 0, 0, false); } - rewriteAsMemoryNode(node, $2); + rewriteAsMemoryNode(node.get(), std::move($2)); } - if (current_function_or_task) { + if (extra->current_function_or_task) { if (node->is_input || node->is_output) - node->port_id = current_function_or_task_port_id++; - } else if (ast_stack.back()->type == AST_GENBLOCK) { + node->port_id = extra->current_function_or_task_port_id++; + } else if (extra->ast_stack.back()->type == AST_GENBLOCK) { if (node->is_input || node->is_output) - frontend_verilog_yyerror("Cannot declare module port `%s' within a generate block.", $1->c_str()); + err_at_loc(@1, "Cannot declare module port `%s' within a generate block.", $1->c_str()); } else { - if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { - port_stubs[*$1] = ++port_counter; + if (extra->do_not_require_port_stubs && (node->is_input || node->is_output) && extra->port_stubs.count(*$1) == 0) { + extra->port_stubs[*$1] = ++extra->port_counter; } - if (port_stubs.count(*$1) != 0) { + if (extra->port_stubs.count(*$1) != 0) { if (!node->is_input && !node->is_output) - frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); - if (node->is_reg && node->is_input && !node->is_output && !sv_mode) - frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str()); - node->port_id = port_stubs[*$1]; - port_stubs.erase(*$1); + err_at_loc(@1, "Module port `%s' is neither input nor output.", $1->c_str()); + if (node->is_reg && node->is_input && !node->is_output && !mode->sv) + err_at_loc(@1, "Input port `%s' is declared as register.", $1->c_str()); + node->port_id = extra->port_stubs[*$1]; + extra->port_stubs.erase(*$1); } else { if (node->is_input || node->is_output) - frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); + err_at_loc(@1, "Module port `%s' is not declared in module header.", $1->c_str()); } } //FIXME: for some reason, TOK_ID has a location which always points to one column *after* the real last column... - SET_AST_NODE_LOC(node, @1, @1); - ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(node)); - delete $1; }; assign_stmt: - TOK_ASSIGN delay assign_expr_list ';'; + TOK_ASSIGN delay assign_expr_list TOK_SEMICOL; assign_expr_list: - assign_expr | assign_expr_list ',' assign_expr; + assign_expr | assign_expr_list TOK_COMMA assign_expr; assign_expr: - lvalue '=' expr { - AstNode *node = new AstNode(AST_ASSIGN, $1, $3); + lvalue TOK_EQ expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN, std::move($1), std::move($3))); SET_AST_NODE_LOC(node, @$, @$); - ast_stack.back()->children.push_back(node); }; -type_name: TOK_ID // first time seen - | TOK_USER_TYPE { if (isInLocalScope($1)) frontend_verilog_yyerror("Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); } +type_name: TOK_ID { $$ = std::move($1); } // first time seen + | TOK_USER_TYPE { if (extra->isInLocalScope($1.get())) err_at_loc(@1, "Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); $$ = std::move($1); } ; typedef_decl: - TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange ';' { - astbuf1 = $2; - astbuf2 = checkRange(astbuf1, $3); - if (astbuf2) - astbuf1->children.push_back(astbuf2); - - if ($5 != NULL) { - if (!astbuf2 && !astbuf1->is_custom_type) { - addRange(astbuf1, 0, 0, false); - } - rewriteAsMemoryNode(astbuf1, $5); + TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange TOK_SEMICOL { + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); + bool has_a_range = (bool)extra->astbuf2; + if (extra->astbuf2) { + extra->astbuf1->children.push_back(std::move(extra->astbuf2)); } - addTypedefNode($4, astbuf1); } - | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); } + + if ($5 != nullptr) { + if (!has_a_range && !extra->astbuf1->is_custom_type) { + addRange(extra->astbuf1.get(), 0, 0, false); + } + rewriteAsMemoryNode(extra->astbuf1.get(), std::move($5)); + } + extra->addTypedefNode($4.get(), std::move(extra->astbuf1)); } + | TOK_TYPEDEF enum_struct_type type_name TOK_SEMICOL { extra->addTypedefNode($3.get(), std::move($2)); } ; typedef_base_type: hierarchical_type_id { - $$ = new AstNode(AST_WIRE); + $$ = std::make_unique(@$, AST_WIRE); $$->is_logic = true; - addWiretypeNode($1, $$); + extra->addWiretypeNode($1.get(), $$.get()); } | integer_vector_type opt_signedness_default_unsigned { - $$ = new AstNode(AST_WIRE); - if ($1 == TOK_REG) { + $$ = std::make_unique(@$, AST_WIRE); + if ($1 == token::TOK_REG) { $$->is_reg = true; } else { $$->is_logic = true; @@ -2151,7 +2186,7 @@ typedef_base_type: $$->is_signed = $2; } | integer_atom_type opt_signedness_default_signed { - $$ = new AstNode(AST_WIRE); + $$ = std::make_unique(@$, AST_WIRE); $$->is_logic = true; $$->is_signed = $2; $$->range_left = $1 - 1; @@ -2159,124 +2194,130 @@ typedef_base_type: }; enum_struct_type: - enum_type - | struct_type + enum_type { $$ = std::move($1); } + | struct_type { $$ = std::move($1); } ; cell_stmt: attr TOK_ID { - astbuf1 = new AstNode(AST_CELL); - append_attr(astbuf1, $1); - astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); - astbuf1->children[0]->str = *$2; - delete $2; - } cell_parameter_list_opt cell_list ';' { - delete astbuf1; + extra->astbuf1 = std::make_unique(@$, AST_CELL); + append_attr(extra->astbuf1.get(), $1); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_CELLTYPE)); + extra->astbuf1->children[0]->str = *$2; + } cell_parameter_list_opt cell_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); } | attr tok_prim_wrapper delay { - astbuf1 = new AstNode(AST_PRIMITIVE); - astbuf1->str = *$2; - append_attr(astbuf1, $1); - delete $2; - } prim_list ';' { - delete astbuf1; + extra->astbuf1 = std::make_unique(@$, AST_PRIMITIVE); + extra->astbuf1->str = *$2; + append_attr(extra->astbuf1.get(), $1); + } prim_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; tok_prim_wrapper: TOK_PRIMITIVE { - $$ = $1; + $$ = std::move($1); } | TOK_OR { - $$ = new std::string("or"); + $$ = std::make_unique("or"); }; cell_list: single_cell | - cell_list ',' single_cell; + cell_list TOK_COMMA single_cell; single_cell: single_cell_no_array | single_cell_arraylist; single_cell_no_array: TOK_ID { - astbuf2 = astbuf1->clone(); - if (astbuf2->type != AST_PRIMITIVE) - astbuf2->str = *$1; - delete $1; - ast_stack.back()->children.push_back(astbuf2); - } '(' cell_port_list ')' { - SET_AST_NODE_LOC(astbuf2, @1, @$); + extra->astbuf2 = extra->astbuf1->clone(); + if (extra->astbuf2->type != AST_PRIMITIVE) + extra->astbuf2->str = *$1; + // TODO optimize again + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); + } TOK_LPAREN cell_port_list TOK_RPAREN { + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; } single_cell_arraylist: TOK_ID non_opt_range { - astbuf2 = astbuf1->clone(); - if (astbuf2->type != AST_PRIMITIVE) - astbuf2->str = *$1; - delete $1; - ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2)); - } '(' cell_port_list ')'{ - SET_AST_NODE_LOC(astbuf2, @1, @$); + extra->astbuf2 = extra->astbuf1->clone(); + if (extra->astbuf2->type != AST_PRIMITIVE) + extra->astbuf2->str = *$1; + // TODO optimize again + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_CELLARRAY, std::move($2), std::move(extra->astbuf2))); + } TOK_LPAREN cell_port_list TOK_RPAREN{ + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; }; cell_list_no_array: single_cell_no_array | - cell_list_no_array ',' single_cell_no_array; + cell_list_no_array TOK_COMMA single_cell_no_array; prim_list: single_prim | - prim_list ',' single_prim; + prim_list TOK_COMMA single_prim; single_prim: single_cell | /* no name */ { - astbuf2 = astbuf1->clone(); - ast_stack.back()->children.push_back(astbuf2); - } '(' cell_port_list ')' { - SET_AST_NODE_LOC(astbuf2, @1, @$); + extra->astbuf2 = extra->astbuf1->clone(); + log_assert(!extra->cell_hack); + extra->cell_hack = extra->astbuf2.get(); + // TODO optimize again + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); + } TOK_LPAREN cell_port_list TOK_RPAREN { + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; } cell_parameter_list_opt: - '#' '(' cell_parameter_list ')' | %empty; + TOK_HASH TOK_LPAREN cell_parameter_list TOK_RPAREN | %empty; cell_parameter_list: - cell_parameter | cell_parameter_list ',' cell_parameter; + cell_parameter | cell_parameter_list TOK_COMMA cell_parameter; cell_parameter: %empty | expr { - AstNode *node = new AstNode(AST_PARASET); - astbuf1->children.push_back(node); - node->children.push_back($1); + auto node = std::make_unique(@$, AST_PARASET); + node->children.push_back(std::move($1)); + extra->astbuf1->children.push_back(std::move(node)); } | - '.' TOK_ID '(' ')' { - // delete unused TOK_ID - delete $2; + TOK_DOT TOK_ID TOK_LPAREN TOK_RPAREN { + // just ignore empty parameters } | - '.' TOK_ID '(' expr ')' { - AstNode *node = new AstNode(AST_PARASET); + TOK_DOT TOK_ID TOK_LPAREN expr TOK_RPAREN { + auto node = std::make_unique(@$, AST_PARASET); node->str = *$2; - astbuf1->children.push_back(node); - node->children.push_back($4); - delete $2; + node->children.push_back(std::move($4)); + extra->astbuf1->children.push_back(std::move(node)); }; cell_port_list: cell_port_list_rules { // remove empty args from end of list - while (!astbuf2->children.empty()) { - AstNode *node = astbuf2->children.back(); + while (!extra->cell_hack->children.empty()) { + auto& node = extra->cell_hack->children.back(); if (node->type != AST_ARGUMENT) break; if (!node->children.empty()) break; if (!node->str.empty()) break; - astbuf2->children.pop_back(); - delete node; + extra->cell_hack->children.pop_back(); } // check port types bool has_positional_args = false; bool has_named_args = false; - for (auto node : astbuf2->children) { + for (auto& node : extra->cell_hack->children) { if (node->type != AST_ARGUMENT) continue; if (node->str.empty()) has_positional_args = true; @@ -2285,52 +2326,49 @@ cell_port_list: } if (has_positional_args && has_named_args) - frontend_verilog_yyerror("Mix of positional and named cell ports."); + err_at_loc(@1, "Mix of positional and named cell ports."); }; cell_port_list_rules: - cell_port | cell_port_list_rules ',' cell_port; + cell_port | cell_port_list_rules TOK_COMMA cell_port; cell_port: attr { - AstNode *node = new AstNode(AST_ARGUMENT); - astbuf2->children.push_back(node); + auto node = std::make_unique(@$, AST_ARGUMENT); + extra->cell_hack->children.push_back(std::move(node)); free_attr($1); } | attr expr { - AstNode *node = new AstNode(AST_ARGUMENT); - astbuf2->children.push_back(node); - node->children.push_back($2); + auto node = std::make_unique(@$, AST_ARGUMENT); + node->children.push_back(std::move($2)); + extra->cell_hack->children.push_back(std::move(node)); free_attr($1); } | - attr '.' TOK_ID '(' expr ')' { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID TOK_LPAREN expr TOK_RPAREN { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - node->children.push_back($5); - delete $3; + node->children.push_back(std::move($5)); + extra->cell_hack->children.push_back(std::move(node)); free_attr($1); } | - attr '.' TOK_ID '(' ')' { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID TOK_LPAREN TOK_RPAREN { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - delete $3; + extra->cell_hack->children.push_back(std::move(node)); free_attr($1); } | - attr '.' TOK_ID { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - node->children.push_back(new AstNode(AST_IDENTIFIER)); + node->children.push_back(std::make_unique(@$, AST_IDENTIFIER)); node->children.back()->str = *$3; - delete $3; + extra->cell_hack->children.push_back(std::move(node)); free_attr($1); } | attr TOK_WILDCARD_CONNECT { - if (!sv_mode) - frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode."); - astbuf2->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(1, false); + if (!mode->sv) + err_at_loc(@2, "Wildcard port connections are only supported in SystemVerilog mode."); + extra->cell_hack->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(@2, 1, false); free_attr($1); }; @@ -2352,100 +2390,88 @@ always_or_always_ff: always_stmt: attr always_or_always_ff { - AstNode *node = new AstNode(AST_ALWAYS); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_ALWAYS)); append_attr(node, $1); if ($2) - node->attributes[ID::always_ff] = AstNode::mkconst_int(1, false); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + node->attributes[ID::always_ff] = AstNode::mkconst_int(@2, 1, false); } always_cond { - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @6, @6); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @6, @6); + extra->ast_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @2, @$); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @$); + extra->ast_stack.pop_back(); SET_RULE_LOC(@$, @2, @$); } | attr always_comb_or_latch { - AstNode *node = new AstNode(AST_ALWAYS); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_ALWAYS)); append_attr(node, $1); if ($2) - node->attributes[ID::always_latch] = AstNode::mkconst_int(1, false); + node->attributes[ID::always_latch] = AstNode::mkconst_int(@2, 1, false); else - node->attributes[ID::always_comb] = AstNode::mkconst_int(1, false); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + node->attributes[ID::always_comb] = AstNode::mkconst_int(@2, 1, false); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - ast_stack.pop_back(); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); } | attr TOK_INITIAL { - AstNode *node = new AstNode(AST_INITIAL); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_INITIAL)); append_attr(node, $1); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - ast_stack.pop_back(); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); }; always_cond: - '@' '(' always_events ')' | - '@' '(' '*' ')' | - '@' ATTR_BEGIN ')' | - '@' '(' ATTR_END | - '@' '*' | + TOK_AT TOK_LPAREN always_events TOK_RPAREN | + TOK_AT TOK_LPAREN TOK_ASTER TOK_RPAREN | + TOK_AT ATTR_BEGIN TOK_RPAREN | + TOK_AT TOK_LPAREN ATTR_END | + TOK_AT TOK_ASTER | %empty; always_events: always_event | always_events TOK_OR always_event | - always_events ',' always_event; + always_events TOK_COMMA always_event; always_event: TOK_POSEDGE expr { - AstNode *node = new AstNode(AST_POSEDGE); - SET_AST_NODE_LOC(node, @1, @1); - ast_stack.back()->children.push_back(node); - node->children.push_back($2); + auto node = std::make_unique(@$, AST_POSEDGE); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children.push_back(std::move($2)); + extra->ast_stack.back()->children.push_back(std::move(node)); } | TOK_NEGEDGE expr { - AstNode *node = new AstNode(AST_NEGEDGE); - SET_AST_NODE_LOC(node, @1, @1); - ast_stack.back()->children.push_back(node); - node->children.push_back($2); + auto node = std::make_unique(@$, AST_NEGEDGE); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children.push_back(std::move($2)); + extra->ast_stack.back()->children.push_back(std::move(node)); } | expr { - AstNode *node = new AstNode(AST_EDGE); - ast_stack.back()->children.push_back(node); - node->children.push_back($1); + auto node = std::make_unique(@$, AST_EDGE); + node->children.push_back(std::move($1)); + extra->ast_stack.back()->children.push_back(std::move(node)); }; opt_label: - ':' TOK_ID { - $$ = $2; + TOK_COL TOK_ID { + $$ = std::move($2); } | %empty { - $$ = NULL; + $$ = nullptr; }; opt_sva_label: - TOK_SVA_LABEL ':' { - $$ = $1; + TOK_SVA_LABEL TOK_COL { + $$ = std::move($1); } | %empty { - $$ = NULL; + $$ = nullptr; }; opt_property: @@ -2461,21 +2487,19 @@ opt_property: modport_stmt: TOK_MODPORT TOK_ID { - AstNode *modport = new AstNode(AST_MODPORT); - ast_stack.back()->children.push_back(modport); - ast_stack.push_back(modport); + AstNode* modport = extra->pushChild(std::make_unique(@$, AST_MODPORT)); modport->str = *$2; - delete $2; + } modport_args_opt { - ast_stack.pop_back(); - log_assert(ast_stack.size() == 2); - } ';' + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 2); + } TOK_SEMICOL modport_args_opt: - '(' ')' | '(' modport_args optional_comma ')'; + TOK_LPAREN TOK_RPAREN | TOK_LPAREN modport_args optional_comma TOK_RPAREN; modport_args: - modport_arg | modport_args ',' modport_arg; + modport_arg | modport_args TOK_COMMA modport_arg; modport_arg: modport_type_token modport_member | @@ -2483,222 +2507,174 @@ modport_arg: modport_member: TOK_ID { - AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); - ast_stack.back()->children.push_back(modport_member); + AstNode* modport_member = extra->saveChild(std::make_unique(@$, AST_MODPORTMEMBER)); modport_member->str = *$1; - modport_member->is_input = current_modport_input; - modport_member->is_output = current_modport_output; - delete $1; + modport_member->is_input = extra->current_modport_input; + modport_member->is_output = extra->current_modport_output; + } modport_type_token: - TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} + TOK_INPUT {extra->current_modport_input = 1; extra->current_modport_output = 0;} | TOK_OUTPUT {extra->current_modport_input = 0; extra->current_modport_output = 1;} assert: - opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' { - if (noassert_mode) { - delete $5; + opt_sva_label TOK_ASSERT opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassert) { + } else { - AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_ASSUME : AST_ASSERT, std::move($5))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } - if ($1 != nullptr) - delete $1; } | - opt_sva_label TOK_ASSUME opt_property '(' expr ')' ';' { - if (noassume_mode) { - delete $5; + opt_sva_label TOK_ASSUME opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassume) { } else { - AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assert_assumes ? AST_ASSERT : AST_ASSUME, std::move($5))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } - if ($1 != nullptr) - delete $1; } | - opt_sva_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' { - if (noassert_mode) { - delete $6; + opt_sva_label TOK_ASSERT opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassert) { } else { - AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_FAIR : AST_LIVE, std::move($6))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } - if ($1 != nullptr) - delete $1; } | - opt_sva_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' { - if (noassume_mode) { - delete $6; + opt_sva_label TOK_ASSUME opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassume) { } else { - AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assert_assumes ? AST_LIVE : AST_FAIR, std::move($6))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } - if ($1 != nullptr) - delete $1; } | - opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { - AstNode *node = new AstNode(AST_COVER, $5); + opt_sva_label TOK_COVER opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, std::move($5))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) { node->str = *$1; - delete $1; } - ast_stack.back()->children.push_back(node); } | - opt_sva_label TOK_COVER opt_property '(' ')' ';' { - AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + opt_sva_label TOK_COVER opt_property TOK_LPAREN TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, AstNode::mkconst_int(@$, 1, false))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @5); if ($1 != nullptr) { node->str = *$1; - delete $1; } - ast_stack.back()->children.push_back(node); } | - opt_sva_label TOK_COVER ';' { - AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); + opt_sva_label TOK_COVER TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, AstNode::mkconst_int(@$, 1, false))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @2); if ($1 != nullptr) { node->str = *$1; - delete $1; } - ast_stack.back()->children.push_back(node); } | - opt_sva_label TOK_RESTRICT opt_property '(' expr ')' ';' { - if (norestrict_mode) { - delete $5; + opt_sva_label TOK_RESTRICT opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($5))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } if (!$3) - log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); - if ($1 != nullptr) - delete $1; + warn_at_loc(@3, "SystemVerilog does not allow \"restrict\" without \"property\"."); } | - opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) { - delete $6; + opt_sva_label TOK_RESTRICT opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($6))); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; - ast_stack.back()->children.push_back(node); } if (!$3) - log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); - if ($1 != nullptr) - delete $1; + warn_at_loc(@3, "SystemVerilog does not allow \"restrict\" without \"property\"."); }; assert_property: - opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + opt_sva_label TOK_ASSERT TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_ASSUME : AST_ASSERT, std::move($5))); SET_AST_NODE_LOC(node, @1, @6); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(AST_ASSUME, $5); + opt_sva_label TOK_ASSUME TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($5))); SET_AST_NODE_LOC(node, @1, @6); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + opt_sva_label TOK_ASSERT TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_FAIR : AST_LIVE, std::move($6))); SET_AST_NODE_LOC(node, @1, @7); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - AstNode *node = new AstNode(AST_FAIR, $6); + opt_sva_label TOK_ASSUME TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($6))); SET_AST_NODE_LOC(node, @1, @7); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(AST_COVER, $5); + opt_sva_label TOK_COVER TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, std::move($5))); SET_AST_NODE_LOC(node, @1, @6); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { - if (norestrict_mode) { - delete $5; + opt_sva_label TOK_RESTRICT TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($5))); SET_AST_NODE_LOC(node, @1, @6); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } } | - opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) { - delete $6; + opt_sva_label TOK_RESTRICT TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($6))); SET_AST_NODE_LOC(node, @1, @7); - ast_stack.back()->children.push_back(node); if ($1 != nullptr) { - ast_stack.back()->children.back()->str = *$1; - delete $1; + extra->ast_stack.back()->children.back()->str = *$1; } } }; simple_behavioral_stmt: - attr lvalue '=' delay expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $5); - ast_stack.back()->children.push_back(node); + attr lvalue TOK_EQ delay expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($5))); SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | attr lvalue attr inc_or_dec_op { - addIncOrDecStmt($1, $2, $3, $4, @1, @4); + extra->addIncOrDecStmt($1, std::move($2), $3, $4, location_range(@1, @4)); } | attr inc_or_dec_op attr lvalue { - addIncOrDecStmt($1, $4, $3, $2, @1, @4); + extra->addIncOrDecStmt($1, std::move($4), $3, $2, location_range(@1, @4)); } | attr lvalue OP_LE delay expr { - AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); - ast_stack.back()->children.push_back(node); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_LE, std::move($2), std::move($5))); SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | attr lvalue asgn_binop delay expr { - addAsgnBinopStmt($1, $2, $3, $5, @2, @5); + (void)extra->addAsgnBinopStmt($1, std::move($2), $3, std::move($5)); }; asgn_binop: @@ -2722,205 +2698,193 @@ inc_or_dec_op: TOK_DECREMENT { $$ = AST_SUB; } ; for_initialization: - TOK_ID '=' expr { - AstNode *ident = new AstNode(AST_IDENTIFIER); + TOK_ID TOK_EQ expr { + auto ident = std::make_unique(@$, AST_IDENTIFIER); ident->str = *$1; - AstNode *node = new AstNode(AST_ASSIGN_EQ, ident, $3); - ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @3); - delete $1; + auto node = std::make_unique(@$, AST_ASSIGN_EQ, std::move(ident), std::move($3)); + SET_AST_NODE_LOC(node.get(), @1, @3); + extra->ast_stack.back()->children.push_back(std::move(node)); } | non_io_wire_type range TOK_ID { - frontend_verilog_yyerror("For loop variable declaration is missing initialization!"); + err_at_loc(@3, "For loop variable declaration is missing initialization!"); } | - non_io_wire_type range TOK_ID '=' expr { - if (!sv_mode) - frontend_verilog_yyerror("For loop inline variable declaration is only supported in SystemVerilog mode!"); + non_io_wire_type range TOK_ID TOK_EQ expr { + if (!mode->sv) + err_at_loc(@4, "For loop inline variable declaration is only supported in SystemVerilog mode!"); // loop variable declaration - AstNode *wire = $1; - AstNode *range = checkRange(wire, $2); + auto wire = std::move($1); + auto range = checkRange(wire.get(), std::move($2)); + SET_AST_NODE_LOC(wire.get(), @1, @3); + SET_AST_NODE_LOC(range.get(), @2, @2); if (range != nullptr) - wire->children.push_back(range); - SET_AST_NODE_LOC(wire, @1, @3); - SET_AST_NODE_LOC(range, @2, @2); + wire->children.push_back(std::move(range)); - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(@$, AST_IDENTIFIER); ident->str = *$3; wire->str = *$3; - delete $3; - AstNode *loop = ast_stack.back(); - AstNode *parent = ast_stack.at(ast_stack.size() - 2); - log_assert(parent->children.back() == loop); + AstNode *parent = extra->ast_stack.at(extra->ast_stack.size() - 2); + auto& loop = parent->children.back(); + log_assert(extra->ast_stack.back() == loop.get()); // loop variable initialization - AstNode *asgn = new AstNode(AST_ASSIGN_EQ, ident, $5); - loop->children.push_back(asgn); - SET_AST_NODE_LOC(asgn, @3, @5); - SET_AST_NODE_LOC(ident, @3, @3); + SET_AST_NODE_LOC(ident.get(), @3, @3); + auto asgn = std::make_unique(@$, AST_ASSIGN_EQ, std::move(ident), std::move($5)); + SET_AST_NODE_LOC(asgn.get(), @3, @5); + loop->children.push_back(std::move(asgn)); // inject a wrapping block to declare the loop variable and // contain the current loop - AstNode *wrapper = new AstNode(AST_BLOCK); + auto wrapper = std::make_unique(@$, AST_BLOCK); wrapper->str = "$fordecl_block$" + std::to_string(autoidx++); - wrapper->children.push_back(wire); - wrapper->children.push_back(loop); - parent->children.back() = wrapper; // replaces `loop` + wrapper->children.push_back(std::move(wire)); + wrapper->children.push_back(std::move(loop)); + parent->children.back() = std::move(wrapper); }; // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl | non_opt_delay behavioral_stmt | - simple_behavioral_stmt ';' | - attr ';' { + simple_behavioral_stmt TOK_SEMICOL | + attr TOK_SEMICOL { free_attr($1); } | attr hierarchical_id { - AstNode *node = new AstNode(AST_TCALL); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_TCALL)); node->str = *$2; - delete $2; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); append_attr(node, $1); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @2, @5); - ast_stack.pop_back(); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @5); + extra->ast_stack.pop_back(); } | attr TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TCALL); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_TCALL)); node->str = *$2; - delete $2; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); append_attr(node, $1); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @2, @5); - ast_stack.pop_back(); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @5); + extra->ast_stack.pop_back(); } | attr TOK_BEGIN { - enterTypeScope(); + extra->enterTypeScope(); } opt_label { - AstNode *node = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_BLOCK)); append_attr(node, $1); - if ($4 != NULL) + if ($4 != nullptr) node->str = *$4; } behavioral_stmt_list TOK_END opt_label { - exitTypeScope(); - checkLabelsMatch("Begin label", $4, $8); - AstNode *node = ast_stack.back(); + extra->exitTypeScope(); + checkLabelsMatch(@8, "Begin label", $4.get(), $8.get()); + AstNode *node = extra->ast_stack.back(); // In SystemVerilog, unnamed blocks with block item declarations // create an implicit hierarchy scope - if (sv_mode && node->str.empty()) - for (const AstNode* child : node->children) + if (mode->sv && node->str.empty()) + for (auto& child : node->children) if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { node->str = "$unnamed_block$" + std::to_string(autoidx++); break; } - SET_AST_NODE_LOC(ast_stack.back(), @2, @8); - delete $4; - delete $8; - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @8); + extra->ast_stack.pop_back(); } | - attr TOK_FOR '(' { - AstNode *node = new AstNode(AST_FOR); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + attr TOK_FOR TOK_LPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_FOR)); append_attr(node, $1); - } for_initialization ';' expr { - ast_stack.back()->children.push_back($7); - } ';' simple_behavioral_stmt ')' { - AstNode *block = new AstNode(AST_BLOCK); + } for_initialization TOK_SEMICOL expr { + extra->ast_stack.back()->children.push_back(std::move($7)); + } TOK_SEMICOL simple_behavioral_stmt TOK_RPAREN { + AstNode* block = extra->pushChild(std::make_unique(@$, AST_BLOCK)); block->str = "$for_loop$" + std::to_string(autoidx++); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @13, @13); - ast_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @2, @13); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @13, @13); + extra->ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @13); + extra->ast_stack.pop_back(); } | - attr TOK_WHILE '(' expr ')' { - AstNode *node = new AstNode(AST_WHILE); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + attr TOK_WHILE TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_WHILE)); append_attr(node, $1); - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back($4); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + extra->ast_stack.back()->children.push_back(std::move($4)); + extra->ast_stack.back()->children.push_back(std::move(block_owned)); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); - ast_stack.pop_back(); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); } | - attr TOK_REPEAT '(' expr ')' { - AstNode *node = new AstNode(AST_REPEAT); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + attr TOK_REPEAT TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_REPEAT)); append_attr(node, $1); - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back($4); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + extra->ast_stack.back()->children.push_back(std::move($4)); + extra->ast_stack.back()->children.push_back(std::move(block_owned)); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); - ast_stack.pop_back(); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); } | - if_attr TOK_IF '(' expr ')' { - AstNode *node = 0; - AstNode *block = new AstNode(AST_BLOCK); - AstNode *context = ast_stack.back(); + if_attr TOK_IF TOK_LPAREN expr TOK_RPAREN { + std::unique_ptr node_owned; + AstNode* node = nullptr; + AstNode *context = extra->ast_stack.back(); + bool patch_block_on_stack = false; if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) { - AstNode *outer = ast_stack[ast_stack.size() - 2]; + AstNode *outer = extra->ast_stack[extra->ast_stack.size() - 2]; log_assert (outer && outer->type == AST_CASE); if (outer->get_bool_attribute(ID::parallel_case)) { // parallel "else if": append condition to outer "if" node = outer; log_assert (node->children.size()); - ast_stack.pop_back(); - delete node->children.back(); node->children.pop_back(); - ast_stack.push_back(block); + // `context` has been killed as a grandchild of `outer` + // we have to undangle it from the stack + patch_block_on_stack = true; } else if (outer->get_bool_attribute(ID::full_case)) - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); } - AstNode *expr = new AstNode(AST_REDUCE_BOOL, $4); + auto expr = std::make_unique(@$, AST_REDUCE_BOOL, std::move($4)); if (!node) { // not parallel "else if": begin new construction - node = new AstNode(AST_CASE); + node_owned = std::make_unique(@$, AST_CASE); + node = node_owned.get(); append_attr(node, $1); - ast_stack.back()->children.push_back(node); - node->children.push_back(node->get_bool_attribute(ID::parallel_case) ? AstNode::mkconst_int(1, false, 1) : expr); - } else + node->children.push_back(node->get_bool_attribute(ID::parallel_case) ? AstNode::mkconst_int(@$, 1, false, 1) : expr->clone()); + extra->ast_stack.back()->children.push_back(std::move(node_owned)); + } else { free_attr($1); - AstNode *cond = new AstNode(AST_COND, node->get_bool_attribute(ID::parallel_case) ? expr : AstNode::mkconst_int(1, false, 1), block); - SET_AST_NODE_LOC(cond, @4, @4); - node->children.push_back(cond); - ast_stack.push_back(node); - ast_stack.push_back(block); + } + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + auto cond_owned = std::make_unique(@$, AST_COND, node->get_bool_attribute(ID::parallel_case) ? std::move(expr) : AstNode::mkconst_int(@$, 1, false, 1), std::move(block_owned)); + SET_AST_NODE_LOC(cond_owned.get(), @4, @4); + node->children.push_back(std::move(cond_owned)); + // Double it and give it to the next person + if (patch_block_on_stack) + extra->ast_stack.back() = block; + extra->ast_stack.push_back(node); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); } optional_else { - ast_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @2, @9); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @9); + extra->ast_stack.pop_back(); } | - case_attr case_type '(' expr ')' { - AstNode *node = new AstNode(AST_CASE, $4); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + case_attr case_type TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_CASE, std::move($4))); append_attr(node, $1); - SET_AST_NODE_LOC(ast_stack.back(), @4, @4); + SET_AST_NODE_LOC(extra->ast_stack.back(), @4, @4); } opt_synopsys_attr case_body TOK_ENDCASE { - SET_AST_NODE_LOC(ast_stack.back(), @2, @9); - case_type_stack.pop_back(); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @9); + extra->case_type_stack.pop_back(); + extra->ast_stack.pop_back(); }; if_attr: @@ -2928,25 +2892,25 @@ if_attr: $$ = $1; } | attr TOK_UNIQUE0 { - AstNode *context = ast_stack.back(); + AstNode *context = extra->ast_stack.back(); if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) - frontend_verilog_yyerror("unique0 keyword cannot be used for 'else if' branch."); - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + err_at_loc(@2, "unique0 keyword cannot be used for 'else if' branch."); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; } | attr TOK_PRIORITY { - AstNode *context = ast_stack.back(); + AstNode *context = extra->ast_stack.back(); if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) - frontend_verilog_yyerror("priority keyword cannot be used for 'else if' branch."); - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + err_at_loc(@2, "priority keyword cannot be used for 'else if' branch."); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; } | attr TOK_UNIQUE { - AstNode *context = ast_stack.back(); + AstNode *context = extra->ast_stack.back(); if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) - frontend_verilog_yyerror("unique keyword cannot be used for 'else if' branch."); - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + err_at_loc(@2, "unique keyword cannot be used for 'else if' branch."); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; }; @@ -2955,38 +2919,38 @@ case_attr: $$ = $1; } | attr TOK_UNIQUE0 { - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; } | attr TOK_PRIORITY { - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; } | attr TOK_UNIQUE { - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); $$ = $1; }; case_type: TOK_CASE { - case_type_stack.push_back(0); + extra->case_type_stack.push_back(0); } | TOK_CASEX { - case_type_stack.push_back('x'); + extra->case_type_stack.push_back('x'); } | TOK_CASEZ { - case_type_stack.push_back('z'); + extra->case_type_stack.push_back('z'); }; opt_synopsys_attr: opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE { - if (ast_stack.back()->attributes.count(ID::full_case) == 0) - ast_stack.back()->attributes[ID::full_case] = AstNode::mkconst_int(1, false); + if (extra->ast_stack.back()->attributes.count(ID::full_case) == 0) + extra->ast_stack.back()->attributes[ID::full_case] = AstNode::mkconst_int(@$, 1, false); } | opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE { - if (ast_stack.back()->attributes.count(ID::parallel_case) == 0) - ast_stack.back()->attributes[ID::parallel_case] = AstNode::mkconst_int(1, false); + if (extra->ast_stack.back()->attributes.count(ID::parallel_case) == 0) + extra->ast_stack.back()->attributes[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); } | %empty; @@ -2996,16 +2960,18 @@ behavioral_stmt_list: optional_else: TOK_ELSE { - AstNode *block = new AstNode(AST_BLOCK); - block->attributes[ID::promoted_if] = AstNode::mkconst_int(1, false ); - AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block); + extra->ast_stack.pop_back(); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + block->attributes[ID::promoted_if] = AstNode::mkconst_int(@$, 1, false); + AstNode* cond = extra->saveChild( + std::make_unique(@$, AST_COND, + std::make_unique(@$, AST_DEFAULT), + std::move(block_owned))); + extra->ast_stack.push_back(block); SET_AST_NODE_LOC(cond, @1, @1); - - ast_stack.pop_back(); - ast_stack.back()->children.push_back(cond); - ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @3, @3); + SET_AST_NODE_LOC(extra->ast_stack.back(), @3, @3); } | %empty %prec FAKE_THEN; @@ -3015,21 +2981,18 @@ case_body: case_item: { - AstNode *node = new AstNode( - case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : - case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + (void)extra->pushChild(std::make_unique( + @$, + extra->case_type_stack.size() && extra->case_type_stack.back() == 'x' ? AST_CONDX : + extra->case_type_stack.size() && extra->case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND)); } case_select { - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); - case_type_stack.push_back(0); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); + extra->case_type_stack.push_back(0); } behavioral_stmt { - case_type_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @4, @4); - ast_stack.pop_back(); - ast_stack.pop_back(); + extra->case_type_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @4, @4); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); }; gen_case_body: @@ -3038,87 +3001,79 @@ gen_case_body: gen_case_item: { - AstNode *node = new AstNode( - case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : - case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + (void)extra->pushChild(std::make_unique( + @$, + extra->case_type_stack.size() && extra->case_type_stack.back() == 'x' ? AST_CONDX : + extra->case_type_stack.size() && extra->case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND)); } case_select { - case_type_stack.push_back(0); - SET_AST_NODE_LOC(ast_stack.back(), @2, @2); + extra->case_type_stack.push_back(0); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2); } gen_stmt_block { - case_type_stack.pop_back(); - ast_stack.pop_back(); + extra->case_type_stack.pop_back(); + extra->ast_stack.pop_back(); }; case_select: - case_expr_list ':' | + case_expr_list TOK_COL | TOK_DEFAULT; case_expr_list: TOK_DEFAULT { - AstNode *node = new AstNode(AST_DEFAULT); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_DEFAULT)); SET_AST_NODE_LOC(node, @1, @1); - ast_stack.back()->children.push_back(node); } | TOK_SVA_LABEL { - AstNode *node = new AstNode(AST_IDENTIFIER); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_IDENTIFIER)); SET_AST_NODE_LOC(node, @1, @1); - ast_stack.back()->children.push_back(node); - ast_stack.back()->children.back()->str = *$1; - delete $1; } | expr { - ast_stack.back()->children.push_back($1); + extra->ast_stack.back()->children.push_back(std::move($1)); } | - case_expr_list ',' expr { - ast_stack.back()->children.push_back($3); + case_expr_list TOK_COMMA expr { + extra->ast_stack.back()->children.push_back(std::move($3)); }; rvalue: - hierarchical_id '[' expr ']' '.' rvalue { - $$ = new AstNode(AST_PREFIX, $3, $6); + hierarchical_id TOK_LBRA expr TOK_RBRA TOK_DOT rvalue { + $$ = std::make_unique(@$, AST_PREFIX, std::move($3), std::move($6)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @6); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @6); } | hierarchical_id range { - $$ = new AstNode(AST_IDENTIFIER, $2); + $$ = std::make_unique(@$, AST_IDENTIFIER, std::move($2)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @1); if ($2 == nullptr && ($$->str == "\\$initstate" || $$->str == "\\$anyconst" || $$->str == "\\$anyseq" || $$->str == "\\$allconst" || $$->str == "\\$allseq")) $$->type = AST_FCALL; } | hierarchical_id non_opt_multirange { - $$ = new AstNode(AST_IDENTIFIER, $2); + $$ = std::make_unique(@$, AST_IDENTIFIER, std::move($2)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @1); }; lvalue: rvalue { - $$ = $1; + $$ = std::move($1); } | - '{' lvalue_concat_list '}' { - $$ = $2; + TOK_LCURL lvalue_concat_list TOK_RCURL { + $$ = std::move($2); }; lvalue_concat_list: expr { - $$ = new AstNode(AST_CONCAT); - $$->children.push_back($1); + $$ = std::make_unique(@$, AST_CONCAT); + $$->children.push_back(std::move($1)); } | - expr ',' lvalue_concat_list { - $$ = $3; - $$->children.push_back($1); + expr TOK_COMMA lvalue_concat_list { + $$ = std::move($3); + $$->children.push_back(std::move($1)); }; opt_arg_list: - '(' arg_list optional_comma ')' | + TOK_LPAREN arg_list optional_comma TOK_RPAREN | %empty; arg_list: @@ -3127,11 +3082,11 @@ arg_list: arg_list2: single_arg | - arg_list ',' single_arg; + arg_list TOK_COMMA single_arg; single_arg: expr { - ast_stack.back()->children.push_back($1); + extra->ast_stack.back()->children.push_back(std::move($1)); }; module_gen_body: @@ -3141,111 +3096,92 @@ module_gen_body: gen_stmt_or_module_body_stmt: gen_stmt | module_body_stmt | - attr ';' { + attr TOK_SEMICOL { free_attr($1); }; genvar_identifier: TOK_ID { - $$ = new AstNode(AST_IDENTIFIER); + $$ = std::make_unique(@$, AST_IDENTIFIER); $$->str = *$1; - delete $1; }; genvar_initialization: TOK_GENVAR genvar_identifier { - frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!"); + err_at_loc(@2, "Generate for loop variable declaration is missing initialization!"); } | - TOK_GENVAR genvar_identifier '=' expr { - if (!sv_mode) - frontend_verilog_yyerror("Generate for loop inline variable declaration is only supported in SystemVerilog mode!"); - AstNode *node = new AstNode(AST_GENVAR); + TOK_GENVAR genvar_identifier TOK_EQ expr { + if (!mode->sv) + err_at_loc(@3, "Generate for loop inline variable declaration is only supported in SystemVerilog mode!"); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_GENVAR)); node->is_reg = true; node->is_signed = true; node->range_left = 31; node->range_right = 0; node->str = $2->str; node->children.push_back(checkRange(node, nullptr)); - ast_stack.back()->children.push_back(node); SET_AST_NODE_LOC(node, @1, @4); - node = new AstNode(AST_ASSIGN_EQ, $2, $4); - ast_stack.back()->children.push_back(node); + node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($4))); SET_AST_NODE_LOC(node, @1, @4); } | - genvar_identifier '=' expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); - ast_stack.back()->children.push_back(node); + genvar_identifier TOK_EQ expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($1), std::move($3))); SET_AST_NODE_LOC(node, @1, @3); }; // this production creates the obligatory if-else shift/reduce conflict gen_stmt: - TOK_FOR '(' { - AstNode *node = new AstNode(AST_GENFOR); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } genvar_initialization ';' expr { - ast_stack.back()->children.push_back($6); - } ';' simple_behavioral_stmt ')' gen_stmt_block { - SET_AST_NODE_LOC(ast_stack.back(), @1, @11); - rewriteGenForDeclInit(ast_stack.back()); - ast_stack.pop_back(); + TOK_FOR TOK_LPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENFOR)); + } genvar_initialization TOK_SEMICOL expr { + extra->ast_stack.back()->children.push_back(std::move($6)); + } TOK_SEMICOL simple_behavioral_stmt TOK_RPAREN gen_stmt_block { + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @11); + extra->rewriteGenForDeclInit(extra->ast_stack.back()); + extra->ast_stack.pop_back(); } | - TOK_IF '(' expr ')' { - AstNode *node = new AstNode(AST_GENIF); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - ast_stack.back()->children.push_back($3); + TOK_IF TOK_LPAREN expr TOK_RPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENIF)); + extra->ast_stack.back()->children.push_back(std::move($3)); } gen_stmt_block opt_gen_else { - SET_AST_NODE_LOC(ast_stack.back(), @1, @7); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->ast_stack.pop_back(); } | - case_type '(' expr ')' { - AstNode *node = new AstNode(AST_GENCASE, $3); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + case_type TOK_LPAREN expr TOK_RPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENCASE, std::move($3))); } gen_case_body TOK_ENDCASE { - case_type_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @1, @7); - ast_stack.pop_back(); + extra->case_type_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->ast_stack.pop_back(); } | TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_TECALL)); node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @1, @3); - ast_stack.pop_back(); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @3); + extra->ast_stack.pop_back(); }; gen_block: TOK_BEGIN { - enterTypeScope(); + extra->enterTypeScope(); } opt_label { - AstNode *node = new AstNode(AST_GENBLOCK); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_GENBLOCK)); node->str = $3 ? *$3 : std::string(); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); } module_gen_body TOK_END opt_label { - exitTypeScope(); - checkLabelsMatch("Begin label", $3, $7); - delete $3; - delete $7; - SET_AST_NODE_LOC(ast_stack.back(), @1, @7); - ast_stack.pop_back(); + extra->exitTypeScope(); + checkLabelsMatch(@7, "Begin label", $3.get(), $7.get()); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->ast_stack.pop_back(); }; // result is wrapped in a genblock only if necessary gen_stmt_block: { - AstNode *node = new AstNode(AST_GENBLOCK); - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); + (void)extra->pushChild(std::make_unique(@$, AST_GENBLOCK)); } gen_stmt_or_module_body_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @2, @2); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2); + extra->ast_stack.pop_back(); } | gen_block; opt_gen_else: @@ -3253,349 +3189,340 @@ opt_gen_else: expr: basic_expr { - $$ = $1; + $$ = std::move($1); } | - basic_expr '?' attr expr ':' expr { - $$ = new AstNode(AST_TERNARY); - $$->children.push_back($1); - $$->children.push_back($4); - $$->children.push_back($6); - SET_AST_NODE_LOC($$, @1, @$); - append_attr($$, $3); + basic_expr TOK_QUE attr expr TOK_COL expr { + $$ = std::make_unique(@$, AST_TERNARY); + $$->children.push_back(std::move($1)); + $$->children.push_back(std::move($4)); + $$->children.push_back(std::move($6)); + SET_AST_NODE_LOC($$.get(), @1, @$); + append_attr($$.get(), $3); } | inc_or_dec_op attr rvalue { - $$ = addIncOrDecExpr($3, $2, $1, @1, @3, false); + $$ = extra->addIncOrDecExpr(std::move($3), $2, $1, location_range(@1, @3), false, mode->sv); } | // TODO: Attributes are allowed in the middle here, but they create some // non-trivial conflicts that don't seem worth solving for now. rvalue inc_or_dec_op { - $$ = addIncOrDecExpr($1, nullptr, $2, @1, @2, true); + $$ = extra->addIncOrDecExpr(std::move($1), nullptr, $2, location_range(@1, @2), true, mode->sv); }; basic_expr: rvalue { - $$ = $1; + $$ = std::move($1); } | - '(' expr ')' integral_number { + TOK_LPAREN expr TOK_RPAREN integral_number { if ($4->compare(0, 1, "'") != 0) - frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", $4->c_str()); - AstNode *bits = $2; - AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); - if (val == NULL) + err_at_loc(@4, "Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", $4->c_str()); + ConstParser p{@4}; + auto val = p.const2ast(*$4, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + if (val == nullptr) log_error("Value conversion failed: `%s'\n", $4->c_str()); - $$ = new AstNode(AST_TO_BITS, bits, val); - delete $4; + $$ = std::make_unique(@$, AST_TO_BITS, std::move($2), std::move(val)); } | hierarchical_id integral_number { if ($2->compare(0, 1, "'") != 0) - frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", $2->c_str()); - AstNode *bits = new AstNode(AST_IDENTIFIER); + err_at_loc(@2, "Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", $2->c_str()); + auto bits = std::make_unique(@$, AST_IDENTIFIER); bits->str = *$1; - SET_AST_NODE_LOC(bits, @1, @1); - AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); - SET_AST_NODE_LOC(val, @2, @2); - if (val == NULL) + SET_AST_NODE_LOC(bits.get(), @1, @1); + ConstParser p{@2}; + auto val = p.const2ast(*$2, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + SET_AST_NODE_LOC(val.get(), @2, @2); + if (val == nullptr) log_error("Value conversion failed: `%s'\n", $2->c_str()); - $$ = new AstNode(AST_TO_BITS, bits, val); - delete $1; - delete $2; + $$ = std::make_unique(@$, AST_TO_BITS, std::move(bits), std::move(val)); } | integral_number { - $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); - SET_AST_NODE_LOC($$, @1, @1); - if ($$ == NULL) + ConstParser p{@1}; + $$ = p.const2ast(*$1, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + SET_AST_NODE_LOC($$.get(), @1, @1); + if ($$ == nullptr) log_error("Value conversion failed: `%s'\n", $1->c_str()); - delete $1; } | TOK_REALVAL { - $$ = new AstNode(AST_REALVALUE); + $$ = std::make_unique(@$, AST_REALVALUE); char *p = (char*)malloc(GetSize(*$1) + 1), *q; for (int i = 0, j = 0; j < GetSize(*$1); j++) if ((*$1)[j] != '_') p[i++] = (*$1)[j], p[i] = 0; $$->realvalue = strtod(p, &q); - SET_AST_NODE_LOC($$, @1, @1); + SET_AST_NODE_LOC($$.get(), @1, @1); log_assert(*q == 0); - delete $1; free(p); } | TOK_STRING { - $$ = AstNode::mkconst_str(*$1); - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + $$ = AstNode::mkconst_str(@1, *$1); + SET_AST_NODE_LOC($$.get(), @1, @1); } | hierarchical_id attr { - AstNode *node = new AstNode(AST_FCALL); + // super sketchy! Orphaned pointer in non-owning extra->ast_stack + AstNode *node = new AstNode(@1, AST_FCALL); node->str = *$1; - delete $1; - ast_stack.push_back(node); + extra->ast_stack.push_back(node); SET_AST_NODE_LOC(node, @1, @1); append_attr(node, $2); - } '(' arg_list optional_comma ')' { - $$ = ast_stack.back(); - ast_stack.pop_back(); + } TOK_LPAREN arg_list optional_comma TOK_RPAREN { + $$.reset(extra->ast_stack.back()); + extra->ast_stack.pop_back(); } | - TOK_TO_SIGNED attr '(' expr ')' { - $$ = new AstNode(AST_TO_SIGNED, $4); - append_attr($$, $2); + TOK_TO_SIGNED attr TOK_LPAREN expr TOK_RPAREN { + $$ = std::make_unique(@$, AST_TO_SIGNED, std::move($4)); + append_attr($$.get(), $2); } | - TOK_TO_UNSIGNED attr '(' expr ')' { - $$ = new AstNode(AST_TO_UNSIGNED, $4); - append_attr($$, $2); + TOK_TO_UNSIGNED attr TOK_LPAREN expr TOK_RPAREN { + $$ = std::make_unique(@$, AST_TO_UNSIGNED, std::move($4)); + append_attr($$.get(), $2); } | - '(' expr ')' { - $$ = $2; + TOK_LPAREN expr TOK_RPAREN { + $$ = std::move($2); } | - '(' expr ':' expr ':' expr ')' { - delete $2; - $$ = $4; - delete $6; + TOK_LPAREN expr TOK_COL expr TOK_COL expr TOK_RPAREN { + $$ = std::move($4); } | - '{' concat_list '}' { - $$ = $2; + TOK_LCURL concat_list TOK_RCURL { + $$ = std::move($2); } | - '{' expr '{' concat_list '}' '}' { - $$ = new AstNode(AST_REPLICATE, $2, $4); + TOK_LCURL expr TOK_LCURL concat_list TOK_RCURL TOK_RCURL { + $$ = std::make_unique(@$, AST_REPLICATE, std::move($2), std::move($4)); } | - '~' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_BIT_NOT, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_TILDE attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_BIT_NOT, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | - basic_expr '&' attr basic_expr { - $$ = new AstNode(AST_BIT_AND, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_AMP attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_AND, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_NAND attr basic_expr { - $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_AND, $1, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_BIT_NOT, std::make_unique(@$, AST_BIT_AND, std::move($1), std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '|' attr basic_expr { - $$ = new AstNode(AST_BIT_OR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PIPE attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_OR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_NOR attr basic_expr { - $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_OR, $1, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_BIT_NOT, std::make_unique(@$, AST_BIT_OR, std::move($1), std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '^' attr basic_expr { - $$ = new AstNode(AST_BIT_XOR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_CARET attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_XOR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_XNOR attr basic_expr { - $$ = new AstNode(AST_BIT_XNOR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_BIT_XNOR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - '&' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_AND, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_AMP attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_AND, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | OP_NAND attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_AND, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); - $$ = new AstNode(AST_LOGIC_NOT, $$); + $$ = std::make_unique(@$, AST_REDUCE_AND, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($$)); } | - '|' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_OR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_PIPE attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_OR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); } | OP_NOR attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_OR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); - $$ = new AstNode(AST_LOGIC_NOT, $$); - SET_AST_NODE_LOC($$, @1, @3); + $$ = std::make_unique(@$, AST_REDUCE_OR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($$)); + SET_AST_NODE_LOC($$.get(), @1, @3); } | - '^' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_XOR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_CARET attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_XOR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | OP_XNOR attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_XNOR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + $$ = std::make_unique(@$, AST_REDUCE_XNOR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | basic_expr OP_SHL attr basic_expr { - $$ = new AstNode(AST_SHIFT_LEFT, $1, new AstNode(AST_TO_UNSIGNED, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_SHIFT_LEFT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_SHR attr basic_expr { - $$ = new AstNode(AST_SHIFT_RIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_SHIFT_RIGHT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_SSHL attr basic_expr { - $$ = new AstNode(AST_SHIFT_SLEFT, $1, new AstNode(AST_TO_UNSIGNED, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_SHIFT_SLEFT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_SSHR attr basic_expr { - $$ = new AstNode(AST_SHIFT_SRIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4)); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_SHIFT_SRIGHT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '<' attr basic_expr { - $$ = new AstNode(AST_LT, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_LT attr basic_expr { + $$ = std::make_unique(@$, AST_LT, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_LE attr basic_expr { - $$ = new AstNode(AST_LE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_EQ attr basic_expr { - $$ = new AstNode(AST_EQ, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_EQ, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_NE attr basic_expr { - $$ = new AstNode(AST_NE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_NE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_EQX attr basic_expr { - $$ = new AstNode(AST_EQX, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_EQX, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_NEX attr basic_expr { - $$ = new AstNode(AST_NEX, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_NEX, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_GE attr basic_expr { - $$ = new AstNode(AST_GE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_GE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '>' attr basic_expr { - $$ = new AstNode(AST_GT, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_GT attr basic_expr { + $$ = std::make_unique(@$, AST_GT, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '+' attr basic_expr { - $$ = new AstNode(AST_ADD, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PLUS attr basic_expr { + $$ = std::make_unique(@$, AST_ADD, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '-' attr basic_expr { - $$ = new AstNode(AST_SUB, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_MINUS attr basic_expr { + $$ = std::make_unique(@$, AST_SUB, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '*' attr basic_expr { - $$ = new AstNode(AST_MUL, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_ASTER attr basic_expr { + $$ = std::make_unique(@$, AST_MUL, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '/' attr basic_expr { - $$ = new AstNode(AST_DIV, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_SLASH attr basic_expr { + $$ = std::make_unique(@$, AST_DIV, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - basic_expr '%' attr basic_expr { - $$ = new AstNode(AST_MOD, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PERC attr basic_expr { + $$ = std::make_unique(@$, AST_MOD, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_POW attr basic_expr { - $$ = new AstNode(AST_POW, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_POW, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - '+' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_POS, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_PLUS attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_POS, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | - '-' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_NEG, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_MINUS attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_NEG, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | basic_expr OP_LAND attr basic_expr { - $$ = new AstNode(AST_LOGIC_AND, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LOGIC_AND, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | basic_expr OP_LOR attr basic_expr { - $$ = new AstNode(AST_LOGIC_OR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LOGIC_OR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); } | - '!' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_LOGIC_NOT, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_EXCL attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), $2); } | - TOK_SIGNED OP_CAST '(' expr ')' { - if (!sv_mode) - frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); - $$ = new AstNode(AST_TO_SIGNED, $4); - SET_AST_NODE_LOC($$, @1, @4); + TOK_SIGNED OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_TO_SIGNED, std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); } | - TOK_UNSIGNED OP_CAST '(' expr ')' { - if (!sv_mode) - frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); - $$ = new AstNode(AST_TO_UNSIGNED, $4); - SET_AST_NODE_LOC($$, @1, @4); + TOK_UNSIGNED OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_TO_UNSIGNED, std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); } | - basic_expr OP_CAST '(' expr ')' { - if (!sv_mode) - frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); - $$ = new AstNode(AST_CAST_SIZE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); + basic_expr OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_CAST_SIZE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); } | - typedef_base_type OP_CAST '(' expr ')' { - if (!sv_mode) - frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); - $$ = new AstNode(AST_CAST_SIZE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); + typedef_base_type OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_CAST_SIZE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); } | - '(' expr '=' expr ')' { - ensureAsgnExprAllowed(); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $4); - ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @2, @4); + TOK_LPAREN expr TOK_EQ expr TOK_RPAREN { + extra->ensureAsgnExprAllowed(@3, mode->sv); $$ = $2->clone(); + auto node = std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($4)); + SET_AST_NODE_LOC(node.get(), @2, @4); + extra->ast_stack.back()->children.push_back(std::move(node)); } | - '(' expr asgn_binop expr ')' { - ensureAsgnExprAllowed(); - $$ = addAsgnBinopStmt(nullptr, $2, $3, $4, @2, @4)-> clone(); + TOK_LPAREN expr asgn_binop expr TOK_RPAREN { + extra->ensureAsgnExprAllowed(@3, mode->sv); + $$ = extra->addAsgnBinopStmt(nullptr, std::move($2), $3, std::move($4))-> clone(); }; concat_list: expr { - $$ = new AstNode(AST_CONCAT, $1); + $$ = std::make_unique(@$, AST_CONCAT, std::move($1)); } | - expr ',' concat_list { - $$ = $3; - $$->children.push_back($1); + expr TOK_COMMA concat_list { + $$ = std::move($3); + $$->children.push_back(std::move($1)); }; integral_number: - TOK_CONSTVAL { $$ = $1; } | - TOK_UNBASED_UNSIZED_CONSTVAL { $$ = $1; } | + TOK_CONSTVAL { $$ = std::move($1); } | + TOK_UNBASED_UNSIZED_CONSTVAL { $$ = std::move($1); } | TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2); - $$ = $1; - delete $2; + $$ = std::move($1); } | TOK_CONSTVAL TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2).append(*$3); - $$ = $1; - delete $2; - delete $3; + $$ = std::move($1); }; diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 821490dca..0e12e6dce 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -25,6 +25,18 @@ YOSYS_NAMESPACE_BEGIN +/** + * This file implements BitPatternPool for efficiently storing and querying + * sets of fixed-width 2-valued logic constants compressed as "bit patterns". + * A bit pattern can have don't cares on one or more bit positions (State::Sa). + * + * In terms of logic synthesis: + * A BitPatternPool is a sum of products (SOP). + * BitPatternPool::bits_t is a cube. + * + * BitPatternPool does not permit adding new patterns, only removing. + * Its intended use case is in analysing cases in case/match constructs in HDL. + */ struct BitPatternPool { int width; @@ -67,6 +79,9 @@ struct BitPatternPool } } + /** + * Constructs a pool of all possible patterns (all don't-care bits) + */ BitPatternPool(int width) { this->width = width; @@ -78,6 +93,10 @@ struct BitPatternPool } } + /** + * Convert a constant SigSpec to a pattern. Normalize Yosys many-valued + * to three-valued logic. + */ bits_t sig2bits(RTLIL::SigSpec sig) { bits_t bits; @@ -88,6 +107,9 @@ struct BitPatternPool return bits; } + /** + * Two cubes match if their intersection is non-empty. + */ bool match(bits_t a, bits_t b) { log_assert(int(a.bitdata.size()) == width); @@ -98,6 +120,15 @@ struct BitPatternPool return true; } + /** + * Does cube sig overlap any cube in the pool? + * For example: + * pool({aaa}).has_any(01a) == true + * pool({01a}).has_any(01a) == true + * pool({011}).has_any(01a) == true + * pool({01a}).has_any(011) == true + * pool({111}).has_any(01a) == false + */ bool has_any(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); @@ -107,6 +138,15 @@ struct BitPatternPool return false; } + /** + * Is cube sig covered by a cube in the pool? + * For example: + * pool({aaa}).has_all(01a) == true + * pool({01a}).has_any(01a) == true + * pool({01a}).has_any(011) == true + * pool({011}).has_all(01a) == false + * pool({111}).has_all(01a) == false + */ bool has_all(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); @@ -121,6 +161,12 @@ struct BitPatternPool return false; } + /** + * Remove cube sig from the pool, splitting the remaining cubes. True if success. + * For example: + * Taking 011 out of pool({01a}) -> pool({010}), returns true. + * Taking 011 out of pool({010}) does nothing, returns false. + */ bool take(RTLIL::SigSpec sig) { bool status = false; @@ -143,6 +189,9 @@ struct BitPatternPool return status; } + /** + * Remove all patterns. Returns false if already empty. + */ bool take_all() { if (database.empty()) diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 0ce5db54d..11640c25f 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -334,6 +334,7 @@ struct CellTypes return v; } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len, bool *errp = nullptr) { if (type == ID($sshr) && !signed1) @@ -416,6 +417,7 @@ struct CellTypes log_abort(); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool *errp = nullptr) { if (cell->type == ID($slice)) { @@ -503,10 +505,13 @@ struct CellTypes return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { if (cell->type.in(ID($mux), ID($_MUX_))) return const_mux(arg1, arg2, arg3); + if (cell->type == ID($_NMUX_)) + return eval_not(const_mux(arg1, arg2, arg3)); if (cell->type == ID($bwmux)) return const_bwmux(arg1, arg2, arg3); if (cell->type == ID($pmux)) @@ -520,6 +525,7 @@ struct CellTypes return eval(cell, arg1, arg2, errp); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4, bool *errp = nullptr) { if (cell->type == ID($_AOI4_)) diff --git a/kernel/consteval.h b/kernel/consteval.h index 844120ef0..adcf86f8a 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -349,7 +349,11 @@ struct ConstEval return false; bool eval_err = false; - RTLIL::Const eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_c.as_const(), sig_d.as_const(), &eval_err); + RTLIL::Const eval_ret; + if (sig_s.size() > 0 && eval(sig_s, undef, cell)) { + eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_s.as_const(), &eval_err); + } else + eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_c.as_const(), sig_d.as_const(), &eval_err); if (eval_err) return false; diff --git a/kernel/ffinit.h b/kernel/ffinit.h index 6d4c97ac3..66c13b68f 100644 --- a/kernel/ffinit.h +++ b/kernel/ffinit.h @@ -27,10 +27,10 @@ YOSYS_NAMESPACE_BEGIN struct FfInitVals { - const SigMap *sigmap; + const SigMapView *sigmap; dict> initbits; - void set(const SigMap *sigmap_, RTLIL::Module *module) + void set(const SigMapView *sigmap_, RTLIL::Module *module) { sigmap = sigmap_; initbits.clear(); @@ -126,7 +126,7 @@ struct FfInitVals initbits.clear(); } - FfInitVals (const SigMap *sigmap, RTLIL::Module *module) + FfInitVals (const SigMapView *sigmap, RTLIL::Module *module) { set(sigmap, module); } diff --git a/kernel/ffmerge.h b/kernel/ffmerge.h index 5428da324..028987503 100644 --- a/kernel/ffmerge.h +++ b/kernel/ffmerge.h @@ -58,7 +58,7 @@ YOSYS_NAMESPACE_BEGIN struct FfMergeHelper { - const SigMap *sigmap; + const SigMapView *sigmap; RTLIL::Module *module; FfInitVals *initvals; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 52ff96028..9c53e6687 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -12,6 +12,7 @@ #ifndef HASHLIB_H #define HASHLIB_H +#include #include #include #include @@ -100,7 +101,7 @@ private: uint32_t hash = ((a << 5) + a) ^ b; return hash; } - public: +public: void hash32(uint32_t i) { state = djb2_xor(i, state); state = mkhash_xorshift(fudge ^ state); @@ -127,6 +128,7 @@ private: *this = hash_ops::hash_into(t, *this); } + [[deprecated]] void commutative_eat(hash_t t) { state ^= t; } @@ -177,58 +179,58 @@ struct hash_ops { }; template struct hash_ops> { - static inline bool cmp(std::pair a, std::pair b) { + static inline bool cmp(const std::pair &a, const std::pair &b) { return a == b; } - [[nodiscard]] static inline Hasher hash_into(std::pair a, Hasher h) { + [[nodiscard]] static inline Hasher hash_into(const 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 + HASH_TOP_LOOP_FST (const std::pair &a) HASH_TOP_LOOP_SND }; template struct hash_ops> { - static inline bool cmp(std::tuple a, std::tuple b) { + static inline bool cmp(const std::tuple &a, const std::tuple &b) { return a == b; } template - static inline typename std::enable_if::type hash_into(std::tuple, Hasher h) { + static inline typename std::enable_if::type hash_into(const std::tuple &, Hasher h) { return h; } template - static inline typename std::enable_if::type hash_into(std::tuple a, Hasher h) { + static inline typename std::enable_if::type hash_into(const std::tuple &a, Hasher h) { typedef hash_ops>::type> element_ops_t; 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 + HASH_TOP_LOOP_FST (const std::tuple &a) HASH_TOP_LOOP_SND }; template struct hash_ops> { - static inline bool cmp(std::vector a, std::vector b) { + static inline bool cmp(const std::vector &a, const std::vector &b) { return a == b; } - [[nodiscard]] static inline Hasher hash_into(std::vector a, Hasher h) { + [[nodiscard]] static inline Hasher hash_into(const std::vector &a, Hasher h) { h.eat((uint32_t)a.size()); for (auto k : a) h.eat(k); return h; } - HASH_TOP_LOOP_FST (std::vector a) HASH_TOP_LOOP_SND + HASH_TOP_LOOP_FST (const std::vector &a) HASH_TOP_LOOP_SND }; template struct hash_ops> { - static inline bool cmp(std::array a, std::array b) { + static inline bool cmp(const std::array &a, const std::array &b) { return a == b; } - [[nodiscard]] static inline Hasher hash_into(std::array a, Hasher h) { + [[nodiscard]] static inline Hasher hash_into(const 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 + HASH_TOP_LOOP_FST (const std::array &a) HASH_TOP_LOOP_SND }; struct hash_cstr_ops { @@ -300,10 +302,10 @@ template<> struct hash_ops { }; template struct hash_ops> { - static inline bool cmp(std::variant a, std::variant b) { + static inline bool cmp(const std::variant &a, const std::variant &b) { return a == b; } - [[nodiscard]] static inline Hasher hash_into(std::variant a, Hasher h) { + [[nodiscard]] static inline Hasher hash_into(const std::variant &a, Hasher h) { std::visit([& h](const auto &v) { h.eat(v); }, a); h.eat(a.index()); return h; @@ -311,10 +313,10 @@ template struct hash_ops> { }; template struct hash_ops> { - static inline bool cmp(std::optional a, std::optional b) { + static inline bool cmp(const std::optional &a, const std::optional &b) { return a == b; } - [[nodiscard]] static inline Hasher hash_into(std::optional a, Hasher h) { + [[nodiscard]] static inline Hasher hash_into(const std::optional &a, Hasher h) { if(a.has_value()) h.eat(*a); else @@ -356,6 +358,29 @@ template> class idict; template> class pool; template> class mfp; +// Computes the hash value of an unordered set of elements. +// See https://www.preprints.org/manuscript/201710.0192/v1/download. +// This is the Sum(4) algorithm from that paper, which has good collision resistance, +// much better than Sum(1) or Xor(1) (and somewhat better than Xor(4)). +class commutative_hash { +public: + commutative_hash() { + buckets.fill(0); + } + void eat(Hasher h) { + Hasher::hash_t v = h.yield(); + size_t index = v & (buckets.size() - 1); + buckets[index] += v; + } + [[nodiscard]] Hasher hash_into(Hasher h) const { + for (auto b : buckets) + h.eat(b); + return h; + } +private: + std::array buckets; +}; + template class dict { struct entry_t @@ -801,14 +826,14 @@ public: } [[nodiscard]] Hasher hash_into(Hasher h) const { + commutative_hash comm; 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()); + comm.eat(entry_hash); } - h.eat(entries.size()); - return h; + return comm.hash_into(h); } void reserve(size_t n) { entries.reserve(n); } @@ -1184,11 +1209,11 @@ public: } [[nodiscard]] Hasher hash_into(Hasher h) const { + commutative_hash comm; for (auto &it : entries) { - h.commutative_eat(ops.hash(it.udata).yield()); + comm.eat(ops.hash(it.udata)); } - h.eat(entries.size()); - return h; + return comm.hash_into(h); } void reserve(size_t n) { entries.reserve(n); } @@ -1353,7 +1378,8 @@ public: return p; } - // Merge sets if the given indices belong to different sets + // Merge sets if the given indices belong to different sets. + // Makes ifind(j) the root of the merged set. void imerge(int i, int j) { i = ifind(i); diff --git a/kernel/io.cc b/kernel/io.cc index 9811919ba..ef73a3b3d 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -412,6 +412,70 @@ static std::string string_view_stringf(std::string_view spec, ...) return result; } +static int spec_parameter_size(std::string_view spec) +{ + // Every valid spec starts with '%' which means the code below + // won't look before the spec start. + switch (spec[spec.size() - 1]) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + switch (spec[spec.size() - 2]) { + case 'h': + if (spec[spec.size() - 3] == 'h') + return sizeof(char); + return sizeof(short); + case 'l': + if (spec[spec.size() - 3] == 'l') + return sizeof(long long); + return sizeof(long); + case 'L': + case 'q': + return sizeof(long long); + case 'j': + return sizeof(intmax_t); + case 'z': + case 'Z': + return sizeof(size_t); + case 't': + return sizeof(ptrdiff_t); + } + return sizeof(int); + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if (spec[spec.size() - 2] == 'L') + return sizeof(long double); + if (spec[spec.size() - 2] == 'l' && spec[spec.size() - 3] == 'l') + return sizeof(long double); + return sizeof(double); + case 'c': + if (spec[spec.size() - 2] == 'l') { + return sizeof(wchar_t); + } + return sizeof(unsigned char); + case 'C': + return sizeof(wchar_t); + case 's': + case 'p': + case 'S': + case 'n': + return sizeof(void *); + case 'm': + return sizeof(int); + default: + return -1; + } +} + template static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints, DynamicIntCount num_dynamic_ints, Arg arg) @@ -439,7 +503,13 @@ void format_emit_long_long(std::string &result, std::string_view spec, int *dyna result += std::to_string(static_cast(arg)); return; } - format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + if (spec_parameter_size(spec) <= 4) { + // On some platforms (Wasm) we must ensure that the arg is properly aligned + // after the dynamic `int` parameters. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (int)arg); + } else { + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + } } void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, @@ -454,7 +524,13 @@ void format_emit_unsigned_long_long(std::string &result, std::string_view spec, result += static_cast(arg); return; } - format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + if (spec_parameter_size(spec) <= 4) { + // On some platforms (Wasm) we must ensure that the arg is properly aligned + // after the dynamic `int` parameters. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (unsigned int)arg); + } else { + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + } } void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, diff --git a/kernel/log.cc b/kernel/log.cc index 679a55562..011071439 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -44,6 +44,7 @@ std::vector log_scratchpads; std::map> log_hdump; std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict log_expect_log, log_expect_warning, log_expect_error; +dict log_expect_prefix_log, log_expect_prefix_warning, log_expect_prefix_error; std::set log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; int log_warnings_count_noexpect = 0; @@ -178,7 +179,7 @@ void logv(const char *format, va_list ap) { log_warn_regex_recusion_guard = true; - if (log_warn_regexes.empty() && log_expect_log.empty()) + if (log_warn_regexes.empty() && log_expect_log.empty() && log_expect_prefix_log.empty()) { linebuffer.clear(); } @@ -191,9 +192,9 @@ void logv(const char *format, va_list ap) if (std::regex_search(linebuffer, re)) log_warning("Found log message matching -W regex:\n%s", str.c_str()); - for (auto &item : log_expect_log) - if (std::regex_search(linebuffer, item.second.pattern)) - item.second.current_count++; + for (auto &[_, item] : log_expect_log) + if (std::regex_search(linebuffer, item.pattern)) + item.current_count++; linebuffer.clear(); } @@ -266,9 +267,15 @@ static void logv_warning_with_prefix(const char *prefix, log_error("%s", message.c_str()); bool warning_match = false; - for (auto &item : log_expect_warning) - if (std::regex_search(message, item.second.pattern)) { - item.second.current_count++; + for (auto &[_, item] : log_expect_warning) + if (std::regex_search(message, item.pattern)) { + item.current_count++; + warning_match = true; + } + + for (auto &[_, item] : log_expect_prefix_warning) + if (std::regex_search(string(prefix) + message, item.pattern)) { + item.current_count++; warning_match = true; } @@ -355,9 +362,13 @@ static void logv_error_with_prefix(const char *prefix, log_make_debug = bak_log_make_debug; - for (auto &item : log_expect_error) - if (std::regex_search(log_last_error, item.second.pattern)) - item.second.current_count++; + for (auto &[_, item] : log_expect_error) + if (std::regex_search(log_last_error, item.pattern)) + item.current_count++; + + for (auto &[_, item] : log_expect_prefix_error) + if (std::regex_search(string(prefix) + string(log_last_error), item.pattern)) + item.current_count++; log_check_expected(); @@ -705,38 +716,39 @@ void log_check_expected() // copy out all of the expected logs so that they cannot be re-checked // or match against themselves dict expect_log, expect_warning, expect_error; + dict expect_prefix_log, expect_prefix_warning, expect_prefix_error; std::swap(expect_warning, log_expect_warning); std::swap(expect_log, log_expect_log); std::swap(expect_error, log_expect_error); + std::swap(expect_prefix_warning, log_expect_prefix_warning); + std::swap(expect_prefix_log, log_expect_prefix_log); + std::swap(expect_prefix_error, log_expect_prefix_error); - for (auto &item : expect_warning) { - if (item.second.current_count == 0) { + auto check = [&](const std::string kind, std::string pattern, LogExpectedItem item) { + if (item.current_count == 0) { log_warn_regexes.clear(); - log_error("Expected warning pattern '%s' not found !\n", item.first.c_str()); + log_error("Expected %s pattern '%s' not found !\n", kind.c_str(), pattern.c_str()); } - if (item.second.current_count != item.second.expected_count) { + if (item.current_count != item.expected_count) { log_warn_regexes.clear(); - log_error("Expected warning pattern '%s' found %d time(s), instead of %d time(s) !\n", - item.first.c_str(), item.second.current_count, item.second.expected_count); + log_error("Expected %s pattern '%s' found %d time(s), instead of %d time(s) !\n", + kind.c_str(), pattern.c_str(), item.current_count, item.expected_count); } - } + }; - for (auto &item : expect_log) { - if (item.second.current_count == 0) { - log_warn_regexes.clear(); - log_error("Expected log pattern '%s' not found !\n", item.first.c_str()); - } - if (item.second.current_count != item.second.expected_count) { - log_warn_regexes.clear(); - log_error("Expected log pattern '%s' found %d time(s), instead of %d time(s) !\n", - item.first.c_str(), item.second.current_count, item.second.expected_count); - } - } + for (auto &[pattern, item] : expect_warning) + check("warning", pattern, item); + for (auto &[pattern, item] : expect_prefix_warning) + check("prefixed warning", pattern, item); + for (auto &[pattern, item] : expect_log) + check("log", pattern, item); + for (auto &[pattern, item] : expect_prefix_log) + check("prefixed log", pattern, item); - for (auto &item : expect_error) - if (item.second.current_count == item.second.expected_count) { + auto check_err = [&](const std::string kind, std::string pattern, LogExpectedItem item) { + if (item.current_count == item.expected_count) { log_warn_regexes.clear(); - log("Expected error pattern '%s' found !!!\n", item.first.c_str()); + log("Expected %s pattern '%s' found !!!\n", kind.c_str(), pattern.c_str()); yosys_shutdown(); #ifdef EMSCRIPTEN throw 0; @@ -747,8 +759,13 @@ void log_check_expected() #endif } else { log_warn_regexes.clear(); - log_error("Expected error pattern '%s' not found !\n", item.first.c_str()); + log_error("Expected %s pattern '%s' not found !\n", kind.c_str(), pattern.c_str()); } + }; + for (auto &[pattern, item] : expect_error) + check_err("error", pattern, item); + for (auto &[pattern, item] : expect_prefix_error) + check_err("prefixed error", pattern, item); } // --------------------------------------------------- diff --git a/kernel/log.h b/kernel/log.h index e26ef072c..48997d250 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -201,6 +201,7 @@ struct LogExpectedItem }; extern dict log_expect_log, log_expect_warning, log_expect_error; +extern dict log_expect_prefix_log, log_expect_prefix_warning, log_expect_prefix_error; void log_check_expected(); const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 646ffcb35..0250346d1 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -952,10 +952,6 @@ RTLIL::Design::~Design() delete pr.second; for (auto n : bindings_) delete n; - for (auto n : verilog_packages) - delete n; - for (auto n : verilog_globals) - delete n; #ifdef WITH_PYTHON RTLIL::Design::get_all_designs()->erase(hashidx_); #endif @@ -4287,9 +4283,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) return; - if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux)) { + if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux) || type == ID($bwmux) || type == ID($bweqx)) { parameters[ID::WIDTH] = GetSize(connections_[ID::Y]); - if (type != ID($buf) && type != ID($mux)) + if (type.in(ID($pmux), ID($bmux))) parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); check(); return; @@ -4344,7 +4340,7 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) parameters[ID::B_WIDTH] = GetSize(connections_[ID::B]); } - if (connections_.count(ID::Y)) + if (connections_.count(ID::Y) && type != ID($concat)) parameters[ID::Y_WIDTH] = GetSize(connections_[ID::Y]); if (connections_.count(ID::Q)) @@ -5710,16 +5706,10 @@ static void sigspec_parse_split(std::vector &tokens, const std::str tokens.push_back(text.substr(start)); } -static int sigspec_parse_get_dummy_line_num() -{ - return 0; -} - bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str) { cover("kernel.rtlil.sigspec.parse"); - AST::current_filename = "input"; std::vector tokens; sigspec_parse_split(tokens, str, ','); @@ -5735,12 +5725,11 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri if (('0' <= netname[0] && netname[0] <= '9') || netname[0] == '\'') { cover("kernel.rtlil.sigspec.parse.const"); - AST::get_line_num = sigspec_parse_get_dummy_line_num; - AST::AstNode *ast = VERILOG_FRONTEND::const2ast(netname); - if (ast == NULL) + VERILOG_FRONTEND::ConstParser p{Location()}; + auto ast = p.const2ast(netname); + if (ast == nullptr) return false; sig.append(RTLIL::Const(ast->bits)); - delete ast; continue; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index d53bb3129..e0de79ea9 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1334,7 +1334,7 @@ struct RTLIL::Design dict modules_; std::vector bindings_; - std::vector verilog_packages, verilog_globals; + std::vector> verilog_packages, verilog_globals; std::unique_ptr verilog_defines; std::vector selection_stack; diff --git a/kernel/satgen.h b/kernel/satgen.h index 2c8cbda13..996eaf9fb 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -64,7 +64,7 @@ struct ezSatPtr : public std::unique_ptr { struct SatGen { ezSAT *ez; - SigMap *sigmap; + const SigMap *sigmap; std::string prefix; SigPool initial_state; std::map asserts_a, asserts_en; @@ -75,12 +75,12 @@ struct SatGen bool model_undef; bool def_formal = false; - SatGen(ezSAT *ez, SigMap *sigmap, std::string prefix = std::string()) : + SatGen(ezSAT *ez, const SigMap *sigmap, std::string prefix = std::string()) : ez(ez), sigmap(sigmap), prefix(prefix), ignore_div_by_zero(false), model_undef(false) { } - void setContext(SigMap *sigmap, std::string prefix = std::string()) + void setContext(const SigMap *sigmap, std::string prefix = std::string()) { this->sigmap = sigmap; this->prefix = prefix; diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 3b0d7b40d..7962eaa70 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -237,6 +237,42 @@ using sort_by_name_id_guard = typename std::enable_if class SigSet> : public SigSet::type>> {}; +struct SigMapView +{ + mfp database; + + // Modify bit to its representative + void apply(RTLIL::SigBit &bit) const + { + bit = database.find(bit); + } + + void apply(RTLIL::SigSpec &sig) const + { + for (auto &bit : sig) + apply(bit); + } + + RTLIL::SigBit operator()(RTLIL::SigBit bit) const + { + apply(bit); + return bit; + } + + RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const + { + apply(sig); + return sig; + } + + RTLIL::SigSpec operator()(RTLIL::Wire *wire) const + { + SigSpec sig(wire); + apply(sig); + return sig; + } +}; + /** * SigMap wraps a union-find "database" * to map SigBits of a module to canonical representative SigBits. @@ -244,10 +280,8 @@ class SigSet> : public SigSet database; - SigMap(RTLIL::Module *module = NULL) { if (module != NULL) @@ -320,37 +354,6 @@ struct SigMap inline void add(Wire *wire) { return add(RTLIL::SigSpec(wire)); } - // Modify bit to its representative - void apply(RTLIL::SigBit &bit) const - { - bit = database.find(bit); - } - - void apply(RTLIL::SigSpec &sig) const - { - for (auto &bit : sig) - apply(bit); - } - - RTLIL::SigBit operator()(RTLIL::SigBit bit) const - { - apply(bit); - return bit; - } - - RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const - { - apply(sig); - return sig; - } - - RTLIL::SigSpec operator()(RTLIL::Wire *wire) const - { - SigSpec sig(wire); - apply(sig); - return sig; - } - // All non-const bits RTLIL::SigSpec allbits() const { @@ -362,6 +365,107 @@ struct SigMap } }; +/** + * SiValgMap wraps a union-find "database" to map SigBits of a module to + * canonical representative SigBits plus some optional Val value associated with the bits. + * Val has a commutative, associative, idempotent operator|=, a default constructor + * which constructs an identity element, and a copy constructor. + * SigBits that are connected share a set in the underlying database; + * the associated value is the "sum" of all the values associated with the contributing bits. + * If any of the SigBits in a set are a constant, the canonical SigBit is a constant. + */ +template +struct SigValMap final : public SigMapView +{ + dict values; + + void swap(SigValMap &other) + { + database.swap(other.database); + values.swap(other.values); + } + + void clear() + { + database.clear(); + values.clear(); + } + + // Rebuild SigMap for all connections in module + void set(RTLIL::Module *module) + { + int bitcount = 0; + for (auto &it : module->connections()) + bitcount += it.first.size(); + + database.clear(); + values.clear(); + database.reserve(bitcount); + + for (auto &it : module->connections()) + add(it.first, it.second); + } + + // Add connections from "from" to "to", bit-by-bit. + void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to) + { + log_assert(GetSize(from) == GetSize(to)); + + for (int i = 0; i < GetSize(from); i++) + { + int bfi = database.lookup(from[i]); + int bti = database.lookup(to[i]); + if (bfi == bti) { + continue; + } + + const RTLIL::SigBit &bf = database[bfi]; + const RTLIL::SigBit &bt = database[bti]; + if (bf.wire == nullptr) { + // bf is constant so make it the canonical representative. + database.imerge(bti, bfi); + merge_value(bt, bf); + } else { + // Make bt the canonical representative. + database.imerge(bfi, bti); + merge_value(bf, bt); + } + } + } + + void addVal(const RTLIL::SigBit &bit, const Val &val) + { + values[database.find(bit)] |= val; + } + + void addVal(const RTLIL::SigSpec &sig, const Val &val) + { + for (const auto &bit : sig) + addVal(bit, val); + } + + Val apply_and_get_value(RTLIL::SigBit &bit) const + { + bit = database.find(bit); + auto it = values.find(bit); + return it == values.end() ? Val() : it->second; + } + +private: + void merge_value(const RTLIL::SigBit &from, const RTLIL::SigBit &to) + { + auto it = values.find(from); + if (it == values.end()) { + return; + } + // values[to] could resize the underlying `entries` so + // finish using `it` first. + Val v = it->second; + values.erase(it); + values[to] |= v; + } +}; + YOSYS_NAMESPACE_END #endif /* SIGTOOLS_H */ diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index ecc8ce623..bc92e7869 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -20,6 +20,7 @@ #ifndef YOSYS_COMMON_H #define YOSYS_COMMON_H +#include #include #include #include diff --git a/misc/create_vcxsrc.sh b/misc/create_vcxsrc.sh index d7b516fd6..d6800d15e 100644 --- a/misc/create_vcxsrc.sh +++ b/misc/create_vcxsrc.sh @@ -20,6 +20,13 @@ mv zlib-1.2.11/* "$vcxsrc"/yosys/libs/zlib/. rm -rf zlib-1.2.11 pushd "$vcxsrc"/yosys ls libs/zlib/*.c | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' >> ../../srcfiles.txt + +if [ -f "/usr/include/FlexLexer.h" ] ; then + mkdir -p libs/flex + cp /usr/include/FlexLexer.h libs/flex/FlexLexer.h + ls libs/flex/*.h >> ../../srcfiles.txt +fi + popd { n=$(grep -B999 '' "$vcxsrc"/YosysVS/YosysVS.vcxproj | wc -l) @@ -31,6 +38,9 @@ popd } > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new sed -i 's,,\n stdcpp17\n /Zc:__cplusplus %(AdditionalOptions),g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new +if [ -f "/usr/include/FlexLexer.h" ] ; then + sed -i 's,,;..\\yosys\\libs\\flex,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new +fi mv "$vcxsrc"/YosysVS/YosysVS.vcxproj.new "$vcxsrc"/YosysVS/YosysVS.vcxproj mkdir -p "$vcxsrc"/yosys diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d4eb22ca5..907a16fe0 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -373,6 +373,12 @@ struct AbstractPass : public Pass { log(" abstractions performed by either mode. This option is not supported in\n"); log(" the -init mode.\n"); log("\n"); + log(" -initstates \n"); + log(" Perform conditional abstraction for the first time steps. See the\n"); + log(" description of the -state and -value modes for details on how the\n"); + log(" condition affects the abstractions performed by either mode. This option\n"); + log(" is not supported in the -init mode.\n"); + log("\n"); log(" -slice :\n"); log(" -slice \n"); log(" -rtlilslice :\n"); @@ -402,8 +408,10 @@ struct AbstractPass : public Pass { Always = -1, ActiveLow = false, // ensuring we can use bool(enable) ActiveHigh = true, + Initstates = 2, }; Enable enable = Enable::Always; + int initstates = 0; std::string enable_name; std::vector slices; for (argidx = 1; argidx < args.size(); argidx++) @@ -435,6 +443,13 @@ struct AbstractPass : public Pass { enable = Enable::ActiveLow; continue; } + if (arg == "-initstates" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); + initstates = atoi(args[++argidx].c_str()); + enable = Enable::Initstates; + continue; + } if (arg == "-slice" && argidx + 1 < args.size()) { slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]); continue; @@ -451,22 +466,50 @@ struct AbstractPass : public Pass { if (mode == Mode::Initial) log_cmd_error("Conditional initial value abstraction is not supported\n"); - if (enable_name.empty()) - log_cmd_error("Unspecified enable wire\n"); + switch (enable) { + case Enable::Always: + log_assert(false); + case Enable::ActiveLow: + case Enable::ActiveHigh: { + if (enable_name.empty()) + log_cmd_error("Unspecified enable wire\n"); + } break; + case Enable::Initstates: { + if (initstates <= 0) + log_cmd_error("Number of initial time steps must be positive\n"); + } break; + } } unsigned int changed = 0; if ((mode == State) || (mode == Value)) { for (auto mod : design->selected_modules()) { - EnableLogic enable_logic = { State::S1, true }; - if (enable != Enable::Always) { - Wire *enable_wire = mod->wire("\\" + enable_name); - if (!enable_wire) - log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); - if (GetSize(enable_wire) != 1) - log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", - enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); - enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + EnableLogic enable_logic; + + switch (enable) { + case Enable::Always: { + enable_logic = { State::S1, true }; + } break; + case Enable::ActiveLow: + case Enable::ActiveHigh: { + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (GetSize(enable_wire) != 1) + log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", + enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); + enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + } break; + case Enable::Initstates: { + SigBit in_init_states = mod->Initstate(NEW_ID); + for (int i = 1; i < initstates; i++) { + Wire *in_init_states_q = mod->addWire(NEW_ID); + mod->addFf(NEW_ID, in_init_states, in_init_states_q); + in_init_states_q->attributes[ID::init] = State::S1; + in_init_states = in_init_states_q; + } + enable_logic = { in_init_states, true }; + } break; } if (mode == State) changed += abstract_state(mod, enable_logic, slices); diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 8bbcb8da0..3017630a6 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -195,16 +195,23 @@ struct CheckPass : public Pass { // in port widths are those for us to check. if (!cell->type.in( ID($add), ID($sub), - ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) + ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), + ID($pmux), ID($bmux))) return false; int in_widths = 0, out_widths = 0; - for (auto &conn : cell->connections()) { - if (cell->input(conn.first)) - in_widths += conn.second.size(); - if (cell->output(conn.first)) - out_widths += conn.second.size(); + if (cell->type.in(ID($pmux), ID($bmux))) { + // We're skipping inputs A and B, since each of their bits contributes only one edge + in_widths = GetSize(cell->getPort(ID::S)); + out_widths = GetSize(cell->getPort(ID::Y)); + } else { + for (auto &conn : cell->connections()) { + if (cell->input(conn.first)) + in_widths += conn.second.size(); + if (cell->output(conn.first)) + out_widths += conn.second.size(); + } } const int threshold = 1024; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index a2ae45ef3..59cd39c98 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -368,14 +368,8 @@ struct DesignPass : public Pass { if (reset_mode || reset_vlog_mode || !load_name.empty() || push_mode || pop_mode) { - for (auto node : design->verilog_packages) - delete node; design->verilog_packages.clear(); - - for (auto node : design->verilog_globals) - delete node; design->verilog_globals.clear(); - design->verilog_defines->clear(); } diff --git a/passes/cmds/logger.cc b/passes/cmds/logger.cc index 276810201..a9762f630 100644 --- a/passes/cmds/logger.cc +++ b/passes/cmds/logger.cc @@ -65,6 +65,8 @@ struct LoggerPass : public Pass { log(" -expect \n"); log(" expect log, warning or error to appear. matched errors will terminate\n"); log(" with exit code 0.\n"); + log(" Types prefix-log, prefix-warning and prefix-error match the entire\n"); + log(" logged string, including filename if present.\n"); log("\n"); log(" -expect-no-warnings\n"); log(" gives error in case there is at least one warning that is not expected.\n"); @@ -156,26 +158,33 @@ struct LoggerPass : public Pass { } if (args[argidx] == "-expect" && argidx+3 < args.size()) { std::string type = args[++argidx]; - if (type!="error" && type!="warning" && type!="log") + if (type!="error" && type!="warning" && type!="log" + && type!="prefix-error" && type!="prefix-warning" && type!="prefix-log") log_cmd_error("Expect command require type to be 'log', 'warning' or 'error' !\n"); - if (type=="error" && log_expect_error.size()>0) + if ((type=="error" || type=="prefix-error") && log_expect_error.size()>0) log_cmd_error("Only single error message can be expected !\n"); std::string pattern = args[++argidx]; - if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2); + if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2); int count = atoi(args[++argidx].c_str()); if (count<=0) log_cmd_error("Number of expected messages must be higher then 0 !\n"); - if (type=="error" && count!=1) + if ((type=="error" || type=="prefix-error") && count!=1) log_cmd_error("Expected error message occurrences must be 1 !\n"); log("Added regex '%s' to expected %s messages list.\n", pattern.c_str(), type.c_str()); try { if (type == "error") log_expect_error[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); + else if (type == "prefix-error") + log_expect_prefix_error[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); else if (type == "warning") log_expect_warning[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); + else if (type == "prefix-warning") + log_expect_prefix_warning[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); else if (type == "log") log_expect_log[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); + else if (type == "prefix-log") + log_expect_prefix_log[pattern] = LogExpectedItem(YS_REGEX_COMPILE(pattern), count); else log_abort(); } catch (const std::regex_error& e) { diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index e0586ec7e..167997dc0 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -67,7 +67,7 @@ static std::string derive_name_from_src(const std::string &src, int counter) return stringf("\\%s$%d", src_base.c_str(), counter); } -static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell, string suffix) +static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell, string suffix, bool move_to_cell) { // Find output const SigSpec *output = nullptr; @@ -93,13 +93,21 @@ static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell, strin name += chunk.wire->name.str(); if (chunk.wire->width != chunk.width) { - name += "["; - if (chunk.width != 1) - name += std::to_string(chunk.offset + chunk.width) + ":"; - name += std::to_string(chunk.offset) + "]"; + int lhs = chunk.wire->to_hdl_index(chunk.offset + chunk.width - 1); + int rhs = chunk.wire->to_hdl_index(chunk.offset); + + if (lhs != rhs) + name += stringf("[%d:%d]", lhs, rhs); + else + name += stringf("[%d]", lhs); } } + RTLIL::Wire *wire; + + if (move_to_cell && (!(wire = cell->module->wire(name)) || !(wire->port_input || wire->port_output))) + return name; + if (suffix.empty()) { suffix = cell->type.str(); } @@ -232,13 +240,17 @@ struct RenamePass : public Pass { log("cells with private names.\n"); log("\n"); log("\n"); - log(" rename -wire [selection] [-suffix ]\n"); + log(" rename -wire [selection] [-move-to-cell] [-suffix ]\n"); log("\n"); log("Assign auto-generated names based on the wires they drive to all selected\n"); log("cells with private names. Ignores cells driving privatly named wires.\n"); log("By default, the cell is named after the wire with the cell type as suffix.\n"); log("The -suffix option can be used to set the suffix to the given string instead.\n"); log("\n"); + log("The -move-to-cell option can be used to name the cell after the wire without\n"); + log("any suffix. If this would lead to conflicts, the suffix is added to the wire\n"); + log("instead. For cells driving ports, the -move-to-cell option is ignored.\n"); + log("\n"); log("\n"); log(" rename -enumerate [-pattern ] [selection]\n"); log("\n"); @@ -286,6 +298,7 @@ struct RenamePass : public Pass { std::string cell_suffix = ""; bool flag_src = false; bool flag_wire = false; + bool flag_move_to_cell = false; bool flag_enumerate = false; bool flag_witness = false; bool flag_hide = false; @@ -345,6 +358,10 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-move-to-cell" && flag_wire && !flag_move_to_cell) { + flag_move_to_cell = true; + continue; + } if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { int pos = args[++argidx].find('%'); pattern_prefix = args[argidx].substr(0, pos); @@ -396,9 +413,26 @@ struct RenamePass : public Pass { dict new_cell_names; for (auto cell : module->selected_cells()) if (cell->name[0] == '$') - new_cell_names[cell] = derive_name_from_cell_output_wire(cell, cell_suffix); - for (auto &it : new_cell_names) - module->rename(it.first, it.second); + new_cell_names[cell] = derive_name_from_cell_output_wire(cell, cell_suffix, flag_move_to_cell); + for (auto &[cell, new_name] : new_cell_names) { + if (flag_move_to_cell) { + RTLIL::Wire *found_wire = module->wire(new_name); + if (found_wire) { + std::string wire_suffix = cell_suffix; + if (wire_suffix.empty()) { + for (auto const &[port, _] : cell->connections()) { + if (cell->output(port)) { + wire_suffix += stringf("%s.%s", cell->type.c_str(), port.c_str() + 1); + break; + } + } + } + IdString new_wire_name = found_wire->name.str() + wire_suffix; + module->rename(found_wire, new_wire_name); + } + } + module->rename(cell, new_name); + } } } else @@ -564,6 +598,8 @@ struct RenamePass : public Pass { for (auto &it : new_cell_names) module->rename(it.first, it.second); + + module->fixup_ports(); } } else diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index af7023bdd..07b1e5228 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -19,13 +19,14 @@ #include -#include "kernel/yosys.h" #include "kernel/celltypes.h" -#include "passes/techmap/libparse.h" #include "kernel/cost.h" #include "kernel/gzip.h" -#include "libs/json11/json11.hpp" #include "kernel/log_help.h" +#include "kernel/yosys.h" +#include "libs/json11/json11.hpp" +#include "passes/techmap/libparse.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -33,139 +34,311 @@ PRIVATE_NAMESPACE_BEGIN struct cell_area_t { double area; bool is_sequential; + vector single_parameter_area; + vector> double_parameter_area; + vector parameter_names; }; -struct statdata_t -{ - #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ - X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \ - X(num_processes) +struct statdata_t { +#define STAT_INT_MEMBERS \ + X(num_wires) \ + X(num_wire_bits) \ + X(num_pub_wires) X(num_pub_wire_bits) X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) - #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) X(sequential_area) +#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) X(sequential_area) - #define X(_name) unsigned int _name; +#define X(_name) unsigned int _name; STAT_INT_MEMBERS - #undef X +#undef X +#define X(_name) unsigned int local_##_name; + STAT_INT_MEMBERS +#undef X double area = 0; double sequential_area = 0; + double local_area = 0; + double local_sequential_area = 0; + double submodule_area = 0; + int num_submodules = 0; + std::map num_submodules_by_type; + std::map submodules_area_by_type; + + std::map local_num_cells_by_type; + std::map local_area_cells_by_type; + std::map local_seq_area_cells_by_type; string tech; std::map num_cells_by_type; + std::map area_cells_by_type; + std::map seq_area_cells_by_type; std::set unknown_cell_area; statdata_t operator+(const statdata_t &other) const { statdata_t sum = other; - #define X(_name) sum._name += _name; +#define X(_name) sum._name += _name; STAT_NUMERIC_MEMBERS - #undef X +#undef X for (auto &it : num_cells_by_type) sum.num_cells_by_type[it.first] += it.second; return sum; } - statdata_t operator*(unsigned int other) const { statdata_t sum = *this; - #define X(_name) sum._name *= other; +#define X(_name) sum._name *= other; STAT_NUMERIC_MEMBERS - #undef X +#undef X for (auto &it : sum.num_cells_by_type) it.second *= other; return sum; } + statdata_t add(const statdata_t &other) + { +#define X(_name) _name += other._name; + STAT_NUMERIC_MEMBERS +#undef X + for (auto &it : other.num_cells_by_type) { + if (num_cells_by_type.count(it.first)) + num_cells_by_type[it.first] += it.second; + else + num_cells_by_type[it.first] = it.second; + } + for (auto &it : other.submodules_area_by_type) { + if (submodules_area_by_type.count(it.first)) + submodules_area_by_type[it.first] += it.second; + else + submodules_area_by_type[it.first] = it.second; + } + for (auto &it : other.area_cells_by_type) { + if (area_cells_by_type.count(it.first)) + area_cells_by_type[it.first] += it.second; + else + area_cells_by_type[it.first] = it.second; + } + for (auto &it : other.seq_area_cells_by_type) { + if (seq_area_cells_by_type.count(it.first)) + seq_area_cells_by_type[it.first] += it.second; + else + seq_area_cells_by_type[it.first] = it.second; + } + unknown_cell_area.insert(other.unknown_cell_area.begin(), other.unknown_cell_area.end()); + return *this; + } statdata_t() { - #define X(_name) _name = 0; +#define X(_name) _name = 0; STAT_NUMERIC_MEMBERS - #undef X +#undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict &cell_area, string techname) + statdata_t(cell_area_t &cell_data, string techname) + { + tech = techname; + area = cell_data.area; + if (cell_data.is_sequential) { + sequential_area = cell_data.area; + } + } + + statdata_t(const RTLIL::Design *design, const RTLIL::Module *mod, bool width_mode, dict &cell_area, string techname) { tech = techname; - #define X(_name) _name = 0; +#define X(_name) _name = 0; STAT_NUMERIC_MEMBERS - #undef X +#undef X +#define X(_name) local_##_name = 0; + STAT_NUMERIC_MEMBERS +#undef X + // additional_cell_area - for (auto wire : mod->selected_wires()) - { + for (auto wire : mod->selected_wires()) { if (wire->port_input || wire->port_output) { num_ports++; + local_num_ports++; num_port_bits += wire->width; + local_num_port_bits += wire->width; } if (wire->name.isPublic()) { num_pub_wires++; + local_num_pub_wires++; num_pub_wire_bits += wire->width; + local_num_pub_wire_bits += wire->width; } num_wires++; + local_num_wires++; num_wire_bits += wire->width; + local_num_wire_bits += wire->width; } for (auto &it : mod->memories) { if (!design->selected(mod, it.second)) continue; num_memories++; + local_num_memories++; num_memory_bits += it.second->width * it.second->size; + local_num_memory_bits += it.second->width * it.second->size; } - - for (auto cell : mod->selected_cells()) - { + for (auto cell : mod->selected_cells()) { RTLIL::IdString cell_type = cell->type; - - if (width_mode) - { - if (cell_type.in(ID($not), ID($pos), ID($neg), - ID($logic_not), ID($logic_and), ID($logic_or), - ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), - ID($lut), ID($and), ID($or), ID($xor), ID($xnor), - ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), - ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), - ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) { + if (width_mode) { + if (cell_type.in(ID($not), ID($pos), ID($neg), ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_and), + ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($lut), ID($and), ID($or), + ID($xor), ID($xnor), ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), ID($lt), + ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), ID($add), ID($sub), ID($mul), + ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) { int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0; int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0; int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0; cell_type = stringf("%s_%d", cell_type.c_str(), max({width_a, width_b, width_y})); - } - else if (cell_type.in(ID($mux), ID($pmux))) + } else if (cell_type.in(ID($mux))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y))); - else if (cell_type == ID($bmux)) - cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S))); + else if (cell_type.in(ID($bmux), ID($pmux))) + cell_type = + stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S))); else if (cell_type == ID($demux)) - cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S))); - else if (cell_type.in( - ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), - ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), - ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr))) + cell_type = + stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S))); + else if (cell_type.in(ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), + ID($sdff), ID($sdffe), ID($sdffce), ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), + ID($dlatchsr))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Q))); } if (!cell_area.empty()) { + // check if cell_area provides a area calculator + if (cell_area.count(cell->type)) { + cell_area_t cell_data = cell_area.at(cell->type); + if (cell_data.single_parameter_area.size() > 0) { + // assume that we just take the max of the A,B,Y ports + + int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0; + int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0; + int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0; + int width_q = cell->hasPort(ID::Q) ? GetSize(cell->getPort(ID::Q)) : 0; + int max_width = max({width_a, width_b, width_y, width_q}); + if (!cell_area.count(cell_type)) { + cell_area[cell_type] = cell_data; + } + if (cell_data.single_parameter_area.size() > max_width - 1u) { + cell_area.at(cell_type).area = cell_data.single_parameter_area.at(max_width - 1); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + + } else { + log_warning("too small single_parameter_area %s width: %d size: %d\n", cell_type.c_str(), max_width, + (int)cell_data.single_parameter_area.size()); + cell_area.at(cell_type).area = cell_data.single_parameter_area.back(); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } + vector widths; + if (cell_data.parameter_names.size() > 0) { + for (auto &it : cell_data.parameter_names) { + RTLIL::IdString port_name; + if (it == "A") { + port_name = ID::A; + } else if (it == "B") { + port_name = ID::B; + } else if (it == "Y") { + port_name = ID::Y; + } else if (it == "Q") { + port_name = ID::Q; + } else if (it == "S") { + port_name = ID::S; + } else { + port_name = ID(it); + } + if (cell->hasPort(port_name)) { + int width = GetSize(cell->getPort(port_name)); + widths.push_back(width); + } else { + widths.push_back(0); + } + } + } + + if (cell_data.double_parameter_area.size() > 0) { + if (!cell_area.count(cell_type)) { + cell_area[cell_type] = cell_data; + } + if (widths.size() == 2) { + unsigned int width_a = widths.at(0); + unsigned int width_b = widths.at(1); + if (width_a > 0 && width_b > 0) { + if (cell_data.double_parameter_area.size() > width_a - 1 && + cell_data.double_parameter_area.at(width_a - 1).size() > width_b - 1) { + cell_area.at(cell_type).area = + cell_data.double_parameter_area.at(width_a - 1).at(width_b - 1); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } else { + log_warning("too small double_parameter_area %s, width_a: %d, width_b: %d, size_a: %d, size_b: %d\n", cell_type.c_str(), + width_a, width_b, (int)cell_data.double_parameter_area.size(), + (int)cell_data.double_parameter_area.at(width_a - 1).size()); + cell_area.at(cell_type).area = cell_data.double_parameter_area.back().back(); + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } else { + cell_area.at(cell_type).area = cell_data.area; + cell_area.at(cell_type).is_sequential = cell_data.is_sequential; + } + } else { + log_error("double_parameter_area for %s has %d parameters, but only 2 are expected.\n", cell_type.c_str(), + (int)cell_data.double_parameter_area.size()); + } + } + } + if (cell_area.count(cell_type)) { cell_area_t cell_data = cell_area.at(cell_type); if (cell_data.is_sequential) { sequential_area += cell_data.area; + local_sequential_area += cell_data.area; } area += cell_data.area; - } - else { - unknown_cell_area.insert(cell_type); - } - } + num_cells++; + num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] += cell_data.area; + seq_area_cells_by_type[cell_type] += cell_data.is_sequential ? cell_data.area : 0; + local_area_cells_by_type[cell_type] += cell_data.area; + local_seq_area_cells_by_type[cell_type] += cell_data.is_sequential ? cell_data.area : 0; + local_area += cell_data.area; + local_num_cells++; + local_num_cells_by_type[cell_type]++; - num_cells++; - num_cells_by_type[cell_type]++; + } else { + unknown_cell_area.insert(cell_type); + num_cells++; + num_cells_by_type[cell_type]++; + local_num_cells++; + local_num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] = 0; + seq_area_cells_by_type[cell_type] = 0; + local_area_cells_by_type[cell_type] = 0; + local_seq_area_cells_by_type[cell_type] = 0; + } + } else { + num_cells++; + num_cells_by_type[cell_type]++; + area_cells_by_type[cell_type] = 0; + seq_area_cells_by_type[cell_type] = 0; + local_num_cells++; + local_num_cells_by_type[cell_type]++; + local_area_cells_by_type[cell_type] = 0; + local_seq_area_cells_by_type[cell_type] = 0; + } } for (auto &it : mod->processes) { if (!design->selected(mod, it.second)) continue; num_processes++; + local_num_processes++; } + RTLIL::IdString cell_name = mod->name; + auto s = cell_name.str(); } unsigned int estimate_xilinx_lc() @@ -238,22 +411,132 @@ struct statdata_t return tran_cnt; } - void log_data(RTLIL::IdString mod_name, bool top_mod) + /* + format a floating point value to a of 8 characters, with at most 7 digits or scientific notation + uses - to mark zero or very small values + */ + std::string f_val(double value) { - log(" Number of wires: %6u\n", num_wires); - log(" Number of wire bits: %6u\n", num_wire_bits); - log(" Number of public wires: %6u\n", num_pub_wires); - log(" Number of public wire bits: %6u\n", num_pub_wire_bits); - log(" Number of ports: %6u\n", num_ports); - log(" Number of port bits: %6u\n", num_port_bits); - log(" Number of memories: %6u\n", num_memories); - log(" Number of memory bits: %6u\n", num_memory_bits); - log(" Number of processes: %6u\n", num_processes); - log(" Number of cells: %6u\n", num_cells); - for (auto &it : num_cells_by_type) - if (it.second) - log(" %-26s %6u\n", log_id(it.first), it.second); + if (std::abs(value) < 1e-12) + return " -"; + char buf[16]; + + int len = snprintf(buf, sizeof(buf), "%.3f", value); + + while (len > 0 && buf[len - 1] == '0') + --len; + if (len > 0 && buf[len - 1] == '.') + --len; + buf[len] = '\0'; + + if (len <= 7) { + return std::string(8 - len, ' ') + std::string(buf); + } + + // use scientific notation, this should always fit in 8 characters + snprintf(buf, sizeof(buf), "%8.3G", value); + + return std::string(buf); + } + + void print_log_line(const std::string &name, unsigned int count_local, double area_local, unsigned int count_global, double area_global, + int spacer = 0, bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + const std::string indent(2 * spacer, ' '); + + std::string count_local_str = f_val(static_cast(count_local)); + std::string count_global_str = f_val(static_cast(count_global)); + std::string area_local_str = f_val(area_local); + std::string area_global_str = f_val(area_global); + + if (print_area) { + if (print_hierarchical) { + log(" %s %s %s %s %s%s\n", count_global_str.c_str(), area_global_str.c_str(), count_local_str.c_str(), + area_local_str.c_str(), indent.c_str(), name.c_str()); + } else if (print_global_only) { + log(" %s %s %s%s\n", count_global_str.c_str(), area_global_str.c_str(), indent.c_str(), name.c_str()); + } else { + if (count_local > 0) + log(" %s %s %s%s\n", count_local_str.c_str(), area_local_str.c_str(), indent.c_str(), name.c_str()); + } + } else { + if (print_hierarchical) { + log(" %s %s %s%s\n", count_global_str.c_str(), count_local_str.c_str(), indent.c_str(), name.c_str()); + } else if (print_global_only) { + log(" %s %s%s\n", count_global_str.c_str(), indent.c_str(), name.c_str()); + } else { + if (count_local > 0) + log(" %s %s%s\n", count_local_str.c_str(), indent.c_str(), name.c_str()); + } + } + } + + void print_log_header(bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + if (print_area) { + if (print_hierarchical) { + log(" %8s-%8s-%8s-%8s-%s\n", "+", "--------", "--------", "--------", "Count including submodules."); + log(" %8s %8s-%8s-%8s-%s\n", "|", "+", "--------", "--------", "Area including submodules."); + log(" %8s %8s %8s-%8s-%s\n", "|", "|", "+", "--------", "Local count, excluding submodules."); + log(" %8s %8s %8s %8s-%s\n", "|", "|", "|", "+", "Local area, excluding submodules."); + log(" %8s %8s %8s %8s \n", "|", "|", "|", "|"); + } else if (print_global_only) { + log(" %8s-%8s-%s\n", "+", "--------", "Count including submodules."); + log(" %8s %8s-%s\n", "|", "+", "Area including submodules."); + log(" %8s %8s \n", "|", "|"); + } else { + log(" %8s-%8s-%s\n", "+", "--------", "Local Count, excluding submodules."); + log(" %8s %8s-%s\n", "|", "+", "Local Area, excluding submodules."); + log(" %8s %8s \n", "|", "|"); + } + } else { + if (print_hierarchical) { + log(" %8s-%8s-%8s-%s\n", "+", "--------", "--------", "Count including submodules."); + log(" %8s %8s-%8s-%s\n", "|", "+", "--------", "Local count, excluding submodules."); + log(" %8s %8s \n", "|", "|"); + } else if (print_global_only) { + log(" %8s-%8s-%s\n", "+", "--------", "Count including submodules."); + log(" %8s \n", "|"); + } else { + log(" %8s-%8s-%s\n", "+", "--------", "Local Count, excluding submodules."); + log(" %8s \n", "|"); + } + } + } + + void log_data(RTLIL::IdString mod_name, bool top_mod, bool print_area = true, bool print_hierarchical = true, bool print_global_only = false) + { + + print_log_header(print_area, print_hierarchical, print_global_only); + + print_log_line("wires", local_num_wires, 0, num_wires, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("wire bits", local_num_wire_bits, 0, num_wire_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("public wires", local_num_pub_wires, 0, num_pub_wires, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("public wire bits", local_num_pub_wire_bits, 0, num_pub_wire_bits, 0, 0, print_area, print_hierarchical, + print_global_only); + print_log_line("ports", local_num_ports, 0, num_ports, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("port bits", local_num_port_bits, 0, num_port_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("memories", local_num_memories, 0, num_memories, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("memory bits", local_num_memory_bits, 0, num_memory_bits, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("processes", local_num_processes, 0, num_processes, 0, 0, print_area, print_hierarchical, print_global_only); + print_log_line("cells", local_num_cells, local_area, num_cells, area, 0, print_area, print_hierarchical, print_global_only); + for (auto &it : num_cells_by_type) + if (it.second) { + auto name = string(log_id(it.first)); + print_log_line(name, local_num_cells_by_type.count(it.first) ? local_num_cells_by_type.at(it.first) : 0, + local_area_cells_by_type.count(it.first) ? local_area_cells_by_type.at(it.first) : 0, it.second, + area_cells_by_type.at(it.first), 1, print_area, print_hierarchical, print_global_only); + } + if (num_submodules > 0) { + print_log_line("submodules", num_submodules, 0, num_submodules, submodule_area, 0, print_area, print_hierarchical, + print_global_only); + for (auto &it : num_submodules_by_type) + if (it.second) + print_log_line(string(log_id(it.first)), it.second, 0, it.second, + submodules_area_by_type.count(it.first) ? submodules_area_by_type.at(it.first) : 0, 1, + print_area, print_hierarchical, print_global_only); + } if (!unknown_cell_area.empty()) { log("\n"); for (auto cell_type : unknown_cell_area) @@ -262,18 +545,28 @@ struct statdata_t if (area != 0) { log("\n"); - log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); - log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area); + if (print_hierarchical || print_global_only) { + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); + log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0 * sequential_area / area); + } else { + double local_area = 0; + for (auto &it : local_area_cells_by_type) + local_area += it.second; + double local_sequential_area = 0; + for (auto &it : local_seq_area_cells_by_type) + local_sequential_area += it.second; + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), local_area); + log(" of which used for sequential elements: %f (%.2f%%)\n", local_sequential_area, + 100.0 * local_sequential_area / local_area); + } } - if (tech == "xilinx") - { + if (tech == "xilinx") { log("\n"); log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc()); } - if (tech == "cmos") - { + if (tech == "cmos") { bool tran_cnt_exact = true; unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); @@ -282,93 +575,315 @@ struct statdata_t } } - void log_data_json(const char *mod_name, bool first_module) + string json_line(unsigned int count_local, double area_local, unsigned int count_global, double area_global) + { + + return stringf("{ \"count\": \"%u\", \"area\": \"%f\", \"local_count\": \"%u\", \"local_area\": \"%f\" }", count_global, area_global, + count_local, area_local); + } + + void log_data_json(const char *mod_name, bool first_module, bool hierarchical = false, bool global_only = false) { if (!first_module) log(",\n"); - log(" %s: {\n", json11::Json(mod_name).dump().c_str()); - log(" \"num_wires\": %u,\n", num_wires); - log(" \"num_wire_bits\": %u,\n", num_wire_bits); - log(" \"num_pub_wires\": %u,\n", num_pub_wires); - log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); - log(" \"num_ports\": %u,\n", num_ports); - log(" \"num_port_bits\": %u,\n", num_port_bits); - log(" \"num_memories\": %u,\n", num_memories); - log(" \"num_memory_bits\": %u,\n", num_memory_bits); - log(" \"num_processes\": %u,\n", num_processes); - log(" \"num_cells\": %u,\n", num_cells); - if (area != 0) { - log(" \"area\": %f,\n", area); - } - log(" \"num_cells_by_type\": {\n"); - bool first_line = true; - for (auto &it : num_cells_by_type) - if (it.second) { - if (!first_line) - log(",\n"); - log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); - first_line = false; + if (hierarchical) { + log(" %s: {\n", json11::Json(mod_name).dump().c_str()); + log(" \"num_wires\": %s,\n", json_line(local_num_wires, 0, num_wires, 0).c_str()); + log(" \"num_wire_bits\": %s,\n", json_line(local_num_wire_bits, 0, num_wire_bits, 0).c_str()); + log(" \"num_pub_wires\": %s,\n", json_line(local_num_pub_wires, 0, num_pub_wires, 0).c_str()); + log(" \"num_pub_wire_bits\": %s,\n", json_line(local_num_pub_wire_bits, 0, num_pub_wire_bits, 0).c_str()); + log(" \"num_ports\": %s,\n", json_line(local_num_ports, 0, num_ports, 0).c_str()); + log(" \"num_port_bits\": %s,\n", json_line(local_num_port_bits, 0, num_port_bits, 0).c_str()); + log(" \"num_memories\": %s,\n", json_line(local_num_memories, 0, num_memories, 0).c_str()); + log(" \"num_memory_bits\": %s,\n", json_line(local_num_memory_bits, 0, num_memory_bits, 0).c_str()); + log(" \"num_processes\": %s,\n", json_line(local_num_processes, 0, num_processes, 0).c_str()); + log(" \"num_cells\": %s,\n", json_line(local_num_cells, local_area, num_cells, area).c_str()); + log(" \"num_submodules\": %s,\n", json_line(0, 0, num_submodules, submodule_area).c_str()); + log(" \"sequential_area\": %s,\n", json_line(0, local_sequential_area, 0, sequential_area).c_str()); + + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %s", json11::Json(log_id(it.first)).dump().c_str(), + json_line(local_num_cells_by_type.count(it.first) ? local_num_cells_by_type.at(it.first) : 0, + local_area_cells_by_type.count(it.first) ? local_area_cells_by_type.at(it.first) : 0, it.second, + area_cells_by_type.at(it.first)) + .c_str()); + first_line = false; + } + log("\n },\n"); + log(" \"num_submodules_by_type\": {\n"); + first_line = true; + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %s", json11::Json(log_id(it.first)).dump().c_str(), + json_line(0, 0, it.second, + submodules_area_by_type.count(it.first) ? submodules_area_by_type.at(it.first) : 0) + .c_str()); + first_line = false; + } + log("\n }\n"); + if (tech == "xilinx") { + log(" \"estimated_num_lc\": %u,\n", estimate_xilinx_lc()); } - log("\n"); - log(" }"); - if (tech == "xilinx") - { - log(",\n"); - log(" \"estimated_num_lc\": %u", estimate_xilinx_lc()); + if (tech == "cmos") { + bool tran_cnt_exact = true; + unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); + log(" \"estimated_num_transistors\": \"%u%s\"\n", tran_cnt, tran_cnt_exact ? "" : "+"); + } + log(" }"); + + } else { + if (global_only) { + log(" %s: {\n", json11::Json(mod_name).dump().c_str()); + log(" \"num_wires\": %u,\n", num_wires); + log(" \"num_wire_bits\": %u,\n", num_wire_bits); + log(" \"num_pub_wires\": %u,\n", num_pub_wires); + log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); + log(" \"num_ports\": %u,\n", num_ports); + log(" \"num_port_bits\": %u,\n", num_port_bits); + log(" \"num_memories\": %u,\n", num_memories); + log(" \"num_memory_bits\": %u,\n", num_memory_bits); + log(" \"num_processes\": %u,\n", num_processes); + log(" \"num_cells\": %u,\n", num_cells); + log(" \"num_submodules\": %u,\n", num_submodules); + if (area != 0) { + log(" \"area\": %f,\n", area); + log(" \"sequential_area\": %f,\n", sequential_area); + } + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); + first_line = false; + } + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); + first_line = false; + } + log("\n"); + log(" }"); + } else { + log(" %s: {\n", json11::Json(mod_name).dump().c_str()); + log(" \"num_wires\": %u,\n", local_num_wires); + log(" \"num_wire_bits\": %u,\n", local_num_wire_bits); + log(" \"num_pub_wires\": %u,\n", local_num_pub_wires); + log(" \"num_pub_wire_bits\": %u,\n", local_num_pub_wire_bits); + log(" \"num_ports\": %u,\n", local_num_ports); + log(" \"num_port_bits\": %u,\n", local_num_port_bits); + log(" \"num_memories\": %u,\n", local_num_memories); + log(" \"num_memory_bits\": %u,\n", local_num_memory_bits); + log(" \"num_processes\": %u,\n", local_num_processes); + log(" \"num_cells\": %u,\n", local_num_cells); + log(" \"num_submodules\": %u,\n", num_submodules); + if (area != 0) { + log(" \"area\": %f,\n", area); + log(" \"sequential_area\": %f,\n", sequential_area); + } + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : local_num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); + first_line = false; + } + for (auto &it : num_submodules_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); + first_line = false; + } + log("\n"); + log(" }"); + } + if (tech == "xilinx") { + log(",\n"); + log(" \"estimated_num_lc\": %u", estimate_xilinx_lc()); + } + if (tech == "cmos") { + bool tran_cnt_exact = true; + unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); + log(",\n"); + log(" \"estimated_num_transistors\": \"%u%s\"", tran_cnt, tran_cnt_exact ? "" : "+"); + } + log("\n"); + log(" }"); } - if (tech == "cmos") - { - bool tran_cnt_exact = true; - unsigned int tran_cnt = cmos_transistor_count(&tran_cnt_exact); - log(",\n"); - log(" \"estimated_num_transistors\": \"%u%s\"", tran_cnt, tran_cnt_exact ? "" : "+"); - } - log("\n"); - log(" }"); } }; -statdata_t hierarchy_worker(std::map &mod_stat, RTLIL::IdString mod, int level, bool quiet = false) +statdata_t hierarchy_worker(std::map &mod_stat, RTLIL::IdString mod, int level, bool quiet = false, bool has_area = true, + bool hierarchy_mode = true) { statdata_t mod_data = mod_stat.at(mod); - std::map num_cells_by_type; - num_cells_by_type.swap(mod_data.num_cells_by_type); - for (auto &it : num_cells_by_type) + for (auto &it : mod_data.num_submodules_by_type) { if (mod_stat.count(it.first) > 0) { if (!quiet) - log(" %*s%-*s %6u\n", 2*level, "", 26-2*level, log_id(it.first), it.second); - mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1, quiet) * it.second; - mod_data.num_cells -= it.second; - } else { - mod_data.num_cells_by_type[it.first] += it.second; + mod_data.print_log_line(string(log_id(it.first)), mod_stat.at(it.first).local_num_cells, + mod_stat.at(it.first).local_area, mod_stat.at(it.first).num_cells, mod_stat.at(it.first).area, + level, has_area, hierarchy_mode); + hierarchy_worker(mod_stat, it.first, level + 1, quiet, has_area, hierarchy_mode) * it.second; } + } return mod_data; } +statdata_t hierarchy_builder(const RTLIL::Design *design, const RTLIL::Module *top_mod, std::map &mod_stat, + bool width_mode, dict &cell_area, string techname) +{ + if (top_mod == nullptr) + top_mod = design->top_module(); + statdata_t mod_data(design, top_mod, width_mode, cell_area, techname); + for (auto cell : top_mod->selected_cells()) { + if (cell_area.count(cell->type) == 0) { + if (design->has(cell->type)) { + if (!(design->module(cell->type)->attributes.count(ID::blackbox))) { + // deal with modules + mod_data.add( + hierarchy_builder(design, design->module(cell->type), mod_stat, width_mode, cell_area, techname)); + mod_data.num_submodules_by_type[cell->type]++; + mod_data.submodules_area_by_type[cell->type] += mod_stat.at(cell->type).area; + mod_data.submodule_area += mod_stat.at(cell->type).area; + mod_data.num_submodules++; + mod_data.unknown_cell_area.erase(cell->type); + mod_data.num_cells -= + (mod_data.num_cells_by_type.count(cell->type) != 0) ? mod_data.num_cells_by_type.at(cell->type) : 0; + mod_data.num_cells_by_type.erase(cell->type); + mod_data.local_num_cells -= (mod_data.local_num_cells_by_type.count(cell->type) != 0) + ? mod_data.local_num_cells_by_type.at(cell->type) + : 0; + mod_data.local_num_cells_by_type.erase(cell->type); + mod_data.local_area_cells_by_type.erase(cell->type); + } else { + // deal with blackbox cells + if (design->module(cell->type)->attributes.count(ID::area) && + design->module(cell->type)->attributes.at(ID::area).size() == 0) { + mod_data.num_submodules_by_type[cell->type]++; + mod_data.num_submodules++; + mod_data.submodules_area_by_type[cell->type] += + double(design->module(cell->type)->attributes.at(ID::area).as_int()); + mod_data.area += double(design->module(cell->type)->attributes.at(ID::area).as_int()); + mod_data.unknown_cell_area.erase(cell->type); + mod_data.num_cells -= + (mod_data.num_cells_by_type.count(cell->type) != 0) ? mod_data.num_cells_by_type.at(cell->type) : 0; + mod_data.num_cells_by_type.erase(cell->type); + mod_data.local_num_cells -= (mod_data.local_num_cells_by_type.count(cell->type) != 0) + ? mod_data.local_num_cells_by_type.at(cell->type) + : 0; + mod_data.local_num_cells_by_type.erase(cell->type); + mod_data.local_area_cells_by_type.erase(cell->type); + } + } + } + } + } + mod_stat[top_mod->name] = mod_data; + return mod_data; +} + void read_liberty_cellarea(dict &cell_area, string liberty_file) { - std::istream* f = uncompressed(liberty_file.c_str()); + std::istream *f = uncompressed(liberty_file.c_str()); yosys_input_files.insert(liberty_file); LibertyParser libparser(*f, liberty_file); delete f; - for (auto cell : libparser.ast->children) - { + for (auto cell : libparser.ast->children) { if (cell->id != "cell" || cell->args.size() != 1) continue; const LibertyAst *ar = cell->find("area"); bool is_flip_flop = cell->find("ff") != nullptr; - if (ar != nullptr && !ar->value.empty()) - cell_area["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop}; + vector single_parameter_area; + vector> double_parameter_area; + vector port_names; + const LibertyAst *sar = cell->find("single_area_parameterised"); + if (sar != nullptr) { + for (const auto &s : sar->args) { + if (s.empty()) { + //catches trailing commas + continue; + } + try { + double value = std::stod(s); + single_parameter_area.push_back(value); + } catch (const std::exception &e) { + log_error("Failed to parse single parameter area value '%s': %s\n", s.c_str(), e.what()); + } + } + if (single_parameter_area.size() == 0) + log_error("single parameter area has size 0: %s\n", sar->args[single_parameter_area.size() - 1].c_str()); + // check if it is a double parameterised area + } + const LibertyAst *dar = cell->find("double_area_parameterised"); + if (dar != nullptr) { + for (const auto &s : dar->args) { + + + vector sub_array; + std::string::size_type start = 0; + std::string::size_type end = s.find_first_of(",", start); + while (end != std::string::npos) { + sub_array.push_back(s.substr(start, end - start)); + start = end + 1; + end = s.find_first_of(",", start); + } + sub_array.push_back(s.substr(start, end)); + + vector cast_sub_array; + for (const auto &s : sub_array) { + double value = 0; + if (s.empty()) { + //catches trailing commas + continue; + } + try { + value = std::stod(s); + cast_sub_array.push_back(value); + } catch (const std::exception &e) { + log_error("Failed to parse double parameter area value for '%s': %s\n", s.c_str(), e.what()); + } + } + double_parameter_area.push_back(cast_sub_array); + if (cast_sub_array.size() == 0) + log_error("double paramter array has size 0: %s\n", s.c_str()); + } + } + const LibertyAst *par = cell->find("port_names"); + if (par != nullptr) { + for (const auto &s : par->args) { + port_names.push_back(s); + } + } + + if (ar != nullptr && !ar->value.empty()) { + string prefix = cell->args[0].substr(0, 1) == "$" ? "" : "\\"; + cell_area[prefix + cell->args[0]] = {atof(ar->value.c_str()), is_flip_flop, single_parameter_area, double_parameter_area, + port_names}; + } } } struct StatPass : public Pass { - StatPass() : Pass("stat", "print some statistics") { } - bool formatted_help() override { + StatPass() : Pass("stat", "print some statistics") {} + bool formatted_help() override + { auto *help = PrettyHelp::get_current(); help->set_group("passes/status"); return false; @@ -381,6 +896,7 @@ struct StatPass : public Pass { log("\n"); log("Print some statistics (number of objects) on the selected portion of the\n"); log("design.\n"); + log("Extracts the area of cells from a liberty file, if provided.\n"); log("\n"); log(" -top \n"); log(" print design hierarchy with this module as top. if the design is fully\n"); @@ -402,35 +918,38 @@ struct StatPass : public Pass { log(" output the statistics in a machine-readable JSON format.\n"); log(" this is output to the console; use \"tee\" to output to a file.\n"); log("\n"); + log(" -hierarchy\n"); + log(" print hierarchical statistics, i.e. The area and number of cells include submodules.\n"); + log(" this changes the format of the json output.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { - bool width_mode = false, json_mode = false; + bool width_mode = false, json_mode = false, hierarchy_mode = false; RTLIL::Module *top_mod = nullptr; std::map mod_stat; dict cell_area; string techname; size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { + for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-width") { width_mode = true; continue; } - if (args[argidx] == "-liberty" && argidx+1 < args.size()) { + if (args[argidx] == "-liberty" && argidx + 1 < args.size()) { string liberty_file = args[++argidx]; rewrite_filename(liberty_file); read_liberty_cellarea(cell_area, liberty_file); continue; } - if (args[argidx] == "-tech" && argidx+1 < args.size()) { + if (args[argidx] == "-tech" && argidx + 1 < args.size()) { techname = args[++argidx]; continue; } - if (args[argidx] == "-top" && argidx+1 < args.size()) { - if (design->module(RTLIL::escape_id(args[argidx+1])) == nullptr) - log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str()); + if (args[argidx] == "-top" && argidx + 1 < args.size()) { + if (design->module(RTLIL::escape_id(args[argidx + 1])) == nullptr) + log_cmd_error("Can't find module %s.\n", args[argidx + 1].c_str()); top_mod = design->module(RTLIL::escape_id(args[++argidx])); continue; } @@ -438,11 +957,15 @@ struct StatPass : public Pass { json_mode = true; continue; } + if (args[argidx] == "-hierarchy") { + hierarchy_mode = true; + continue; + } break; } extra_args(args, argidx, design); - if(!json_mode) + if (!json_mode) log_header(design, "Printing statistics.\n"); if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode) @@ -457,24 +980,38 @@ struct StatPass : public Pass { log(" \"modules\": {\n"); } + if (top_mod != nullptr) { + hierarchy_builder(design, top_mod, mod_stat, width_mode, cell_area, techname); + } else { + for (auto mod : design->selected_modules()) { + if (mod_stat.count(mod->name) == 0) { + hierarchy_builder(design, mod, mod_stat, width_mode, cell_area, techname); + } + } + } + bool first_module = true; - for (auto mod : design->selected_modules()) - { + // determine if anything has a area. + bool has_area = false; + for (auto &it : mod_stat) { + if (it.second.area > 0 || it.second.sequential_area > 0) { + has_area = true; + break; + } + } + for (auto mod : design->selected_modules()) { if (!top_mod && design->full_selection()) if (mod->get_bool_attribute(ID::top)) top_mod = mod; - - statdata_t data(design, mod, width_mode, cell_area, techname); - mod_stat[mod->name] = data; - + statdata_t data = mod_stat.at(mod->name); if (json_mode) { - data.log_data_json(mod->name.c_str(), first_module); + data.log_data_json(mod->name.c_str(), first_module, hierarchy_mode); first_module = false; } else { log("\n"); log("=== %s%s ===\n", log_id(mod->name), mod->is_selected_whole() ? "" : " (partially selected)"); log("\n"); - data.log_data(mod->name, false); + data.log_data(mod->name, false, has_area, hierarchy_mode); } } @@ -483,22 +1020,24 @@ struct StatPass : public Pass { log(top_mod == nullptr ? " }\n" : " },\n"); } - if (top_mod != nullptr) - { + if (top_mod != nullptr) { if (!json_mode && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); log("\n"); - log(" %-28s %6d\n", log_id(top_mod->name), 1); + mod_stat[top_mod->name].print_log_header(has_area, hierarchy_mode, true); + mod_stat[top_mod->name].print_log_line(log_id(top_mod->name), mod_stat[top_mod->name].local_num_cells, + mod_stat[top_mod->name].local_area, mod_stat[top_mod->name].num_cells, + mod_stat[top_mod->name].area, 0, has_area, hierarchy_mode, true); } - statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode); + statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode, has_area, hierarchy_mode); if (json_mode) - data.log_data_json("design", true); + data.log_data_json("design", true, hierarchy_mode, true); else if (GetSize(mod_stat) > 1) { log("\n"); - data.log_data(top_mod->name, true); + data.log_data(top_mod->name, true, has_area, hierarchy_mode, true); } design->scratchpad_set_int("stat.num_wires", data.num_wires); diff --git a/passes/cmds/wrapcell.cc b/passes/cmds/wrapcell.cc index 0c15848e4..39a183e23 100644 --- a/passes/cmds/wrapcell.cc +++ b/passes/cmds/wrapcell.cc @@ -47,7 +47,7 @@ struct ContextData { std::string unused_outputs; }; -std::optional format(std::string fmt, const dict ¶meters, +std::optional format_with_params(std::string fmt, const dict ¶meters, const ContextData &context) { std::stringstream result; @@ -230,7 +230,7 @@ struct WrapcellPass : Pass { context.unused_outputs += "_" + RTLIL::unescape_id(chunk.format(cell)); } - std::optional unescaped_name = format(name_fmt, cell->parameters, context); + std::optional unescaped_name = format_with_params(name_fmt, cell->parameters, context); if (!unescaped_name) log_error("Formatting error when processing cell '%s' in module '%s'\n", log_id(cell), log_id(module)); @@ -270,7 +270,7 @@ struct WrapcellPass : Pass { if (rule.value_fmt.empty()) { subm->set_bool_attribute(rule.name); } else { - std::optional value = format(rule.value_fmt, cell->parameters, context); + std::optional value = format_with_params(rule.value_fmt, cell->parameters, context); if (!value) log_error("Formatting error when processing cell '%s' in module '%s'\n", diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index 8d882ae83..1995be3cd 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -37,14 +37,15 @@ struct EquivInductWorker int max_seq; int success_counter; + bool set_assumes; dict ez_step_is_consistent; pool cell_warn_cache; SigPool undriven_signals; - EquivInductWorker(Module *module, const pool &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module), + EquivInductWorker(Module *module, const pool &unproven_equiv_cells, bool model_undef, int max_seq, bool set_assumes) : module(module), sigmap(module), cells(module->selected_cells()), workset(unproven_equiv_cells), - satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0) + satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0), set_assumes(set_assumes) { satgen.model_undef = model_undef; } @@ -77,6 +78,16 @@ struct EquivInductWorker } } + if (set_assumes) { + if (step == 1) { + RTLIL::SigSpec assumes_a, assumes_en; + satgen.getAssumes(assumes_a, assumes_en, step); + for (int i = 0; i < GetSize(assumes_a); i++) + log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i])); + } + ez->assume(satgen.importAssumes(step)); + } + if (satgen.model_undef) { for (auto bit : undriven_signals.export_all()) ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step))); @@ -184,6 +195,9 @@ struct EquivInductPass : public Pass { log(" -seq \n"); log(" the max. number of time steps to be considered (default = 4)\n"); log("\n"); + log(" -set-assumes\n"); + log(" set all assumptions provided via $assume cells\n"); + log("\n"); log("This command is very effective in proving complex sequential circuits, when\n"); log("the internal state of the circuit quickly propagates to $equiv cells.\n"); log("\n"); @@ -200,7 +214,7 @@ struct EquivInductPass : public Pass { void execute(std::vector args, Design *design) override { int success_counter = 0; - bool model_undef = false; + bool model_undef = false, set_assumes = false; int max_seq = 4; log_header(design, "Executing EQUIV_INDUCT pass.\n"); @@ -215,6 +229,10 @@ struct EquivInductPass : public Pass { max_seq = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-set-assumes") { + set_assumes = true; + continue; + } break; } extra_args(args, argidx, design); @@ -222,6 +240,7 @@ struct EquivInductPass : public Pass { for (auto module : design->selected_modules()) { pool unproven_equiv_cells; + vector assume_cells; for (auto cell : module->selected_cells()) if (cell->type == ID($equiv)) { @@ -234,7 +253,7 @@ struct EquivInductPass : public Pass { continue; } - EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq); + EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq, set_assumes); worker.run(); success_counter += worker.success_counter; } diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 59974a1e6..8ba42595e 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -27,215 +27,345 @@ struct EquivSimpleWorker { Module *module; const vector &equiv_cells; - Cell *equiv_cell; + const vector &assume_cells; + struct Cone { + pool cells; + pool bits; + void clear() { + cells.clear(); + bits.clear(); + } + }; - SigMap &sigmap; - dict &bit2driver; + struct DesignModel { + const SigMap &sigmap; + dict &bit2driver; + }; + DesignModel model; ezSatPtr ez; SatGen satgen; - int max_seq; - bool short_cones; - bool verbose; + + struct Config { + bool verbose = false; + bool short_cones = false; + bool model_undef = false; + bool nogroup = false; + bool set_assumes = false; + int max_seq = 1; + }; + Config cfg; pool> imported_cells_cache; - EquivSimpleWorker(const vector &equiv_cells, SigMap &sigmap, dict &bit2driver, int max_seq, bool short_cones, bool verbose, bool model_undef) : - module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr), - sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), short_cones(short_cones), verbose(verbose) + EquivSimpleWorker(const vector &equiv_cells, const vector &assume_cells, DesignModel model, Config cfg) : + module(equiv_cells.front()->module), equiv_cells(equiv_cells), assume_cells(assume_cells), + model(model), satgen(ez.get(), &model.sigmap), cfg(cfg) { - satgen.model_undef = model_undef; + satgen.model_undef = cfg.model_undef; } - bool find_input_cone(pool &next_seed, pool &cells_cone, pool &bits_cone, const pool &cells_stop, const pool &bits_stop, pool *input_bits, Cell *cell) - { - if (cells_cone.count(cell)) + struct ConeFinder { + DesignModel model; + // Bits we should also analyze in a later iteration (flop inputs) + pool &next_seed; + // Cells and bits we've seen so far while traversing + Cone& cone; + // We're not allowed to traverse past cells and bits in `stop` + const Cone& stop; + // Input bits are bits that no longer can be traversed + // Tracking these is optional + pool* input_bits; + + // Recursively traverses backwards from a cell to find all cells in its input cone + // Adds cell to cone.cells, stops at cells in 'stop' set + // Returns true if stopped on a stop cell + bool find_input_cone(Cell *cell) + { + if (cone.cells.count(cell)) + return false; + + cone.cells.insert(cell); + + if (stop.cells.count(cell)) + return true; + + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_input(cell->type, conn.first)) + for (auto bit : model.sigmap(conn.second)) { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + if (!conn.first.in(ID::CLK, ID::C)) + next_seed.insert(bit); + } else + find_input_cone(bit); + } return false; + } + void find_input_cone(SigBit bit) + { + if (cone.bits.count(bit)) + return; - cells_cone.insert(cell); + cone.bits.insert(bit); - if (cells_stop.count(cell)) - return true; + if (stop.bits.count(bit)) { + if (input_bits != nullptr) input_bits->insert(bit); + return; + } - for (auto &conn : cell->connections()) - if (yosys_celltypes.cell_input(cell->type, conn.first)) - for (auto bit : sigmap(conn.second)) { - if (RTLIL::builtin_ff_cell_types().count(cell->type)) { - if (!conn.first.in(ID::CLK, ID::C)) - next_seed.insert(bit); - } else - find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit); - } - return false; - } + if (!model.bit2driver.count(bit)) + return; - void find_input_cone(pool &next_seed, pool &cells_cone, pool &bits_cone, const pool &cells_stop, const pool &bits_stop, pool *input_bits, SigBit bit) + // If the input cone of the driver cell reaches a stop bit, + // then `bit` is an "input bit" + if (find_input_cone(model.bit2driver.at(bit))) + if (input_bits != nullptr) input_bits->insert(bit); + } + void find_input_cone(pool bits) + { + for (auto bit : bits) + find_input_cone(bit); + } + }; + + // Builds (full or short) input cones from the seeds + // Creates full cones (no stops) and optionally short cones (stop at other side's cone) + // Updates seed_a/seed_b with next iteration's FF inputs + // Returns input bits and cone structures for SAT problem construction + std::tuple, Cone, Cone> init_iter(pool& seed_a, pool& seed_b) const { - if (bits_cone.count(bit)) - return; + // Empty, never inserted to, to traverse full cones + const Cone no_stop; + Cone full_cone_a, full_cone_b; - bits_cone.insert(bit); + // Values of seed_* for the next iteration + pool next_seed_a, next_seed_b; - if (bits_stop.count(bit)) { - if (input_bits != nullptr) input_bits->insert(bit); - return; + { + ConeFinder finder_a {model, next_seed_a, full_cone_a, no_stop, nullptr}; + finder_a.find_input_cone(seed_a); + + ConeFinder finder_b {model, next_seed_b, full_cone_b, no_stop, nullptr}; + finder_b.find_input_cone(seed_b); } - if (!bit2driver.count(bit)) - return; + Cone short_cone_a, short_cone_b; + pool input_bits; - if (find_input_cone(next_seed, cells_cone, bits_cone, cells_stop, bits_stop, input_bits, bit2driver.at(bit))) - if (input_bits != nullptr) input_bits->insert(bit); + if (cfg.short_cones) + { + // Rebuild cones with the knowledge of the full cones. + // Avoids stuffing overlaps in input cones into the solver + // e.g. for A by using the full B cone as stops + next_seed_a.clear(); + ConeFinder short_finder_a = {model, next_seed_a, short_cone_a, short_cone_b, &input_bits}; + short_finder_a.find_input_cone(seed_a); + next_seed_a.swap(seed_a); + + next_seed_b.clear(); + ConeFinder short_finder_b = {model, next_seed_b, short_cone_b, short_cone_a, &input_bits}; + short_finder_b.find_input_cone(seed_b); + next_seed_b.swap(seed_b); + } + else + { + short_cone_a = full_cone_a; + next_seed_a.swap(seed_a); + + short_cone_b = full_cone_b; + next_seed_b.swap(seed_b); + } + return std::make_tuple(input_bits, short_cone_a, short_cone_b); } - bool run_cell() + void report_new_cells(const pool& cells, const Cone& cone_a, const Cone& cone_b) const { - SigBit bit_a = sigmap(equiv_cell->getPort(ID::A)).as_bit(); - SigBit bit_b = sigmap(equiv_cell->getPort(ID::B)).as_bit(); - int ez_context = ez->frozen_literal(); + log(" Adding %d new cells to the problem (%d A, %d B, %d shared).\n", + GetSize(cells), GetSize(cone_a.cells), GetSize(cone_b.cells), + (GetSize(cone_a.cells) + GetSize(cone_b.cells)) - GetSize(cells)); + #if 0 + for (auto cell : short_cells_cone_a) + log(" A-side cell: %s\n", log_id(cell)); + for (auto cell : short_cells_cone_b) + log(" B-side cell: %s\n", log_id(cell)); + #endif + } + void report_new_assume_cells(const pool& extra_problem_cells, int old_size, const pool& problem_cells) const + { + if (cfg.verbose) { + log(" Adding %d new cells to check assumptions (and reusing %d).\n", + GetSize(problem_cells) - old_size, + old_size - (GetSize(problem_cells) - GetSize(extra_problem_cells))); + #if 0 + for (auto cell : extra_problem_cells) + log(" cell: %s\n", log_id(cell)); + #endif + } + } + + // Ensure the input cones of $assume cells get modelled by the problem + pool add_assumes_to_problem(const Cone& cone_a, const Cone& cone_b) const + { + pool extra_problem_cells; + for (auto assume : assume_cells) { + pool assume_seed, dummy_next_seed, overlap_bits; + assume_seed.insert(model.sigmap(assume->getPort(ID::A)).as_bit()); + assume_seed.insert(model.sigmap(assume->getPort(ID::EN)).as_bit()); + + for (auto& cone : {cone_a, cone_b}) { + Cone assume_cone; + ConeFinder{model, dummy_next_seed, assume_cone, cone, &overlap_bits} + .find_input_cone(assume_seed); + if (GetSize(overlap_bits)) { + extra_problem_cells.insert(assume); + extra_problem_cells.insert(assume_cone.cells.begin(), assume_cone.cells.end()); + overlap_bits.clear(); + } + assume_cone.clear(); + dummy_next_seed.clear(); + } + } + return extra_problem_cells; + } + + static void report_missing_model(Cell* cell) + { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type)); + else + log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + } + + void prepare_ezsat(int ez_context, SigBit bit_a, SigBit bit_b) + { if (satgen.model_undef) { - int ez_a = satgen.importSigBit(bit_a, max_seq+1); - int ez_b = satgen.importDefSigBit(bit_b, max_seq+1); - int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1); + int ez_a = satgen.importSigBit(bit_a, cfg.max_seq+1); + int ez_b = satgen.importDefSigBit(bit_b, cfg.max_seq+1); + int ez_undef_a = satgen.importUndefSigBit(bit_a, cfg.max_seq+1); ez->assume(ez->XOR(ez_a, ez_b), ez_context); ez->assume(ez->NOT(ez_undef_a), ez_context); } else { - int ez_a = satgen.importSigBit(bit_a, max_seq+1); - int ez_b = satgen.importSigBit(bit_b, max_seq+1); + int ez_a = satgen.importSigBit(bit_a, cfg.max_seq+1); + int ez_b = satgen.importSigBit(bit_b, cfg.max_seq+1); ez->assume(ez->XOR(ez_a, ez_b), ez_context); } + } + void construct_ezsat(const pool& input_bits, int step) + { + if (cfg.set_assumes) { + if (cfg.verbose && step == cfg.max_seq) { + RTLIL::SigSpec assumes_a, assumes_en; + satgen.getAssumes(assumes_a, assumes_en, step+1); + for (int i = 0; i < GetSize(assumes_a); i++) + log(" Import constraint from assume cell: %s when %s (%d).\n", log_signal(assumes_a[i]), log_signal(assumes_en[i]), step); + } + ez->assume(satgen.importAssumes(step+1)); + } + if (satgen.model_undef) { + for (auto bit : input_bits) + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1))); + } + + if (cfg.verbose) + log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); + } + + bool prove_equiv_cell(Cell* cell) + { + SigBit bit_a = model.sigmap(cell->getPort(ID::A)).as_bit(); + SigBit bit_b = model.sigmap(cell->getPort(ID::B)).as_bit(); + int ez_context = ez->frozen_literal(); + + prepare_ezsat(ez_context, bit_a, bit_b); + + // Two bits, bit_a, and bit_b, have been marked equivalent in the design + // We will be traversing the input cones for each of them + // In the first iteration, we will using those as starting points pool seed_a = { bit_a }; pool seed_b = { bit_b }; - if (verbose) { - log(" Trying to prove $equiv cell %s:\n", log_id(equiv_cell)); - log(" A = %s, B = %s, Y = %s\n", log_signal(bit_a), log_signal(bit_b), log_signal(equiv_cell->getPort(ID::Y))); + if (cfg.verbose) { + log(" Trying to prove $equiv cell %s:\n", log_id(cell)); + log(" A = %s, B = %s, Y = %s\n", log_signal(bit_a), log_signal(bit_b), log_signal(cell->getPort(ID::Y))); } else { - log(" Trying to prove $equiv for %s:", log_signal(equiv_cell->getPort(ID::Y))); + log(" Trying to prove $equiv for %s:", log_signal(cell->getPort(ID::Y))); } - int step = max_seq; + int step = cfg.max_seq; while (1) { - pool no_stop_cells; - pool no_stop_bits; - - pool full_cells_cone_a, full_cells_cone_b; - pool full_bits_cone_a, full_bits_cone_b; - - pool next_seed_a, next_seed_b; - - for (auto bit_a : seed_a) - find_input_cone(next_seed_a, full_cells_cone_a, full_bits_cone_a, no_stop_cells, no_stop_bits, nullptr, bit_a); - - for (auto bit_b : seed_b) - find_input_cone(next_seed_b, full_cells_cone_b, full_bits_cone_b, no_stop_cells, no_stop_bits, nullptr, bit_b); - - pool short_cells_cone_a, short_cells_cone_b; - pool short_bits_cone_a, short_bits_cone_b; - pool input_bits; - - if (short_cones) - { - next_seed_a.clear(); - for (auto bit_a : seed_a) - find_input_cone(next_seed_a, short_cells_cone_a, short_bits_cone_a, full_cells_cone_b, full_bits_cone_b, &input_bits, bit_a); - next_seed_a.swap(seed_a); - - next_seed_b.clear(); - for (auto bit_b : seed_b) - find_input_cone(next_seed_b, short_cells_cone_b, short_bits_cone_b, full_cells_cone_a, full_bits_cone_a, &input_bits, bit_b); - next_seed_b.swap(seed_b); - } - else - { - short_cells_cone_a = full_cells_cone_a; - short_bits_cone_a = full_bits_cone_a; - next_seed_a.swap(seed_a); - - short_cells_cone_b = full_cells_cone_b; - short_bits_cone_b = full_bits_cone_b; - next_seed_b.swap(seed_b); - } + // Traverse input cones of seed_a and seed_b, potentially finding new seeds + auto [input_bits, cone_a, cone_b] = init_iter(seed_a, seed_b); + // Cells to model in SAT solver pool problem_cells; - problem_cells.insert(short_cells_cone_a.begin(), short_cells_cone_a.end()); - problem_cells.insert(short_cells_cone_b.begin(), short_cells_cone_b.end()); + problem_cells.insert(cone_a.cells.begin(), cone_a.cells.end()); + problem_cells.insert(cone_b.cells.begin(), cone_b.cells.end()); - if (verbose) - { - log(" Adding %d new cells to the problem (%d A, %d B, %d shared).\n", - GetSize(problem_cells), GetSize(short_cells_cone_a), GetSize(short_cells_cone_b), - (GetSize(short_cells_cone_a) + GetSize(short_cells_cone_b)) - GetSize(problem_cells)); - #if 0 - for (auto cell : short_cells_cone_a) - log(" A-side cell: %s\n", log_id(cell)); + if (cfg.verbose) + report_new_cells(problem_cells, cone_a, cone_b); - for (auto cell : short_cells_cone_b) - log(" B-side cell: %s\n", log_id(cell)); - #endif + if (cfg.set_assumes) { + auto extras = add_assumes_to_problem(cone_a, cone_b); + if (GetSize(extras)) { + auto old_size = GetSize(problem_cells); + problem_cells.insert(extras.begin(), extras.end()); + report_new_assume_cells(extras, old_size, problem_cells); + } } for (auto cell : problem_cells) { auto key = pair(cell, step+1); if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) { - if (RTLIL::builtin_ff_cell_types().count(cell->type)) - log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type)); - else - log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + report_missing_model(cell); } imported_cells_cache.insert(key); } - if (satgen.model_undef) { - for (auto bit : input_bits) - ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1))); - } - - if (verbose) - log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); + construct_ezsat(input_bits, step); if (!ez->solve(ez_context)) { - log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); - equiv_cell->setPort(ID::B, equiv_cell->getPort(ID::A)); + log(cfg.verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); + // Replace $equiv cell with a short + cell->setPort(ID::B, cell->getPort(ID::A)); ez->assume(ez->NOT(ez_context)); return true; } - if (verbose) - log(" Failed to prove equivalence with sequence length %d.\n", max_seq - step); + if (cfg.verbose) + log(" Failed to prove equivalence with sequence length %d.\n", cfg.max_seq - step); if (--step < 0) { - if (verbose) + if (cfg.verbose) log(" Reached sequence limit.\n"); break; } if (seed_a.empty() && seed_b.empty()) { - if (verbose) + if (cfg.verbose) log(" No nets to continue in previous time step.\n"); break; } if (seed_a.empty()) { - if (verbose) + if (cfg.verbose) log(" No nets on A-side to continue in previous time step.\n"); break; } if (seed_b.empty()) { - if (verbose) + if (cfg.verbose) log(" No nets on B-side to continue in previous time step.\n"); break; } - if (verbose) { + if (cfg.verbose) { #if 0 log(" Continuing analysis in previous time step with the following nets:\n"); for (auto bit : seed_a) @@ -248,7 +378,7 @@ struct EquivSimpleWorker } } - if (!verbose) + if (!cfg.verbose) log(" failed.\n"); ez->assume(ez->NOT(ez_context)); @@ -260,14 +390,13 @@ struct EquivSimpleWorker if (GetSize(equiv_cells) > 1) { SigSpec sig; for (auto c : equiv_cells) - sig.append(sigmap(c->getPort(ID::Y))); + sig.append(model.sigmap(c->getPort(ID::Y))); log(" Grouping SAT models for %s:\n", log_signal(sig)); } int counter = 0; for (auto c : equiv_cells) { - equiv_cell = c; - if (run_cell()) + if (prove_equiv_cell(c)) counter++; } return counter; @@ -301,35 +430,41 @@ struct EquivSimplePass : public Pass { log(" -seq \n"); log(" the max. number of time steps to be considered (default = 1)\n"); log("\n"); + log(" -set-assumes\n"); + log(" set all assumptions provided via $assume cells\n"); + log("\n"); } void execute(std::vector args, Design *design) override { - bool verbose = false, short_cones = false, model_undef = false, nogroup = false; + EquivSimpleWorker::Config cfg = {}; int success_counter = 0; - int max_seq = 1; log_header(design, "Executing EQUIV_SIMPLE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-v") { - verbose = true; + cfg.verbose = true; continue; } if (args[argidx] == "-short") { - short_cones = true; + cfg.short_cones = true; continue; } if (args[argidx] == "-undef") { - model_undef = true; + cfg.model_undef = true; continue; } if (args[argidx] == "-nogroup") { - nogroup = true; + cfg.nogroup = true; continue; } if (args[argidx] == "-seq" && argidx+1 < args.size()) { - max_seq = atoi(args[++argidx].c_str()); + cfg.max_seq = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-set-assumes") { + cfg.set_assumes = true; continue; } break; @@ -347,17 +482,21 @@ struct EquivSimplePass : public Pass { SigMap sigmap(module); dict bit2driver; dict> unproven_equiv_cells; + vector assumes; int unproven_cells_counter = 0; - for (auto cell : module->selected_cells()) + for (auto cell : module->selected_cells()) { if (cell->type == ID($equiv) && cell->getPort(ID::A) != cell->getPort(ID::B)) { auto bit = sigmap(cell->getPort(ID::Y).as_bit()); auto bit_group = bit; - if (!nogroup && bit_group.wire) + if (!cfg.nogroup && bit_group.wire) bit_group.offset = 0; unproven_equiv_cells[bit_group][bit] = cell; unproven_cells_counter++; + } else if (cell->type == ID($assume)) { + assumes.push_back(cell); } + } if (unproven_equiv_cells.empty()) continue; @@ -375,15 +514,16 @@ struct EquivSimplePass : public Pass { } unproven_equiv_cells.sort(); - for (auto it : unproven_equiv_cells) + for (auto [_, d] : unproven_equiv_cells) { - it.second.sort(); + d.sort(); vector cells; - for (auto it2 : it.second) - cells.push_back(it2.second); + for (auto [_, cell] : d) + cells.push_back(cell); - EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, short_cones, verbose, model_undef); + EquivSimpleWorker::DesignModel model {sigmap, bit2driver}; + EquivSimpleWorker worker(cells, assumes, model, cfg); success_counter += worker.run(); } } diff --git a/passes/memory/memlib.h b/passes/memory/memlib.h index c3f7728f1..43dec7386 100644 --- a/passes/memory/memlib.h +++ b/passes/memory/memlib.h @@ -100,7 +100,7 @@ enum class WrTransKind { struct WrTransDef { WrTransTargetKind target_kind; - int target_group; + int target_group = 0; WrTransKind kind; }; diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 210c4828f..2fb0f947f 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -199,7 +199,7 @@ struct OptDffWorker const auto complimentary_var = find_comp(left, right); - if (complimentary_var) { + if (complimentary_var && new_patterns.count(right)) { new_patterns.erase(right); right.erase(complimentary_var.value()); new_patterns.insert(right); diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index b519d33d6..ba8168e74 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -44,20 +44,16 @@ struct OptMergeWorker CellTypes ct; int total_count; - static vector> sorted_pmux_in(const dict &conn) + static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) { - SigSpec sig_s = conn.at(ID::S); - SigSpec sig_b = conn.at(ID::B); - int s_width = GetSize(sig_s); int width = GetSize(sig_b) / s_width; - vector> sb_pairs; + hashlib::commutative_hash comm; for (int i = 0; i < s_width; i++) - sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + comm.eat(hash_ops>::hash({sig_s[i], sig_b.extract(i*width, width)})); - std::sort(sb_pairs.begin(), sb_pairs.end()); - return sb_pairs; + return comm.hash_into(h); } static void sort_pmux_conn(dict &conn) @@ -89,12 +85,10 @@ struct OptMergeWorker // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - std::array inputs = { - assign_map(cell->getPort(ID::A)), - assign_map(cell->getPort(ID::B)) - }; - std::sort(inputs.begin(), inputs.end()); - h = hash_ops>::hash_into(inputs, h); + hashlib::commutative_hash comm; + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::A)))); + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::B)))); + h = comm.hash_into(h); } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort(); @@ -104,44 +98,31 @@ struct OptMergeWorker a.sort_and_unify(); h = a.hash_into(h); } else if (cell->type == ID($pmux)) { - dict conn = cell->connections(); - assign_map.apply(conn.at(ID::A)); - assign_map.apply(conn.at(ID::B)); - assign_map.apply(conn.at(ID::S)); - for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { - h = s_bit.hash_into(h); - h = b_chunk.hash_into(h); - } + SigSpec sig_s = assign_map(cell->getPort(ID::S)); + SigSpec sig_b = assign_map(cell->getPort(ID::B)); + h = hash_pmux_in(sig_s, sig_b, h); h = assign_map(cell->getPort(ID::A)).hash_into(h); } else { - std::vector> conns; - for (const auto& conn : cell->connections()) { - conns.push_back(conn); + hashlib::commutative_hash comm; + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) + continue; + comm.eat(hash_ops>::hash({port, assign_map(sig)})); } - std::sort(conns.begin(), conns.end()); - for (const auto& [port, sig] : conns) { - if (!cell->output(port)) { - h = port.hash_into(h); - h = assign_map(sig).hash_into(h); - } - } - + h = comm.hash_into(h); if (RTLIL::builtin_ff_cell_types().count(cell->type)) h = initvals(cell->getPort(ID::Q)).hash_into(h); - } return h; } static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) { - using Paramvec = std::vector>; - Paramvec params; + hashlib::commutative_hash comm; for (const auto& param : cell->parameters) { - params.push_back(param); + comm.eat(hash_ops>::hash(param)); } - std::sort(params.begin(), params.end()); - return hash_ops::hash_into(params, h); + return comm.hash_into(h); } Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const @@ -227,7 +208,7 @@ struct OptMergeWorker } OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) : - design(design), module(module), assign_map(module), mode_share_all(mode_share_all) + design(design), module(module), mode_share_all(mode_share_all) { total_count = 0; ct.setup_internals(); diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 100b1b495..98803b935 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -23,12 +23,40 @@ #include "kernel/celltypes.h" #include #include +#include +#include #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -using RTLIL::id2cstr; +/** + * TERMINOLOGY + * + * A multiplexer tree (mux tree) is a tree composed exclusively of muxes. + * By mux, I mean $mux or $pmux. By port, I usually mean input port. + * The children of a node are all the muxes driving its input ports (A, B). + * It must be rooted in a "root mux", a mux which has multiple mux users + * or any number of non-mux users. Only the root and leaf nodes can be + * root muxes, not the internal nodes. Leaf nodes that are root muxes + * are roots of "input trees". + * + * OPERATING PRINCIPLE + * + * This pass traverses mux trees, learning port activations and making + * assumptions about them as it goes. When valid, ports are replaced + * with constants, or removed if they can never be activated. + * When valid, muxes are replaced with shorts from port to output, + * or removed if their outputs are found to be no longer observable. + * + * Input trees can be recursed into if limits_t::recursions_left allows. + * Otherwise, the input tree is queued for a re-run with a fresh knowledge_t. + * At any point, if glob_evals_left goes to 0, the pass terminates. + * + * Unlike share, this pass doesn't use SAT to learn things about logic + * driving the mux control signals, and traverses mux regions from users + * to drivers. + */ struct OptMuxtreeWorker { @@ -36,9 +64,10 @@ struct OptMuxtreeWorker RTLIL::Module *module; SigMap assign_map; int removed_count; - int glob_abort_cnt = 100000; + int glob_evals_left = 100000; struct bitinfo_t { + // Is bit directly used by non-mux cells or ports? bool seen_non_mux; pool mux_users; pool mux_drivers; @@ -48,12 +77,13 @@ struct OptMuxtreeWorker vector bit2info; struct portinfo_t { - int ctrl_sig; - pool input_sigs; - pool input_muxes; - bool const_activated; - bool const_deactivated; - bool enabled; + int ctrl_sig = -1; // No associated control signal by default + pool input_sigs = {}; + pool input_muxes = {}; + bool const_activated = false; + bool const_deactivated = false; + // Is the port reachable from inputs of a mux tree? + bool observable = false; }; struct muxinfo_t { @@ -66,13 +96,16 @@ struct OptMuxtreeWorker vector root_enable_muxes; pool root_mux_rerun; - OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : - design(design), module(module), assign_map(module), removed_count(0) - { - log("Running muxtree optimizer on module %s..\n", module->name.c_str()); - - log(" Creating internal representation of mux trees.\n"); + portinfo_t used_port_bit(RTLIL::SigSpec& sig, int mux_idx) { + portinfo_t portinfo = {}; + for (int bit_idx : sig2bits(sig)) { + bit2info[bit_idx].mux_users.insert(mux_idx); + portinfo.input_sigs.insert(bit_idx); + } + return portinfo; + } + void track_mux(Cell* cell) { // Populate bit2info[]: // .seen_non_mux // .mux_users @@ -82,73 +115,59 @@ struct OptMuxtreeWorker // .input_sigs // .const_activated // .const_deactivated - for (auto cell : module->cells()) - { - if (cell->type.in(ID($mux), ID($pmux))) - { - RTLIL::SigSpec sig_a = cell->getPort(ID::A); - RTLIL::SigSpec sig_b = cell->getPort(ID::B); - RTLIL::SigSpec sig_s = cell->getPort(ID::S); - RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + RTLIL::SigSpec sig_s = cell->getPort(ID::S); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); - muxinfo_t muxinfo; - muxinfo.cell = cell; + muxinfo_t muxinfo; + muxinfo.cell = cell; + int this_mux_idx = GetSize(mux2info); - for (int i = 0; i < GetSize(sig_s); i++) { - RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); - RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); - portinfo_t portinfo; - portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front(); - for (int idx : sig2bits(sig)) { - bit2info[idx].mux_users.insert(GetSize(mux2info)); - portinfo.input_sigs.insert(idx); - } - portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool(); - portinfo.const_deactivated = ctrl_sig.is_fully_const() && !ctrl_sig.as_bool(); - portinfo.enabled = false; - muxinfo.ports.push_back(portinfo); - } - - portinfo_t portinfo; - for (int idx : sig2bits(sig_a)) { - bit2info[idx].mux_users.insert(GetSize(mux2info)); - portinfo.input_sigs.insert(idx); - } - portinfo.ctrl_sig = -1; - portinfo.const_activated = false; - portinfo.const_deactivated = false; - portinfo.enabled = false; - muxinfo.ports.push_back(portinfo); - - for (int idx : sig2bits(sig_y)) - bit2info[idx].mux_drivers.insert(GetSize(mux2info)); - - for (int idx : sig2bits(sig_s)) - bit2info[idx].seen_non_mux = true; - - mux2info.push_back(muxinfo); - } - else - { - for (auto &it : cell->connections()) { - for (int idx : sig2bits(it.second)) - bit2info[idx].seen_non_mux = true; - } - } + // Analyze port B + // In case of $pmux, port B is multiple slices, concatenated, one per bit of port S + for (int i = 0; i < GetSize(sig_s); i++) { + RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); + RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); + portinfo_t portinfo = used_port_bit(sig, this_mux_idx); + portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front(); + portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool(); + portinfo.const_deactivated = ctrl_sig.is_fully_const() && !ctrl_sig.as_bool(); + muxinfo.ports.push_back(portinfo); } + + // Analyze port A + muxinfo.ports.push_back(used_port_bit(sig_a, this_mux_idx)); + + for (int idx : sig2bits(sig_y)) + bit2info[idx].mux_drivers.insert(this_mux_idx); + + for (int idx : sig2bits(sig_s)) + bit2info[idx].seen_non_mux = true; + + mux2info.push_back(muxinfo); + } + + void see_non_mux_cell(Cell* cell) { + for (auto &it : cell->connections()) { + for (int idx : sig2bits(it.second)) + bit2info[idx].seen_non_mux = true; + } + } + + void see_non_mux_wires() { for (auto wire : module->wires()) { if (wire->port_output || wire->get_bool_attribute(ID::keep)) for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } + } - if (mux2info.empty()) { - log(" No muxes found in this module.\n"); - return; - } - - // Populate mux2info[].ports[]: - // .input_muxes + // Populate mux2info[].ports[]: + // .input_muxes + void fixup_input_muxes() { + // bit2info knows the mux users and mux drivers of bits + // use this to tell mux2info ports about what muxes are driven by it for (int i = 0; i < GetSize(bit2info); i++) for (int j : bit2info[i].mux_users) for (auto &p : mux2info[j].ports) { @@ -156,12 +175,15 @@ struct OptMuxtreeWorker for (int k : bit2info[i].mux_drivers) p.input_muxes.insert(k); } + } - log(" Evaluating internal representation of mux trees.\n"); - + void populate_roots() { + // mux_to_users[i] means "set of muxes using output of mux i" dict> mux_to_users; - root_muxes.resize(GetSize(mux2info)); + // Pure root muxes (outputs seen by non-muxes) root_enable_muxes.resize(GetSize(mux2info)); + // All root muxes (outputs seen by non-muxes or multiple muxes) + root_muxes.resize(GetSize(mux2info)); for (auto &bi : bit2info) { for (int i : bi.mux_drivers) @@ -175,16 +197,44 @@ struct OptMuxtreeWorker } } - for (auto &it : mux_to_users) - if (GetSize(it.second) > 1) - root_muxes.at(it.first) = true; + for (auto &[driving_mux, user_muxes] : mux_to_users) + if (GetSize(user_muxes) > 1) + root_muxes.at(driving_mux) = true; + } + + OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : + design(design), module(module), assign_map(module), removed_count(0) + { + log("Running muxtree optimizer on module %s..\n", module->name.c_str()); + + log(" Creating internal representation of mux trees.\n"); + + for (auto cell : module->cells()) + { + if (cell->type.in(ID($mux), ID($pmux))) + track_mux(cell); + else + see_non_mux_cell(cell); + } + see_non_mux_wires(); + + if (mux2info.empty()) { + log(" No muxes found in this module.\n"); + return; + } + + fixup_input_muxes(); + + log(" Evaluating internal representation of mux trees.\n"); + + populate_roots(); for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++) if (root_muxes.at(mux_idx)) { log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : ""); root_mux_rerun.erase(mux_idx); eval_root_mux(mux_idx); - if (glob_abort_cnt == 0) { + if (glob_evals_left == 0) { log(" Giving up (too many iterations)\n"); return; } @@ -196,21 +246,21 @@ struct OptMuxtreeWorker log_assert(root_enable_muxes.at(mux_idx)); root_mux_rerun.erase(mux_idx); eval_root_mux(mux_idx); - if (glob_abort_cnt == 0) { + if (glob_evals_left == 0) { log(" Giving up (too many iterations)\n"); return; } } log(" Analyzing evaluation results.\n"); - log_assert(glob_abort_cnt > 0); + log_assert(glob_evals_left > 0); for (auto &mi : mux2info) { vector live_ports; for (int port_idx = 0; port_idx < GetSize(mi.ports); port_idx++) { portinfo_t &pi = mi.ports[port_idx]; - if (pi.enabled) { + if (pi.observable) { live_ports.push_back(port_idx); } else { log(" dead port %d/%d on %s %s.\n", port_idx+1, GetSize(mi.ports), @@ -288,73 +338,125 @@ struct OptMuxtreeWorker struct knowledge_t { - // database of known inactive signals - // the payload is a reference counter used to manage the - // list. when it is non-zero the signal in known to be inactive - vector known_inactive; + // Known inactive signals + // The payload is a reference counter used to manage the list + // When it is non-zero, the signal in known to be inactive + // When it reaches zero, the map element is removed + std::unordered_map known_inactive; // database of known active signals - vector known_active; + std::unordered_map known_active; // this is just used to keep track of visited muxes in order to prohibit // endless recursion in mux loops - vector visited_muxes; + std::unordered_set visited_muxes; }; - void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count) + static void activate_port(knowledge_t &knowledge, int port_idx, const muxinfo_t &muxinfo) { + // First, mark all other ports inactive + for (int i = 0; i < GetSize(muxinfo.ports); i++) { + if (i == port_idx) + continue; + if (muxinfo.ports[i].ctrl_sig >= 0) + ++knowledge.known_inactive[muxinfo.ports[i].ctrl_sig]; + } + // Mark port active unless it's the last one + if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) + ++knowledge.known_active[muxinfo.ports[port_idx].ctrl_sig]; + } + + static void deactivate_port(knowledge_t &knowledge, int port_idx, const muxinfo_t &muxinfo) { + auto unlearn = [](std::unordered_map& knowns, int i) { + auto it = knowns.find(i); + if (it != knowns.end()) + if (--it->second == 0) + knowns.erase(it); + }; + + if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) + unlearn(knowledge.known_active, muxinfo.ports[port_idx].ctrl_sig); + + // Undo inactivity assumptions for other ports + for (int i = 0; i < GetSize(muxinfo.ports); i++) { + if (i == port_idx) + continue; + if (muxinfo.ports[i].ctrl_sig >= 0) + unlearn(knowledge.known_inactive, muxinfo.ports[i].ctrl_sig); + } + } + + struct limits_t { + // Are we allowed to replace inputs with constants? + // True if knowledge doesn't contain assumptions + bool do_replace_known = true; + // Are we allowed to mark ports as observable? + // True if we're recursing from a pure root mux + bool do_mark_ports_observable = true; + // How many more subtree recursions into input trees can we take? + // Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. + int recursions_left = 3; + limits_t subtree() const { + limits_t ret = *this; + log_assert(ret.recursions_left > 0); + ret.recursions_left--; + return ret; + } + }; + void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, limits_t limits) { - if (glob_abort_cnt == 0) + if (glob_evals_left == 0) return; muxinfo_t &muxinfo = mux2info[mux_idx]; - if (do_enable_ports) - muxinfo.ports[port_idx].enabled = true; + if (limits.do_mark_ports_observable) + muxinfo.ports[port_idx].observable = true; - for (int i = 0; i < GetSize(muxinfo.ports); i++) { - if (i == port_idx) - continue; - if (muxinfo.ports[i].ctrl_sig >= 0) - knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)++; - } + // For the purposes of recursion, we assume the port is active, + // meaning all other ports are inactive + activate_port(knowledge, port_idx, muxinfo); - if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) - knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)++; - - vector parent_muxes; + vector input_mux_queue; for (int m : muxinfo.ports[port_idx].input_muxes) { - if (knowledge.visited_muxes[m]) + if (knowledge.visited_muxes.count(m)) continue; - knowledge.visited_muxes[m] = true; - parent_muxes.push_back(m); + knowledge.visited_muxes.insert(m); + input_mux_queue.push_back(m); } - for (int m : parent_muxes) { + for (int m : input_mux_queue) { if (root_enable_muxes.at(m)) continue; else if (root_muxes.at(m)) { - if (abort_count == 0) { + // This leaf node of the current tree + // is the root of an input tree of the current tree + if (limits.recursions_left == 0) { + // Ran out of subtree depth, re-eval this input tree in the next re-run root_mux_rerun.insert(m); root_enable_muxes.at(m) = true; log_debug(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell)); - } else - eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1); - } else - eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count); - if (glob_abort_cnt == 0) + } else { + auto new_limits = limits.subtree(); + // Since our knowledge includes assumption, + // we can't generally allow replacing in an input tree based on it + new_limits.do_replace_known = false; + eval_mux(knowledge, m, new_limits); + } + } else { + // This non-root input mux has only this mux as a user, + // so here we are allowed to pass along do_replace_known + eval_mux(knowledge, m, limits); + } + if (glob_evals_left == 0) return; } - for (int m : parent_muxes) - knowledge.visited_muxes[m] = false; - if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) - knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)--; + // Allow revisiting input muxes, since evaluating other ports should + // revisit these input muxes with different activation assumptions + for (int m : input_mux_queue) + knowledge.visited_muxes.erase(m); - for (int i = 0; i < GetSize(muxinfo.ports); i++) { - if (i == port_idx) - continue; - if (muxinfo.ports[i].ctrl_sig >= 0) - knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)--; - } + // Undo our assumptions that the port is active + deactivate_port(knowledge, port_idx, muxinfo); } void replace_known(knowledge_t &knowledge, muxinfo_t &muxinfo, IdString portname) @@ -362,37 +464,51 @@ struct OptMuxtreeWorker SigSpec sig = muxinfo.cell->getPort(portname); bool did_something = false; - int width = 0; + int width_if_b = 0; idict ctrl_bits; if (portname == ID::B) - width = GetSize(muxinfo.cell->getPort(ID::A)); + width_if_b = GetSize(muxinfo.cell->getPort(ID::A)); for (int bit : sig2bits(muxinfo.cell->getPort(ID::S), false)) ctrl_bits(bit); - int port_idx = 0, port_off = 0; + int slice_idx = 0, slice_off = 0; vector bits = sig2bits(sig, false); for (int i = 0; i < GetSize(bits); i++) { if (bits[i] >= 0) { - if (knowledge.known_inactive.at(bits[i])) { + if (knowledge.known_inactive.count(bits[i]) > 0) { sig[i] = State::S0; did_something = true; } else - if (knowledge.known_active.at(bits[i])) { + if (knowledge.known_active.count(bits[i]) > 0) { sig[i] = State::S1; did_something = true; } if (ctrl_bits.count(bits[i])) { - if (width) { - sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; - } else { + if (!width_if_b) { + // Single-bit $mux example + // mux: S ? B : A = Y + // A=S + // 0 ? B : 0 = 0 + // 1 ? B : 1 = B + // rewrite to A=0 + // 0 ? B : 0 = 0 + // 1 ? B : 0 = B + // which is equivalent sig[i] = State::S0; + } else { + // "Sliced" $pmux example + // B[i]=S[j] + // i == j => B[i] activated only when B[i] is high, safe to rewrite to 1 + // i != j => B[i] activated only when B[i] is low, safe to rewrite to 0 + sig[i] = ctrl_bits.at(bits[i]) == slice_idx ? State::S1 : State::S0; } did_something = true; } } - if (width) { - if (++port_off == width) - port_idx++, port_off=0; + if (width_if_b) { + // Roll over into next slice + if (++slice_off == width_if_b) + slice_idx++, slice_off=0; } } @@ -403,16 +519,17 @@ struct OptMuxtreeWorker } } - void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count) + void eval_mux(knowledge_t &knowledge, int mux_idx, limits_t limits) { - if (glob_abort_cnt == 0) + if (glob_evals_left == 0) return; - glob_abort_cnt--; + glob_evals_left--; muxinfo_t &muxinfo = mux2info[mux_idx]; + log_debug("\t\teval %s (replace %d enable %d)\n", log_id(muxinfo.cell), limits.do_replace_known, limits.do_mark_ports_observable); // set input ports to constants if we find known active or inactive signals - if (do_replace_known) { + if (limits.do_replace_known) { replace_known(knowledge, muxinfo, ID::A); replace_known(knowledge, muxinfo, ID::B); } @@ -422,21 +539,21 @@ struct OptMuxtreeWorker { portinfo_t &portinfo = muxinfo.ports[port_idx]; if (portinfo.const_activated) { - eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + eval_mux_port(knowledge, mux_idx, port_idx, limits); return; } } - // compare ports with known_active signals. if we find a match, only this - // port can be active. do not include the last port (its the default port - // that has no control signals). + // Compare ports with known active control signals. if we find a match, + // only this port can be active. Do not include the last port, + // it's the default port without an associated control signal for (int port_idx = 0; port_idx < GetSize(muxinfo.ports)-1; port_idx++) { portinfo_t &portinfo = muxinfo.ports[port_idx]; if (portinfo.const_deactivated) continue; - if (knowledge.known_active.at(portinfo.ctrl_sig)) { - eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + if (knowledge.known_active.count(portinfo.ctrl_sig) > 0) { + eval_mux_port(knowledge, mux_idx, port_idx, limits); return; } } @@ -449,24 +566,23 @@ struct OptMuxtreeWorker if (portinfo.const_deactivated) continue; if (port_idx < GetSize(muxinfo.ports)-1) - if (knowledge.known_inactive.at(portinfo.ctrl_sig)) + if (knowledge.known_inactive.count(portinfo.ctrl_sig) > 0) continue; - eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + eval_mux_port(knowledge, mux_idx, port_idx, limits); - if (glob_abort_cnt == 0) + if (glob_evals_left == 0) return; } } void eval_root_mux(int mux_idx) { - log_assert(glob_abort_cnt > 0); + log_assert(glob_evals_left > 0); knowledge_t knowledge; - knowledge.known_inactive.resize(GetSize(bit2info)); - knowledge.known_active.resize(GetSize(bit2info)); - knowledge.visited_muxes.resize(GetSize(mux2info)); - knowledge.visited_muxes[mux_idx] = true; - eval_mux(knowledge, mux_idx, true, root_enable_muxes.at(mux_idx), 3); + knowledge.visited_muxes.insert(mux_idx); + limits_t limits = {}; + limits.do_mark_ports_observable = root_enable_muxes.at(mux_idx); + eval_mux(knowledge, mux_idx, limits); } }; diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index cc37677ce..9aaac84e9 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -113,25 +113,82 @@ bool map_mux8; bool map_mux16; bool markgroups; -int map_autoidx; -SigMap assign_map; -RTLIL::Module *module; -std::vector signal_list; -dict signal_map; -FfInitVals initvals; + pool enabled_gates; bool cmos_cost; -bool had_init; -bool clk_polarity, en_polarity, arst_polarity, srst_polarity; -RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig; -dict pi_map, po_map; - -int undef_bits_lost; - -int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1) +struct AbcConfig { - assign_map.apply(bit); + std::string script_file; + std::string exe_file; + std::vector liberty_files; + std::vector genlib_files; + std::string constr_file; + vector lut_costs; + std::string delay_target; + std::string sop_inputs; + std::string sop_products; + std::string lutin_shared; + std::vector dont_use_cells; + bool cleanup = true; + bool keepff = false; + bool fast_mode = false; + bool show_tempdir = false; + bool sop_mode = false; + bool abc_dress = false; +}; + +struct AbcSigVal { + bool is_port; + + AbcSigVal(bool is_port = false) : is_port(is_port) {} + AbcSigVal &operator|=(const AbcSigVal &other) { + is_port |= other.is_port; + return *this; + } +}; + +using AbcSigMap = SigValMap; + +struct AbcModuleState { + const AbcConfig &config; + + int map_autoidx = 0; + std::vector signal_list; + dict signal_map; + FfInitVals &initvals; + bool had_init = false; + bool did_run_abc = false; + + bool clk_polarity = false; + bool en_polarity = false; + bool arst_polarity = false; + bool srst_polarity = false; + RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig; + dict pi_map, po_map; + + int undef_bits_lost = 0; + + std::string tempdir_name; + + AbcModuleState(const AbcConfig &config, FfInitVals &initvals) + : config(config), initvals(initvals) {} + + int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1); + void mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig); + bool extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff); + std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr); + void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts); + void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); + void abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, + bool dff_mode, std::string clk_str); + void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); + void finish(); +}; + +int AbcModuleState::map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type, int in1, int in2, int in3, int in4) +{ + AbcSigVal val = assign_map.apply_and_get_value(bit); if (bit == State::Sx) undef_bits_lost++; @@ -144,7 +201,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, gate.in2 = -1; gate.in3 = -1; gate.in4 = -1; - gate.is_port = false; + gate.is_port = bit.wire != nullptr && val.is_port; gate.bit = bit; gate.init = initvals(bit); signal_list.push_back(gate); @@ -167,40 +224,40 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, return gate.id; } -void mark_port(RTLIL::SigSpec sig) +void AbcModuleState::mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig) { for (auto &bit : assign_map(sig)) if (bit.wire != nullptr && signal_map.count(bit) > 0) signal_list[signal_map[bit]].is_port = true; } -void extract_cell(RTLIL::Cell *cell, bool keepff) +bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff) { if (RTLIL::builtin_ff_cell_types().count(cell->type)) { FfData ff(&initvals, cell); gate_type_t type = G(FF); if (!ff.has_clk) - return; + return false; if (ff.has_gclk) - return; + return false; if (ff.has_aload) - return; + return false; if (ff.has_sr) - return; + return false; if (!ff.is_fine) - return; + return false; if (clk_polarity != ff.pol_clk) - return; + return false; if (clk_sig != assign_map(ff.sig_clk)) - return; + return false; if (ff.has_ce) { if (en_polarity != ff.pol_ce) - return; + return false; if (en_sig != assign_map(ff.sig_ce)) - return; + return false; } else { if (GetSize(en_sig) != 0) - return; + return false; } if (ff.val_init == State::S1) { type = G(FF1); @@ -211,50 +268,55 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) } if (ff.has_arst) { if (arst_polarity != ff.pol_arst) - return; + return false; if (arst_sig != assign_map(ff.sig_arst)) - return; + return false; if (ff.val_arst == State::S1) { if (type == G(FF0)) - return; + return false; type = G(FF1); } else if (ff.val_arst == State::S0) { if (type == G(FF1)) - return; + return false; type = G(FF0); } } else { if (GetSize(arst_sig) != 0) - return; + return false; } if (ff.has_srst) { if (srst_polarity != ff.pol_srst) - return; + return false; if (srst_sig != assign_map(ff.sig_srst)) - return; + return false; if (ff.val_srst == State::S1) { if (type == G(FF0)) - return; + return false; type = G(FF1); } else if (ff.val_srst == State::S0) { if (type == G(FF1)) - return; + return false; type = G(FF0); } } else { if (GetSize(srst_sig) != 0) - return; + return false; } - if (keepff) - for (auto &c : ff.sig_q.chunks()) - if (c.wire != nullptr) - c.wire->attributes[ID::keep] = 1; + int gate_id = map_signal(assign_map, ff.sig_q, type, map_signal(assign_map, ff.sig_d)); + if (keepff) { + SigBit bit = ff.sig_q; + if (assign_map(bit).wire != nullptr) { + signal_list[gate_id].is_port = true; + } + if (bit.wire != nullptr) + bit.wire->attributes[ID::keep] = 1; + } - map_signal(ff.sig_q, type, map_signal(ff.sig_d)); + map_signal(assign_map, ff.sig_q, type, map_signal(assign_map, ff.sig_d)); ff.remove(); - return; + return true; } if (cell->type.in(ID($_BUF_), ID($_NOT_))) @@ -265,10 +327,10 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_a); assign_map.apply(sig_y); - map_signal(sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(sig_a)); + map_signal(assign_map, sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(assign_map, sig_a)); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) @@ -281,30 +343,30 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_b); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); if (cell->type == ID($_AND_)) - map_signal(sig_y, G(AND), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(AND), mapped_a, mapped_b); else if (cell->type == ID($_NAND_)) - map_signal(sig_y, G(NAND), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(NAND), mapped_a, mapped_b); else if (cell->type == ID($_OR_)) - map_signal(sig_y, G(OR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(OR), mapped_a, mapped_b); else if (cell->type == ID($_NOR_)) - map_signal(sig_y, G(NOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(NOR), mapped_a, mapped_b); else if (cell->type == ID($_XOR_)) - map_signal(sig_y, G(XOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(XOR), mapped_a, mapped_b); else if (cell->type == ID($_XNOR_)) - map_signal(sig_y, G(XNOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(XNOR), mapped_a, mapped_b); else if (cell->type == ID($_ANDNOT_)) - map_signal(sig_y, G(ANDNOT), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(ANDNOT), mapped_a, mapped_b); else if (cell->type == ID($_ORNOT_)) - map_signal(sig_y, G(ORNOT), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(ORNOT), mapped_a, mapped_b); else log_abort(); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_MUX_), ID($_NMUX_))) @@ -319,14 +381,14 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_s); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_s = map_signal(sig_s); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_s = map_signal(assign_map, sig_s); - map_signal(sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s); + map_signal(assign_map, sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) @@ -341,14 +403,14 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_c); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_c = map_signal(sig_c); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_c = map_signal(assign_map, sig_c); - map_signal(sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); + map_signal(assign_map, sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) @@ -365,19 +427,21 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_d); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_c = map_signal(sig_c); - int mapped_d = map_signal(sig_d); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_c = map_signal(assign_map, sig_c); + int mapped_d = map_signal(assign_map, sig_d); - map_signal(sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); + map_signal(assign_map, sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); module->remove(cell); - return; + return true; } + + return false; } -std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr) +std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire) { std::string abc_sname = abc_name.substr(1); bool isnew = false; @@ -416,7 +480,7 @@ std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullp return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1); } -void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) +void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) { if (f == nullptr) return; @@ -445,7 +509,13 @@ void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &w fprintf(f, "}\n"); } -void handle_loops() +void connect(AbcSigMap &assign_map, RTLIL::Module *module, const RTLIL::SigSig &conn) +{ + module->connect(conn); + assign_map.add(conn.first, conn.second); +} + +void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) { // http://en.wikipedia.org/wiki/Topological_sorting // (Kahn, Arthur B. (1962), "Topological sorting of large networks") @@ -550,7 +620,7 @@ void handle_loops() first_line = false; } - int id3 = map_signal(RTLIL::SigSpec(wire)); + int id3 = map_signal(assign_map, RTLIL::SigSpec(wire)); signal_list[id1].is_port = true; signal_list[id3].is_port = true; log_assert(id3 == int(in_edges_count.size())); @@ -569,7 +639,7 @@ void handle_loops() } edges[id1].swap(edges[id3]); - module->connect(RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); + connect(assign_map, module, RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); } } @@ -646,13 +716,15 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho struct abc_output_filter { + const AbcModuleState &state; bool got_cr; int escape_seq_state; std::string linebuf; std::string tempdir_name; bool show_tempdir; - abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + abc_output_filter(const AbcModuleState& state, std::string tempdir_name, bool show_tempdir) + : state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir) { got_cr = false; escape_seq_state = 0; @@ -693,8 +765,8 @@ struct abc_output_filter int pi, po; if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", - pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", - po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + pi, state.pi_map.count(pi) ? state.pi_map.at(pi).c_str() : "???", + po, state.po_map.count(po) ? state.po_map.at(po).c_str() : "???"); return; } @@ -703,20 +775,11 @@ struct abc_output_filter } }; -void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::vector &liberty_files, std::vector &genlib_files, std::string constr_file, - bool cleanup, vector lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, - std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, - const std::vector &cells, bool show_tempdir, bool sop_mode, bool abc_dress, std::vector &dont_use_cells) +void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, + bool dff_mode, std::string clk_str) { - module = current_module; map_autoidx = autoidx++; - signal_map.clear(); - signal_list.clear(); - pi_map.clear(); - po_map.clear(); - if (clk_str != "$") { clk_polarity = true; @@ -787,39 +850,39 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (dff_mode && clk_sig.empty()) log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); - std::string tempdir_name; - if (cleanup) + if (config.cleanup) tempdir_name = get_base_tmpdir() + "/"; else tempdir_name = "_tmp_"; tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX"; tempdir_name = make_temp_dir(tempdir_name); log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", - module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); + module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, config.show_tempdir).c_str()); std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", tempdir_name.c_str()); - if (!liberty_files.empty() || !genlib_files.empty()) { + if (!config.liberty_files.empty() || !config.genlib_files.empty()) { std::string dont_use_args; - for (std::string dont_use_cell : dont_use_cells) { + for (std::string dont_use_cell : config.dont_use_cells) { dont_use_args += stringf("-X \"%s\" ", dont_use_cell.c_str()); } bool first_lib = true; - for (std::string liberty_file : liberty_files) { + for (std::string liberty_file : config.liberty_files) { abc_script += stringf("read_lib %s %s -w \"%s\" ; ", dont_use_args.c_str(), first_lib ? "" : "-m", liberty_file.c_str()); first_lib = false; } - for (std::string liberty_file : genlib_files) + for (std::string liberty_file : config.genlib_files) abc_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); - if (!constr_file.empty()) - abc_script += stringf("read_constr -v \"%s\"; ", constr_file.c_str()); + if (!config.constr_file.empty()) + abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file.c_str()); } else - if (!lut_costs.empty()) + if (!config.lut_costs.empty()) abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str()); - if (!script_file.empty()) { + if (!config.script_file.empty()) { + const std::string &script_file = config.script_file; if (script_file[0] == '+') { for (size_t i = 1; i < script_file.size(); i++) if (script_file[i] == '\'') @@ -830,37 +893,38 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += script_file[i]; } else abc_script += stringf("source %s", script_file.c_str()); - } else if (!lut_costs.empty()) { + } else if (!config.lut_costs.empty()) { bool all_luts_cost_same = true; - for (int this_cost : lut_costs) - if (this_cost != lut_costs.front()) + for (int this_cost : config.lut_costs) + if (this_cost != config.lut_costs.front()) all_luts_cost_same = false; - abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; - if (all_luts_cost_same && !fast_mode) + abc_script += config.fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; + if (all_luts_cost_same && !config.fast_mode) abc_script += "; lutpack {S}"; - } else if (!liberty_files.empty() || !genlib_files.empty()) - abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); - else if (sop_mode) - abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; + } else if (!config.liberty_files.empty() || !config.genlib_files.empty()) + abc_script += config.constr_file.empty() ? + (config.fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (config.fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + else if (config.sop_mode) + abc_script += config.fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; else - abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + abc_script += config.fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; - if (script_file.empty() && !delay_target.empty()) + if (config.script_file.empty() && !config.delay_target.empty()) for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) - abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.delay_target + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{I}", pos)) - abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.sop_inputs + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{P}", pos)) - abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.sop_products + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) - abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); - if (abc_dress) + abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3); + if (config.abc_dress) abc_script += stringf("; dress \"%s/input.blif\"", tempdir_name.c_str()); abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -895,34 +959,34 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin undef_bits_lost = 0; had_init = false; + std::vector kept_cells; for (auto c : cells) - extract_cell(c, keepff); + if (!extract_cell(assign_map, module, c, config.keepff)) + kept_cells.push_back(c); if (undef_bits_lost) log("Replacing %d occurrences of constant undef bits with constant zero bits\n", undef_bits_lost); - for (auto wire : module->wires()) { - if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) - mark_port(wire); - } - - for (auto cell : module->cells()) - for (auto &port_it : cell->connections()) - mark_port(port_it.second); + // Wires with port_id > 0, ID::keep, and connections to cells outside our cell set have already + // been accounted for via AbcSigVal::is_port. Now we just need to account for + // connections to cells inside our cell set that weren't removed by extract_cell(). + for (auto cell : kept_cells) + for (auto &port_it : cell->connections()) + mark_port(assign_map, port_it.second); if (clk_sig.size() != 0) - mark_port(clk_sig); + mark_port(assign_map, clk_sig); if (en_sig.size() != 0) - mark_port(en_sig); + mark_port(assign_map, en_sig); if (arst_sig.size() != 0) - mark_port(arst_sig); + mark_port(assign_map, arst_sig); if (srst_sig.size() != 0) - mark_port(srst_sig); + mark_port(assign_map, srst_sig); - handle_loops(); + handle_loops(assign_map, module); buffer = stringf("%s/input.blif", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -1095,21 +1159,21 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_))); fclose(f); - if (!lut_costs.empty()) { + if (!config.lut_costs.empty()) { buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - for (int i = 0; i < GetSize(lut_costs); i++) - fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); + for (int i = 0; i < GetSize(config.lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, config.lut_costs.at(i)); fclose(f); } - buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); - log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, config.show_tempdir).c_str()); #ifndef YOSYS_LINK_ABC - abc_output_filter filt(tempdir_name, show_tempdir); + abc_output_filter filt(*this, tempdir_name, config.show_tempdir); int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); #else string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name.c_str()); @@ -1133,7 +1197,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin // These needs to be mutable, supposedly due to getopt char *abc_argv[5]; string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); - abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[0] = strdup(config.exe_file.c_str()); abc_argv[1] = strdup("-s"); abc_argv[2] = strdup("-f"); abc_argv[3] = strdup(tmp_script_name.c_str()); @@ -1150,199 +1214,160 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fclose(old_stdout); fclose(old_stderr); std::ifstream temp_stdouterr_r(temp_stdouterr_name); - abc_output_filter filt(tempdir_name, show_tempdir); + abc_output_filter filt(*this, tempdir_name, config.show_tempdir); for (std::string line; std::getline(temp_stdouterr_r, line); ) filt.next_line(line + "\n"); temp_stdouterr_r.close(); #endif - if (ret != 0) + if (ret != 0) { log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - - buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); - std::ifstream ifs; - ifs.open(buffer); - if (ifs.fail()) - log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - - bool builtin_lib = liberty_files.empty() && genlib_files.empty(); - RTLIL::Design *mapped_design = new RTLIL::Design; - parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, sop_mode); - - ifs.close(); - - log_header(design, "Re-integrating ABC results.\n"); - RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); - if (mapped_mod == nullptr) - log_error("ABC output file does not contain a module `netlist'.\n"); - for (auto w : mapped_mod->wires()) { - RTLIL::Wire *orig_wire = nullptr; - RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); - if (orig_wire != nullptr && orig_wire->attributes.count(ID::src)) - wire->attributes[ID::src] = orig_wire->attributes[ID::src]; - if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; - design->select(module, wire); + return; } + did_run_abc = true; + return; + } + log("Don't call ABC as there is nothing to map.\n"); +} - SigMap mapped_sigmap(mapped_mod); - FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); +void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) +{ + if (!did_run_abc) { + return; + } - dict cell_stats; - for (auto c : mapped_mod->cells()) + std::string buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); + std::ifstream ifs; + ifs.open(buffer); + if (ifs.fail()) + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); + + bool builtin_lib = config.liberty_files.empty() && config.genlib_files.empty(); + RTLIL::Design *mapped_design = new RTLIL::Design; + parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, config.sop_mode); + + ifs.close(); + + log_header(design, "Re-integrating ABC results.\n"); + RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); + if (mapped_mod == nullptr) + log_error("ABC output file does not contain a module `netlist'.\n"); + for (auto w : mapped_mod->wires()) { + RTLIL::Wire *orig_wire = nullptr; + RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); + if (orig_wire != nullptr && orig_wire->attributes.count(ID::src)) + wire->attributes[ID::src] = orig_wire->attributes[ID::src]; + if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; + design->select(module, wire); + } + + SigMap mapped_sigmap(mapped_mod); + FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); + + dict cell_stats; + for (auto c : mapped_mod->cells()) + { + if (builtin_lib) { - if (builtin_lib) - { - cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type.in(ID(ZERO), ID(ONE))) { - RTLIL::SigSig conn; - RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); - conn.first = module->wire(name_y); - conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); - module->connect(conn); - continue; - } - if (c->type == ID(BUF)) { - RTLIL::SigSig conn; - RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); - RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); - conn.first = module->wire(name_y); - conn.second = module->wire(name_a); - module->connect(conn); - continue; - } - if (c->type == ID(NOT)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(MUX), ID(NMUX))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(MUX4)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(MUX8)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(MUX16)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, - ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(AOI3), ID(OAI3))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(AOI4), ID(OAI4))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(DFF)) { - log_assert(clk_sig.size() == 1); - FfData ff(module, &initvals, remap_name(c->name)); - ff.width = 1; - ff.is_fine = true; - ff.has_clk = true; - ff.pol_clk = clk_polarity; - ff.sig_clk = clk_sig; - if (en_sig.size() != 0) { - log_assert(en_sig.size() == 1); - ff.has_ce = true; - ff.pol_ce = en_polarity; - ff.sig_ce = en_sig; - } - RTLIL::Const init = mapped_initvals(c->getPort(ID::Q)); - if (had_init) - ff.val_init = init; - else - ff.val_init = State::Sx; - if (arst_sig.size() != 0) { - log_assert(arst_sig.size() == 1); - ff.has_arst = true; - ff.pol_arst = arst_polarity; - ff.sig_arst = arst_sig; - ff.val_arst = init; - } - if (srst_sig.size() != 0) { - log_assert(srst_sig.size() == 1); - ff.has_srst = true; - ff.pol_srst = srst_polarity; - ff.sig_srst = srst_sig; - ff.val_srst = init; - } - ff.sig_d = module->wire(remap_name(c->getPort(ID::D).as_wire()->name)); - ff.sig_q = module->wire(remap_name(c->getPort(ID::Q).as_wire()->name)); - RTLIL::Cell *cell = ff.emit(); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - design->select(module, cell); - continue; - } - } - else - cell_stats[RTLIL::unescape_id(c->type)]++; - - if (c->type.in(ID(_const0_), ID(_const1_))) { + cell_stats[RTLIL::unescape_id(c->type)]++; + if (c->type.in(ID(ZERO), ID(ONE))) { RTLIL::SigSig conn; - conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); - conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); - module->connect(conn); + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + conn.first = module->wire(name_y); + conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); + connect(assign_map, module, conn); continue; } - - if (c->type == ID(_dff_)) { + if (c->type == ID(BUF)) { + RTLIL::SigSig conn; + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); + conn.first = module->wire(name_y); + conn.second = module->wire(name_a); + connect(assign_map, module, conn); + continue; + } + if (c->type == ID(NOT)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(MUX), ID(NMUX))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX4)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX8)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX16)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, + ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(AOI3), ID(OAI3))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(AOI4), ID(OAI4))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(DFF)) { log_assert(clk_sig.size() == 1); FfData ff(module, &initvals, remap_name(c->name)); ff.width = 1; @@ -1352,6 +1377,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin ff.sig_clk = clk_sig; if (en_sig.size() != 0) { log_assert(en_sig.size() == 1); + ff.has_ce = true; ff.pol_ce = en_polarity; ff.sig_ce = en_sig; } @@ -1362,12 +1388,14 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin ff.val_init = State::Sx; if (arst_sig.size() != 0) { log_assert(arst_sig.size() == 1); + ff.has_arst = true; ff.pol_arst = arst_polarity; ff.sig_arst = arst_sig; ff.val_arst = init; } if (srst_sig.size() != 0) { log_assert(srst_sig.size() == 1); + ff.has_srst = true; ff.pol_srst = srst_polarity; ff.sig_srst = srst_sig; ff.val_srst = init; @@ -1379,70 +1407,117 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } + } + else + cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { - SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); - SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); - module->connect(my_y, my_a); - continue; + if (c->type.in(ID(_const0_), ID(_const1_))) { + RTLIL::SigSig conn; + conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); + conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); + connect(assign_map, module, conn); + continue; + } + + if (c->type == ID(_dff_)) { + log_assert(clk_sig.size() == 1); + FfData ff(module, &initvals, remap_name(c->name)); + ff.width = 1; + ff.is_fine = true; + ff.has_clk = true; + ff.pol_clk = clk_polarity; + ff.sig_clk = clk_sig; + if (en_sig.size() != 0) { + log_assert(en_sig.size() == 1); + ff.pol_ce = en_polarity; + ff.sig_ce = en_sig; } - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + RTLIL::Const init = mapped_initvals(c->getPort(ID::Q)); + if (had_init) + ff.val_init = init; + else + ff.val_init = State::Sx; + if (arst_sig.size() != 0) { + log_assert(arst_sig.size() == 1); + ff.pol_arst = arst_polarity; + ff.sig_arst = arst_sig; + ff.val_arst = init; + } + if (srst_sig.size() != 0) { + log_assert(srst_sig.size() == 1); + ff.pol_srst = srst_polarity; + ff.sig_srst = srst_sig; + ff.val_srst = init; + } + ff.sig_d = module->wire(remap_name(c->getPort(ID::D).as_wire()->name)); + ff.sig_q = module->wire(remap_name(c->getPort(ID::Q).as_wire()->name)); + RTLIL::Cell *cell = ff.emit(); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->parameters = c->parameters; - for (auto &conn : c->connections()) { - RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks()) { - if (c.width == 0) - continue; - log_assert(c.width == 1); - newsig.append(module->wire(remap_name(c.wire->name))); - } - cell->setPort(conn.first, newsig); - } design->select(module, cell); + continue; } - for (auto conn : mapped_mod->connections()) { - if (!conn.first.is_fully_const()) - conn.first = module->wire(remap_name(conn.first.as_wire()->name)); - if (!conn.second.is_fully_const()) - conn.second = module->wire(remap_name(conn.second.as_wire()->name)); - module->connect(conn); + if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { + SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); + SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); + connect(assign_map, module, RTLIL::SigSig(my_a, my_y)); + continue; } - 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; - for (auto &si : signal_list) - if (si.is_port) { - char buffer[100]; - snprintf(buffer, 100, "\\ys__n%d", si.id); - RTLIL::SigSig conn; - if (si.type != G(NONE)) { - conn.first = si.bit; - conn.second = module->wire(remap_name(buffer)); - out_wires++; - } else { - conn.first = module->wire(remap_name(buffer)); - conn.second = si.bit; - in_wires++; - } - module->connect(conn); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto &c : conn.second.chunks()) { + if (c.width == 0) + continue; + log_assert(c.width == 1); + newsig.append(module->wire(remap_name(c.wire->name))); } - log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); - log("ABC RESULTS: input signals: %8d\n", in_wires); - log("ABC RESULTS: output signals: %8d\n", out_wires); - - delete mapped_design; - } - else - { - log("Don't call ABC as there is nothing to map.\n"); + cell->setPort(conn.first, newsig); + } + design->select(module, cell); } - if (cleanup) + for (auto conn : mapped_mod->connections()) { + if (!conn.first.is_fully_const()) + conn.first = module->wire(remap_name(conn.first.as_wire()->name)); + if (!conn.second.is_fully_const()) + conn.second = module->wire(remap_name(conn.second.as_wire()->name)); + connect(assign_map, module, 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; + for (auto &si : signal_list) + if (si.is_port) { + char buffer[100]; + snprintf(buffer, 100, "\\ys__n%d", si.id); + RTLIL::SigSig conn; + if (si.type != G(NONE)) { + conn.first = si.bit; + conn.second = module->wire(remap_name(buffer)); + out_wires++; + } else { + conn.first = module->wire(remap_name(buffer)); + conn.second = si.bit; + in_wires++; + } + connect(assign_map, module, conn); + } + log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + delete mapped_design; +} + +void AbcModuleState::finish() +{ + if (config.cleanup) { log("Removing temp directory.\n"); remove_directory(tempdir_name); @@ -1451,6 +1526,47 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_pop(); } +// For every signal that connects cells from different sets, or a cell in a set to a cell not in any set, +// mark it as a port in `assign_map`. +void assign_cell_connection_ports(RTLIL::Module *module, const std::vector *> &cell_sets, + AbcSigMap &assign_map) +{ + pool cells_in_no_set; + for (RTLIL::Cell *cell : module->cells()) { + cells_in_no_set.insert(cell); + } + // For every canonical signal in `assign_map`, the index of the set it is connected to, + // or -1 if it connects a cell in one set to a cell in another set or not in any set. + dict signal_cell_set; + for (int i = 0; i < int(cell_sets.size()); ++i) { + for (RTLIL::Cell *cell : *cell_sets[i]) { + cells_in_no_set.erase(cell); + for (auto &port_it : cell->connections()) { + for (SigBit bit : port_it.second) { + assign_map.apply(bit); + auto it = signal_cell_set.find(bit); + if (it == signal_cell_set.end()) + signal_cell_set[bit] = i; + else if (it->second >= 0 && it->second != i) { + it->second = -1; + assign_map.addVal(bit, AbcSigVal(true)); + } + } + } + } + } + for (RTLIL::Cell *cell : cells_in_no_set) { + for (auto &port_it : cell->connections()) { + for (SigBit bit : port_it.second) { + assign_map.apply(bit); + auto it = signal_cell_set.find(bit); + if (it != signal_cell_set.end() && it->second >= 0) + assign_map.addVal(bit, AbcSigVal(true)); + } + } + } +} + struct AbcPass : public Pass { AbcPass() : Pass("abc", "use ABC for technology mapping") { } void help() override @@ -1652,71 +1768,52 @@ struct AbcPass : public Pass { log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); - assign_map.clear(); - signal_list.clear(); - signal_map.clear(); - initvals.clear(); - pi_map.clear(); - po_map.clear(); - - std::string exe_file = yosys_abc_executable; - std::string script_file, default_liberty_file, constr_file, clk_str; - std::vector liberty_files, genlib_files, dont_use_cells; - std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; - bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; - bool show_tempdir = false, sop_mode = false; - bool abc_dress = false; - vector lut_costs; - markgroups = false; - - map_mux4 = false; - map_mux8 = false; - map_mux16 = false; - enabled_gates.clear(); - cmos_cost = false; + AbcConfig config; // get arguments from scratchpad first, then override by command arguments std::string lut_arg, luts_arg, g_arg; - exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */); - script_file = design->scratchpad_get_string("abc.script", script_file); - default_liberty_file = design->scratchpad_get_string("abc.liberty", default_liberty_file); - constr_file = design->scratchpad_get_string("abc.constr", constr_file); + config.exe_file = design->scratchpad_get_string("abc.exe", yosys_abc_executable /* inherit default value if not set */); + config.script_file = design->scratchpad_get_string("abc.script", ""); + std::string default_liberty_file = design->scratchpad_get_string("abc.liberty", ""); + config.constr_file = design->scratchpad_get_string("abc.constr", ""); if (design->scratchpad.count("abc.D")) { - delay_target = "-D " + design->scratchpad_get_string("abc.D"); + config.delay_target = "-D " + design->scratchpad_get_string("abc.D"); } if (design->scratchpad.count("abc.I")) { - sop_inputs = "-I " + design->scratchpad_get_string("abc.I"); + config.sop_inputs = "-I " + design->scratchpad_get_string("abc.I"); } if (design->scratchpad.count("abc.P")) { - sop_products = "-P " + design->scratchpad_get_string("abc.P"); + config.sop_products = "-P " + design->scratchpad_get_string("abc.P"); } if (design->scratchpad.count("abc.S")) { - lutin_shared = "-S " + design->scratchpad_get_string("abc.S"); + config.lutin_shared = "-S " + design->scratchpad_get_string("abc.S"); + } else { + config.lutin_shared = "-S 1"; } lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); - sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode); + config.sop_mode = design->scratchpad_get_bool("abc.sop", false); map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4); map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8); map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16); - abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress); + config.abc_dress = design->scratchpad_get_bool("abc.dress", false); g_arg = design->scratchpad_get_string("abc.g", g_arg); - fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode); - dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode); + config.fast_mode = design->scratchpad_get_bool("abc.fast", false); + bool dff_mode = design->scratchpad_get_bool("abc.dff", false); + std::string clk_str; if (design->scratchpad.count("abc.clk")) { clk_str = design->scratchpad_get_string("abc.clk"); dff_mode = true; } - keepff = design->scratchpad_get_bool("abc.keepff", keepff); - cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup); - keepff = design->scratchpad_get_bool("abc.keepff", keepff); - show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir); + config.keepff = design->scratchpad_get_bool("abc.keepff", false); + config.cleanup = !design->scratchpad_get_bool("abc.nocleanup", false); + config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups); if (design->scratchpad_get_bool("abc.debug")) { - cleanup = false; - show_tempdir = true; + config.cleanup = false; + config.show_tempdir = true; } size_t argidx, g_argidx = -1; @@ -1733,43 +1830,43 @@ struct AbcPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exe" && argidx+1 < args.size()) { - exe_file = args[++argidx]; + config.exe_file = args[++argidx]; continue; } if (arg == "-script" && argidx+1 < args.size()) { - script_file = args[++argidx]; + config.script_file = args[++argidx]; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { - liberty_files.push_back(args[++argidx]); + config.liberty_files.push_back(args[++argidx]); continue; } if (arg == "-dont_use" && argidx+1 < args.size()) { - dont_use_cells.push_back(args[++argidx]); + config.dont_use_cells.push_back(args[++argidx]); continue; } if (arg == "-genlib" && argidx+1 < args.size()) { - genlib_files.push_back(args[++argidx]); + config.genlib_files.push_back(args[++argidx]); continue; } if (arg == "-constr" && argidx+1 < args.size()) { - constr_file = args[++argidx]; + config.constr_file = args[++argidx]; continue; } if (arg == "-D" && argidx+1 < args.size()) { - delay_target = "-D " + args[++argidx]; + config.delay_target = "-D " + args[++argidx]; continue; } if (arg == "-I" && argidx+1 < args.size()) { - sop_inputs = "-I " + args[++argidx]; + config.sop_inputs = "-I " + args[++argidx]; continue; } if (arg == "-P" && argidx+1 < args.size()) { - sop_products = "-P " + args[++argidx]; + config.sop_products = "-P " + args[++argidx]; continue; } if (arg == "-S" && argidx+1 < args.size()) { - lutin_shared = "-S " + args[++argidx]; + config.lutin_shared = "-S " + args[++argidx]; continue; } if (arg == "-lut" && argidx+1 < args.size()) { @@ -1781,7 +1878,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-sop") { - sop_mode = true; + config.sop_mode = true; continue; } if (arg == "-mux4") { @@ -1797,7 +1894,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-dress") { - abc_dress = true; + config.abc_dress = true; continue; } if (arg == "-g" && argidx+1 < args.size()) { @@ -1809,7 +1906,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-fast") { - fast_mode = true; + config.fast_mode = true; continue; } if (arg == "-dff") { @@ -1822,15 +1919,15 @@ struct AbcPass : public Pass { continue; } if (arg == "-keepff") { - keepff = true; + config.keepff = true; continue; } if (arg == "-nocleanup") { - cleanup = false; + config.cleanup = false; continue; } if (arg == "-showtmp") { - show_tempdir = true; + config.show_tempdir = true; continue; } if (arg == "-markgroups") { @@ -1841,25 +1938,25 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); - if (genlib_files.empty() && liberty_files.empty() && !default_liberty_file.empty()) - liberty_files.push_back(default_liberty_file); + if (config.genlib_files.empty() && config.liberty_files.empty() && !default_liberty_file.empty()) + config.liberty_files.push_back(default_liberty_file); - rewrite_filename(script_file); - if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') - script_file = std::string(pwd) + "/" + script_file; - for (int i = 0; i < GetSize(liberty_files); i++) { - rewrite_filename(liberty_files[i]); - if (!liberty_files[i].empty() && !is_absolute_path(liberty_files[i])) - liberty_files[i] = std::string(pwd) + "/" + liberty_files[i]; + rewrite_filename(config.script_file); + if (!config.script_file.empty() && !is_absolute_path(config.script_file) && config.script_file[0] != '+') + config.script_file = std::string(pwd) + "/" + config.script_file; + for (int i = 0; i < GetSize(config.liberty_files); i++) { + rewrite_filename(config.liberty_files[i]); + if (!config.liberty_files[i].empty() && !is_absolute_path(config.liberty_files[i])) + config.liberty_files[i] = std::string(pwd) + "/" + config.liberty_files[i]; } - for (int i = 0; i < GetSize(genlib_files); i++) { - rewrite_filename(genlib_files[i]); - if (!genlib_files[i].empty() && !is_absolute_path(genlib_files[i])) - genlib_files[i] = std::string(pwd) + "/" + genlib_files[i]; + for (int i = 0; i < GetSize(config.genlib_files); i++) { + rewrite_filename(config.genlib_files[i]); + if (!config.genlib_files[i].empty() && !is_absolute_path(config.genlib_files[i])) + config.genlib_files[i] = std::string(pwd) + "/" + config.genlib_files[i]; } - rewrite_filename(constr_file); - if (!constr_file.empty() && !is_absolute_path(constr_file)) - constr_file = std::string(pwd) + "/" + constr_file; + rewrite_filename(config.constr_file); + if (!config.constr_file.empty() && !is_absolute_path(config.constr_file)) + config.constr_file = std::string(pwd) + "/" + config.constr_file; // handle -lut argument if (!lut_arg.empty()) { @@ -1872,24 +1969,24 @@ struct AbcPass : public Pass { lut_mode = atoi(lut_arg.c_str()); lut_mode2 = lut_mode; } - lut_costs.clear(); + config.lut_costs.clear(); for (int i = 0; i < lut_mode; i++) - lut_costs.push_back(1); + config.lut_costs.push_back(1); for (int i = lut_mode; i < lut_mode2; i++) - lut_costs.push_back(2 << (i - lut_mode)); + config.lut_costs.push_back(2 << (i - lut_mode)); } //handle -luts argument if (!luts_arg.empty()){ - lut_costs.clear(); + config.lut_costs.clear(); for (auto &tok : split_tokens(luts_arg, ",")) { auto parts = split_tokens(tok, ":"); - if (GetSize(parts) == 0 && !lut_costs.empty()) - lut_costs.push_back(lut_costs.back()); + if (GetSize(parts) == 0 && !config.lut_costs.empty()) + config.lut_costs.push_back(config.lut_costs.back()); else if (GetSize(parts) == 1) - lut_costs.push_back(atoi(parts.at(0).c_str())); + config.lut_costs.push_back(atoi(parts.at(0).c_str())); else if (GetSize(parts) == 2) - while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str())) - lut_costs.push_back(atoi(parts.at(1).c_str())); + while (GetSize(config.lut_costs) < std::atoi(parts.at(0).c_str())) + config.lut_costs.push_back(atoi(parts.at(1).c_str())); else log_cmd_error("Invalid -luts syntax.\n"); } @@ -2020,9 +2117,9 @@ struct AbcPass : public Pass { } } - if (!lut_costs.empty() && !(liberty_files.empty() && genlib_files.empty())) + if (!config.lut_costs.empty() && !(config.liberty_files.empty() && config.genlib_files.empty())) log_cmd_error("Got -lut and -liberty/-genlib! These two options are exclusive.\n"); - if (!constr_file.empty() && (liberty_files.empty() && genlib_files.empty())) + if (!config.constr_file.empty() && (config.liberty_files.empty() && config.genlib_files.empty())) log_cmd_error("Got -constr but no -liberty/-genlib!\n"); if (enabled_gates.empty()) { @@ -2049,12 +2146,26 @@ struct AbcPass : public Pass { continue; } + AbcSigMap assign_map; assign_map.set(mod); + // Create an FfInitVals and use it for all ABC runs. FfInitVals only cares about + // wires with the ID::init attribute and we don't add or remove any such wires + // in this pass. + FfInitVals initvals; initvals.set(&assign_map, mod); + for (auto wire : mod->wires()) + if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) + assign_map.addVal(SigSpec(wire), AbcSigVal(true)); + if (!dff_mode || !clk_str.empty()) { - abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, - delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress, dont_use_cells); + std::vector cells = mod->selected_cells(); + assign_cell_connection_ports(mod, {&cells}, assign_map); + + AbcModuleState state(config, initvals); + state.abc_module(design, mod, assign_map, cells, dff_mode, clk_str); + state.extract(assign_map, design, mod); + state.finish(); continue; } @@ -2206,28 +2317,28 @@ struct AbcPass : public Pass { std::get<4>(it.first) ? "" : "!", log_signal(std::get<5>(it.first)), std::get<6>(it.first) ? "" : "!", log_signal(std::get<7>(it.first))); + { + std::vector*> cell_sets; + for (auto &it : assigned_cells) + cell_sets.push_back(&it.second); + assign_cell_connection_ports(mod, cell_sets, assign_map); + } for (auto &it : assigned_cells) { - clk_polarity = std::get<0>(it.first); - clk_sig = assign_map(std::get<1>(it.first)); - en_polarity = std::get<2>(it.first); - en_sig = assign_map(std::get<3>(it.first)); - arst_polarity = std::get<4>(it.first); - arst_sig = assign_map(std::get<5>(it.first)); - srst_polarity = std::get<6>(it.first); - srst_sig = assign_map(std::get<7>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", - keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress, dont_use_cells); - assign_map.set(mod); + AbcModuleState state(config, initvals); + state.clk_polarity = std::get<0>(it.first); + state.clk_sig = assign_map(std::get<1>(it.first)); + state.en_polarity = std::get<2>(it.first); + state.en_sig = assign_map(std::get<3>(it.first)); + state.arst_polarity = std::get<4>(it.first); + state.arst_sig = assign_map(std::get<5>(it.first)); + state.srst_polarity = std::get<6>(it.first); + state.srst_sig = assign_map(std::get<7>(it.first)); + state.abc_module(design, mod, assign_map, it.second, !state.clk_sig.empty(), "$"); + state.extract(assign_map, design, mod); + state.finish(); } } - assign_map.clear(); - signal_list.clear(); - signal_map.clear(); - initvals.clear(); - pi_map.clear(); - po_map.clear(); - log_pop(); } } AbcPass; diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index b6385766c..4285611f7 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -71,6 +71,29 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::Y, wire); } + if (cell_type.in(ID($_MUX_), ID($_NMUX_))) + { + wire = module->addWire(ID::A); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::B); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::B, wire); + + wire = module->addWire(ID::S); + wire->width = 1; + wire->port_input = true; + cell->setPort(ID::S, wire); + + wire = module->addWire(ID::Y); + wire->width = 1; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (cell_type == ID($bmux)) { int width = 1 + xorshift32(8 * bloat_factor); @@ -167,7 +190,7 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::CO, wire); } - if (cell_type == ID($macc)) + if (cell_type == ID($macc_v2)) { Macc macc; int width = 1 + xorshift32(8 * bloat_factor); @@ -201,6 +224,7 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce this_term.do_subtract = xorshift32(2) == 1; macc.terms.push_back(this_term); } + // Macc::to_cell sets the input ports macc.to_cell(cell); @@ -208,12 +232,6 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce wire->width = width; wire->port_output = true; cell->setPort(ID::Y, wire); - - // 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)) @@ -273,14 +291,19 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire(ID::A); - wire->width = 1 + xorshift32(8 * bloat_factor); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::A, wire); } if (cell_type_flags.find('B') != std::string::npos) { wire = module->addWire(ID::B); - if (cell_type_flags.find('h') != std::string::npos) + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else if (cell_type_flags.find('h') != std::string::npos) wire->width = 1 + xorshift32(6 * bloat_factor); else wire->width = 1 + xorshift32(8 * bloat_factor); @@ -288,6 +311,26 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::B, wire); } + if (cell_type_flags.find('C') != std::string::npos) { + wire = module->addWire(ID::C); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); + wire->port_input = true; + cell->setPort(ID::C, wire); + } + + if (cell_type_flags.find('D') != std::string::npos) { + wire = module->addWire(ID::D); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); + wire->port_input = true; + cell->setPort(ID::D, wire); + } + if (cell_type_flags.find('S') != std::string::npos && xorshift32(2)) { if (cell_type_flags.find('A') != std::string::npos) cell->parameters[ID::A_SIGNED] = true; @@ -304,7 +347,10 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce if (cell_type_flags.find('Y') != std::string::npos) { wire = module->addWire(ID::Y); - wire->width = 1 + xorshift32(8 * bloat_factor); + if (cell_type_flags.find('b') != std::string::npos) + wire->width = 1; + else + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_output = true; cell->setPort(ID::Y, wire); } @@ -345,6 +391,58 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::CO, wire); } + if (cell_type == ID($slice)) + { + int a_size = GetSize(cell->getPort(ID::A)); + int y_size = 1; + if (a_size > 1) + y_size += (xorshift32(8 * bloat_factor) % (a_size - 1)); + wire = module->addWire(ID::Y); + wire->width = y_size; + wire->port_output = true; + cell->setPort(ID::Y, wire); + if (a_size > y_size) + cell->setParam(ID::OFFSET, (xorshift32(8 * bloat_factor) % (a_size - y_size))); + else + cell->setParam(ID::OFFSET, 0); + } + + if (cell_type == ID($concat)) + { + wire = module->addWire(ID::Y); + wire->width = GetSize(cell->getPort(ID::A)) + GetSize(cell->getPort(ID::B)); + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + + if (cell_type == ID($buf)) + { + wire = module->addWire(ID::Y); + wire->width = GetSize(cell->getPort(ID::A)); + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + + if (cell_type.in(ID($bwmux), ID($bweqx))) + { + int a_size = GetSize(cell->getPort(ID::A)); + wire = module->addWire(ID::B); + wire->width = a_size; + wire->port_input = true; + cell->setPort(ID::B, wire); + if (cell_type == ID($bwmux)) + { + wire = module->addWire(ID::S); + wire->width = a_size; + wire->port_input = true; + cell->setPort(ID::S, wire); + } + wire = module->addWire(ID::Y); + wire->width = a_size; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (constmode) { auto conn_list = cell->connections(); @@ -884,6 +982,9 @@ struct TestCellPass : public Pass { cell_types[ID($not)] = "ASY"; cell_types[ID($pos)] = "ASY"; cell_types[ID($neg)] = "ASY"; + // $buf is unsupported with techmap -assert + if (techmap_cmd.compare("techmap -assert") != 0) + cell_types[ID($buf)] = "A"; cell_types[ID($and)] = "ABSY"; cell_types[ID($or)] = "ABSY"; @@ -907,8 +1008,14 @@ struct TestCellPass : public Pass { cell_types[ID($le)] = "ABSY"; cell_types[ID($eq)] = "ABSY"; cell_types[ID($ne)] = "ABSY"; - // cell_types[ID($eqx)] = "ABSY"; - // cell_types[ID($nex)] = "ABSY"; + // $eqx, $nex, and $bweqx don't work in sat, and are unsupported with + // 'techmap -assert' + if (nosat && techmap_cmd.compare("techmap -assert") != 0) + { + cell_types[ID($eqx)] = "ABSY"; + cell_types[ID($nex)] = "ABSY"; + cell_types[ID($bweqx)] = "A"; + } cell_types[ID($ge)] = "ABSY"; cell_types[ID($gt)] = "ABSY"; @@ -919,7 +1026,10 @@ struct TestCellPass : public Pass { cell_types[ID($mod)] = "ABSY"; cell_types[ID($divfloor)] = "ABSY"; cell_types[ID($modfloor)] = "ABSY"; - // cell_types[ID($pow)] = "ABsY"; + // $pow doesnt work in sat, not supported with 'techmap -assert', and only + // only partially supported with '-simlib' + if (nosat && techmap_cmd.compare("aigmap") == 0) + cell_types[ID($pow)] = "ABsY"; cell_types[ID($logic_not)] = "ASY"; cell_types[ID($logic_and)] = "ABSY"; @@ -928,20 +1038,43 @@ struct TestCellPass : public Pass { cell_types[ID($mux)] = "*"; cell_types[ID($bmux)] = "*"; cell_types[ID($demux)] = "*"; - if (edges) { + // $pmux doesn't work in sat, and is not supported with 'techmap -assert' or + // '-simlib' + if (nosat && techmap_cmd.compare("aigmap") == 0) cell_types[ID($pmux)] = "*"; - } + cell_types[ID($bwmux)] = "A"; - // cell_types[ID($slice)] = "A"; - // cell_types[ID($concat)] = "A"; + cell_types[ID($slice)] = "A"; + cell_types[ID($concat)] = "AB"; cell_types[ID($lut)] = "*"; cell_types[ID($sop)] = "*"; cell_types[ID($alu)] = "ABSY"; cell_types[ID($lcu)] = "*"; - cell_types[ID($macc)] = "*"; + cell_types[ID($macc_v2)] = "*"; cell_types[ID($fa)] = "*"; + cell_types[ID($_BUF_)] = "AYb"; + cell_types[ID($_NOT_)] = "AYb"; + cell_types[ID($_AND_)] = "ABYb"; + cell_types[ID($_NAND_)] = "ABYb"; + cell_types[ID($_OR_)] = "ABYb"; + cell_types[ID($_NOR_)] = "ABYb"; + cell_types[ID($_XOR_)] = "ABYb"; + cell_types[ID($_XNOR_)] = "ABYb"; + cell_types[ID($_ANDNOT_)] = "ABYb"; + cell_types[ID($_ORNOT_)] = "ABYb"; + cell_types[ID($_MUX_)] = "*"; + cell_types[ID($_NMUX_)] = "*"; + // wide $_MUX_ cells are not yet implemented + // cell_types[ID($_MUX4_)] = "*"; + // cell_types[ID($_MUX8_)] = "*"; + // cell_types[ID($_MUX16_)] = "*"; + cell_types[ID($_AOI3_)] = "ABCYb"; + cell_types[ID($_OAI3_)] = "ABCYb"; + cell_types[ID($_AOI4_)] = "ABCDYb"; + cell_types[ID($_OAI4_)] = "ABCDYb"; + for (; argidx < GetSize(args); argidx++) { if (args[argidx].rfind("-", 0) == 0) diff --git a/setup.py b/setup.py index a199530d0..4a6ef5af9 100644 --- a/setup.py +++ b/setup.py @@ -51,16 +51,27 @@ class libyosys_so_ext(Extension): ] def custom_build(self, bext: build_ext): + make_flags_split = shlex.split(os.getenv("makeFlags", "")) + # abc linking takes a lot of memory, best get it out of the way first + bext.spawn( + [ + "make", + f"-j{os.cpu_count() or 1}", + "yosys-abc", + *make_flags_split, + *self.args, + ] + ) + # build libyosys and share with abc out of the way bext.spawn( [ "make", f"-j{os.cpu_count() or 1}", self.name, - "yosys-abc", "share", + *make_flags_split, + *self.args, ] - + shlex.split(os.getenv("makeFlags", "")) - + self.args ) build_path = os.path.dirname(os.path.dirname(bext.get_ext_fullpath(self.name))) pyosys_path = os.path.join(build_path, "pyosys") @@ -85,6 +96,7 @@ class libyosys_so_ext(Extension): shutil.copytree("share", share_target) + class custom_build_ext(build_ext): def build_extension(self, ext) -> None: if not hasattr(ext, "custom_build"): @@ -100,8 +112,8 @@ setup( long_description=open(os.path.join(__dir__, "README.md")).read(), long_description_content_type="text/markdown", install_requires=["wheel", "setuptools"], + license="MIT", classifiers=[ - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Intended Audience :: Developers", "Operating System :: POSIX :: Linux", diff --git a/techlibs/lattice/cells_bb_ecp5.v b/techlibs/lattice/cells_bb_ecp5.v index fc22495e2..20a8134c3 100644 --- a/techlibs/lattice/cells_bb_ecp5.v +++ b/techlibs/lattice/cells_bb_ecp5.v @@ -19,6 +19,8 @@ endmodule (* blackbox *) module DP16KD (...); + parameter CLKAMUX = "CLKA"; + parameter CLKBMUX = "CLKB"; parameter DATA_WIDTH_A = 18; parameter DATA_WIDTH_B = 18; parameter REGMODE_A = "NOREG"; @@ -215,6 +217,8 @@ endmodule (* blackbox *) module PDPW16KD (...); + parameter CLKRMUX = "CLKR"; + parameter CLKWMUX = "CLKW"; parameter DATA_WIDTH_W = 36; parameter DATA_WIDTH_R = 36; parameter GSR = "ENABLED"; diff --git a/techlibs/lattice/cells_xtra.py b/techlibs/lattice/cells_xtra.py index c17281cc7..5531fd2b2 100644 --- a/techlibs/lattice/cells_xtra.py +++ b/techlibs/lattice/cells_xtra.py @@ -11,10 +11,11 @@ import re class Cell: - def __init__(self, name, keep=False, port_attrs={}): + def __init__(self, name, keep=False, port_attrs={}, extra_params={}): self.name = name self.keep = keep self.port_attrs = port_attrs + self.extra_params = extra_params self.found = False class State(Enum): @@ -120,8 +121,18 @@ devices = [ #Cell("XOR3"), #Cell("XOR4"), #Cell("XOR5"), - Cell("DP16KD"), - Cell("PDPW16KD"), + Cell("DP16KD", extra_params={ + # Optional clock inverters, present in prjtrellis data but + # not in Diamond bb models. + "CLKAMUX": "CLKA", + "CLKBMUX": "CLKB", + }), + Cell("PDPW16KD", extra_params={ + # Optional clock inverters, present in prjtrellis data but + # not in Diamond bb models. + "CLKWMUX": "CLKW", + "CLKRMUX": "CLKR", + }), #Cell("DPR16X4C"), #Cell("SPR16X4C"), #Cell("LVDSOB"), @@ -795,6 +806,10 @@ def xtract_cells_decl(device, cells, dirs, outf): rng = None module_ports.append((kind, rng, port)) elif l.startswith('parameter ') and state == State.IN_MODULE: + if cell.extra_params: + for name, default in sorted(cell.extra_params.items()): + outf.write(' parameter {} = "{}";\n'.format(name, default)) + cell.extra_params = None l = l.strip() if l.endswith((';', ',')): l = l[:-1] diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 07ec769b5..c9b8eb289 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -78,7 +78,7 @@ struct SynthQuickLogicPass : public ScriptPass { } string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path; - bool abc9, inferAdder, nobram, bramTypes, dsp, ioff; + bool abc9, inferAdder, nobram, bramTypes, dsp, ioff, flatten; void clear_flags() override { @@ -95,6 +95,7 @@ struct SynthQuickLogicPass : public ScriptPass { lib_path = "+/quicklogic/"; dsp = true; ioff = true; + flatten = true; } void set_scratchpad_defaults(RTLIL::Design *design) { @@ -163,6 +164,10 @@ struct SynthQuickLogicPass : public ScriptPass { ioff = false; continue; } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } break; } extra_args(args, argidx, design); @@ -207,7 +212,8 @@ struct SynthQuickLogicPass : public ScriptPass { if (check_label("prepare")) { run("proc"); - run("flatten"); + if (flatten) + run("flatten", "(unless -noflatten)"); if (help_mode || family == "pp3") { run("tribuf -logic", " (for pp3)"); } diff --git a/tests/opt/opt_dff-simplify.il b/tests/opt/opt_dff-simplify.il new file mode 100644 index 000000000..3b86e9e35 --- /dev/null +++ b/tests/opt/opt_dff-simplify.il @@ -0,0 +1,648 @@ +# Generated by Yosys 0.53+24 (git sha1 ab636979e, sccache clang++ 19.1.7 -fPIC -O3 -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address,undefined) +autoidx 171528 +attribute \hdlname "csr_regfile_0000000000000000_1" +module \csr_regfile_0000000000000000_1 + wire $delete_wire$169641 + wire $delete_wire$169642 + wire $delete_wire$169643 + wire $delete_wire$169644 + wire $delete_wire$169645 + wire $delete_wire$169646 + wire $delete_wire$169647 + wire $delete_wire$169648 + wire $delete_wire$169649 + wire $delete_wire$169650 + wire $delete_wire$169651 + wire $delete_wire$169652 + wire $delete_wire$169653 + wire $delete_wire$169654 + wire $delete_wire$169655 + wire $delete_wire$169656 + wire $delete_wire$169657 + wire $delete_wire$169658 + wire $delete_wire$169659 + wire $delete_wire$169660 + wire $delete_wire$169661 + wire $delete_wire$169662 + wire $delete_wire$169663 + wire $delete_wire$169664 + wire $delete_wire$169665 + wire $delete_wire$169666 + wire $delete_wire$169667 + wire $delete_wire$169668 + wire $delete_wire$169669 + wire $delete_wire$169670 + wire $delete_wire$169671 + wire $delete_wire$169672 + wire $delete_wire$169673 + wire $delete_wire$169674 + wire $delete_wire$169675 + wire $delete_wire$169676 + wire $delete_wire$169677 + wire $delete_wire$169678 + wire $delete_wire$169679 + wire $delete_wire$169680 + wire $delete_wire$169681 + wire $delete_wire$169682 + wire $delete_wire$169683 + wire $delete_wire$169684 + wire $delete_wire$169685 + wire $delete_wire$169686 + wire $delete_wire$169687 + wire $delete_wire$169688 + wire $delete_wire$169689 + wire $delete_wire$169690 + wire $delete_wire$169691 + wire $delete_wire$169692 + wire $delete_wire$169693 + wire $delete_wire$169694 + wire $delete_wire$169695 + wire $delete_wire$169696 + wire $delete_wire$169697 + wire $delete_wire$169698 + wire $delete_wire$169699 + wire $delete_wire$169700 + wire $delete_wire$169701 + wire $delete_wire$169702 + wire $delete_wire$169703 + wire $delete_wire$169704 + wire $delete_wire$169705 + wire $delete_wire$169706 + wire $delete_wire$169707 + wire $delete_wire$169708 + wire $delete_wire$169709 + wire $delete_wire$169710 + wire $delete_wire$169711 + wire $delete_wire$169712 + wire width 32 $delete_wire$169713 + wire $delete_wire$169714 + wire $delete_wire$169715 + wire $delete_wire$169716 + wire $delete_wire$169717 + wire $delete_wire$169718 + wire $delete_wire$169719 + wire $delete_wire$169720 + wire $delete_wire$169721 + wire $delete_wire$169722 + wire $delete_wire$169723 + wire $delete_wire$169724 + wire $delete_wire$169725 + wire $delete_wire$169726 + wire $delete_wire$169727 + wire $delete_wire$169728 + wire $delete_wire$169729 + wire $delete_wire$169730 + wire $delete_wire$169731 + wire $delete_wire$169732 + wire $delete_wire$169733 + wire $delete_wire$169734 + wire $delete_wire$169735 + wire $delete_wire$169736 + wire $delete_wire$169737 + wire $delete_wire$169738 + wire $delete_wire$169739 + wire $delete_wire$169740 + wire $delete_wire$169741 + wire $delete_wire$169742 + wire $delete_wire$169743 + wire $delete_wire$169744 + wire $delete_wire$169745 + wire $delete_wire$169746 + wire $delete_wire$169747 + wire $delete_wire$169748 + wire $delete_wire$169749 + wire $delete_wire$169750 + wire $delete_wire$169751 + wire $delete_wire$169752 + wire $delete_wire$169753 + wire $delete_wire$169754 + wire $delete_wire$169755 + wire $delete_wire$169756 + wire $delete_wire$169757 + wire $delete_wire$169758 + wire $delete_wire$169759 + wire $delete_wire$169760 + wire $delete_wire$169761 + wire $delete_wire$169762 + wire $delete_wire$169763 + wire $delete_wire$169764 + wire $delete_wire$169765 + wire $delete_wire$169766 + wire $delete_wire$169767 + wire $delete_wire$169768 + wire $delete_wire$169769 + wire $delete_wire$169770 + wire $delete_wire$169771 + wire $delete_wire$169772 + wire $delete_wire$169773 + wire $delete_wire$169774 + wire $delete_wire$169775 + wire $delete_wire$169776 + wire $delete_wire$169777 + wire $delete_wire$169778 + wire $delete_wire$169779 + wire $delete_wire$169780 + wire $delete_wire$169781 + wire $delete_wire$169782 + wire $delete_wire$169783 + wire $delete_wire$169784 + wire $delete_wire$169785 + wire $delete_wire$169786 + wire $delete_wire$169787 + wire $delete_wire$169788 + wire $delete_wire$169789 + wire $delete_wire$169790 + wire $delete_wire$169791 + wire $delete_wire$169792 + wire $delete_wire$169793 + wire $delete_wire$169794 + wire $delete_wire$169795 + wire $delete_wire$169796 + wire $delete_wire$169797 + wire $delete_wire$169798 + wire $delete_wire$169799 + wire $delete_wire$169800 + wire $delete_wire$169801 + wire $delete_wire$169802 + wire $delete_wire$169803 + wire $delete_wire$169804 + wire $delete_wire$169805 + wire $delete_wire$169806 + wire $delete_wire$169807 + wire $delete_wire$169808 + wire $delete_wire$169809 + wire $delete_wire$169810 + wire $delete_wire$169811 + wire $delete_wire$169812 + wire $delete_wire$169813 + wire $delete_wire$169814 + wire $delete_wire$169815 + wire $delete_wire$169816 + wire $delete_wire$169817 + wire $delete_wire$169818 + wire $delete_wire$169819 + wire $delete_wire$169820 + wire $delete_wire$169821 + wire width 5 $delete_wire$169822 + wire width 17 $delete_wire$169823 + wire $delete_wire$169824 + wire $delete_wire$169825 + wire $delete_wire$169826 + wire $delete_wire$169827 + wire $delete_wire$169828 + wire $delete_wire$169829 + wire $delete_wire$169830 + wire $delete_wire$169831 + wire $delete_wire$169832 + wire $delete_wire$169833 + wire $delete_wire$169834 + wire $delete_wire$169835 + wire $delete_wire$169836 + wire $delete_wire$169837 + wire $delete_wire$169838 + wire $delete_wire$169839 + wire $delete_wire$169840 + wire $delete_wire$169960 + wire $delete_wire$169961 + wire $delete_wire$169962 + wire $delete_wire$169963 + wire $delete_wire$169964 + wire $delete_wire$169965 + wire $delete_wire$169966 + wire $delete_wire$169967 + wire $delete_wire$169968 + wire $delete_wire$169969 + wire $delete_wire$169970 + wire $delete_wire$169971 + wire $delete_wire$169972 + wire $delete_wire$169973 + wire $delete_wire$169974 + wire $delete_wire$169975 + wire $delete_wire$169976 + wire $delete_wire$169977 + wire $delete_wire$169978 + wire $delete_wire$169979 + wire $delete_wire$169980 + wire $delete_wire$169981 + wire $delete_wire$169982 + wire $delete_wire$169983 + wire $delete_wire$169984 + wire $delete_wire$169985 + wire $delete_wire$169986 + wire $delete_wire$169987 + wire $delete_wire$169988 + wire $delete_wire$169989 + wire $delete_wire$169990 + wire $delete_wire$169991 + wire $delete_wire$169992 + wire $delete_wire$169993 + wire $delete_wire$169994 + wire $delete_wire$169995 + wire $delete_wire$169996 + wire $delete_wire$169997 + wire $delete_wire$169998 + wire $delete_wire$169999 + wire $delete_wire$170000 + wire $delete_wire$170001 + wire $delete_wire$170002 + wire $delete_wire$170003 + wire $delete_wire$170004 + wire $delete_wire$170005 + wire $delete_wire$170006 + wire $delete_wire$170007 + wire $delete_wire$170008 + wire $delete_wire$170009 + wire $delete_wire$170010 + wire $delete_wire$170011 + wire $delete_wire$170012 + wire $delete_wire$170013 + wire $delete_wire$170014 + wire $delete_wire$170015 + wire $delete_wire$170016 + wire $delete_wire$170017 + wire $delete_wire$170018 + wire $delete_wire$170019 + wire $delete_wire$170020 + wire $delete_wire$170021 + wire $delete_wire$170022 + wire $delete_wire$170023 + wire $delete_wire$170024 + wire $delete_wire$170025 + wire $delete_wire$170026 + wire $delete_wire$170027 + wire $delete_wire$170028 + wire $delete_wire$170029 + wire $delete_wire$170030 + wire $delete_wire$170031 + wire $delete_wire$170032 + wire $delete_wire$170033 + wire $delete_wire$170034 + wire $delete_wire$170035 + wire $delete_wire$170036 + wire $delete_wire$170037 + wire $delete_wire$170038 + wire $delete_wire$170039 + wire $delete_wire$170040 + wire $delete_wire$170041 + wire $delete_wire$170042 + wire $delete_wire$170043 + wire $delete_wire$170044 + wire $delete_wire$170045 + wire $delete_wire$170046 + wire $delete_wire$170047 + wire $delete_wire$170048 + wire $delete_wire$170049 + wire $delete_wire$170050 + wire $delete_wire$170051 + wire $delete_wire$170052 + wire $delete_wire$170053 + wire $delete_wire$170054 + wire $delete_wire$170055 + wire $delete_wire$170056 + wire $delete_wire$170057 + wire $delete_wire$170058 + wire $delete_wire$170059 + wire $delete_wire$170635 + wire $delete_wire$170636 + wire $delete_wire$170637 + wire $delete_wire$170638 + wire $delete_wire$170639 + wire $delete_wire$170640 + wire $delete_wire$170641 + wire $delete_wire$170642 + wire $delete_wire$170643 + wire $delete_wire$170644 + wire $delete_wire$170645 + wire $delete_wire$170646 + wire $delete_wire$170647 + wire $delete_wire$170648 + wire $delete_wire$170649 + wire $delete_wire$170650 + wire $delete_wire$170651 + wire $delete_wire$170652 + wire $delete_wire$170653 + wire $delete_wire$170654 + wire $delete_wire$170655 + wire $delete_wire$170656 + wire $delete_wire$170657 + wire $delete_wire$170658 + wire $delete_wire$170659 + wire $delete_wire$170660 + wire $delete_wire$170661 + wire $delete_wire$170662 + wire $delete_wire$170663 + wire $delete_wire$170664 + wire $delete_wire$170665 + wire $delete_wire$170666 + wire $delete_wire$170667 + wire $delete_wire$170668 + wire $delete_wire$170669 + wire $delete_wire$170670 + wire $delete_wire$170749 + wire $delete_wire$170750 + wire $delete_wire$170751 + wire $delete_wire$170752 + wire $delete_wire$170753 + wire $delete_wire$170754 + wire $delete_wire$170755 + wire width 2 $delete_wire$170756 + wire width 2 $delete_wire$170757 + wire $delete_wire$170758 + wire $delete_wire$170759 + wire $delete_wire$170760 + wire $delete_wire$170761 + wire $delete_wire$170762 + wire $delete_wire$170763 + wire $delete_wire$170764 + wire $delete_wire$170765 + wire $delete_wire$170766 + wire $delete_wire$170767 + wire $delete_wire$170768 + wire $delete_wire$170769 + wire $delete_wire$170770 + wire $delete_wire$170771 + wire $delete_wire$170772 + wire $delete_wire$170773 + wire $delete_wire$170774 + wire $delete_wire$170775 + wire $delete_wire$170859 + wire $delete_wire$170860 + wire $delete_wire$170861 + wire $delete_wire$170862 + wire $delete_wire$170863 + wire $delete_wire$170864 + wire $delete_wire$170865 + wire $delete_wire$170866 + wire $delete_wire$170867 + wire $delete_wire$170868 + wire $delete_wire$170869 + wire $delete_wire$170870 + wire $delete_wire$170871 + wire $delete_wire$170872 + wire $delete_wire$170873 + wire $delete_wire$170874 + wire $delete_wire$170875 + wire $delete_wire$170876 + wire $delete_wire$170877 + wire $delete_wire$170878 + wire $delete_wire$170879 + wire $delete_wire$170880 + wire $delete_wire$170881 + wire $delete_wire$170882 + wire $delete_wire$170996 + wire $delete_wire$170997 + wire $delete_wire$170998 + wire $delete_wire$170999 + wire $delete_wire$171000 + wire $delete_wire$171001 + wire $delete_wire$171002 + wire $delete_wire$171003 + wire $delete_wire$171004 + wire $delete_wire$171005 + wire $delete_wire$171006 + wire $delete_wire$171007 + wire $delete_wire$171008 + wire $delete_wire$171009 + wire $delete_wire$171010 + wire $delete_wire$171011 + wire $delete_wire$171035 + wire $delete_wire$171036 + wire $delete_wire$171037 + wire $delete_wire$171038 + wire $delete_wire$171039 + wire $delete_wire$171040 + wire $delete_wire$171041 + wire $delete_wire$171042 + wire $delete_wire$171043 + wire $delete_wire$171044 + wire $delete_wire$171045 + wire $delete_wire$171046 + wire $delete_wire$171047 + wire $delete_wire$171048 + wire $delete_wire$171049 + wire $delete_wire$171172 + wire $delete_wire$171173 + wire $delete_wire$171174 + wire $delete_wire$171175 + wire $delete_wire$171176 + wire $delete_wire$171177 + wire $delete_wire$171178 + wire $delete_wire$171179 + wire $delete_wire$171180 + wire $delete_wire$171181 + wire $delete_wire$171182 + wire $delete_wire$171183 + wire $delete_wire$171184 + wire $delete_wire$171185 + wire $delete_wire$171186 + wire $delete_wire$171187 + wire $delete_wire$171188 + wire $delete_wire$171189 + wire $delete_wire$171190 + wire width 2 $delete_wire$171200 + wire width 2 $delete_wire$171201 + wire $delete_wire$171202 + wire $delete_wire$171203 + wire $delete_wire$171204 + wire $delete_wire$171205 + wire $delete_wire$171206 + wire $delete_wire$171207 + wire $delete_wire$171226 + wire $delete_wire$171227 + wire $delete_wire$171228 + wire $delete_wire$171229 + wire $delete_wire$171230 + wire $delete_wire$171231 + wire $delete_wire$171232 + wire $delete_wire$171254 + wire $delete_wire$171255 + wire $delete_wire$171256 + wire $delete_wire$171257 + wire $delete_wire$171258 + wire $delete_wire$171259 + wire $delete_wire$171267 + wire $delete_wire$171268 + wire $delete_wire$171269 + wire $delete_wire$171270 + wire $delete_wire$171271 + wire $delete_wire$171272 + wire $delete_wire$171314 + wire $delete_wire$171315 + wire $delete_wire$171316 + wire $delete_wire$171317 + wire $delete_wire$171318 + wire $delete_wire$171338 + wire $delete_wire$171339 + wire $delete_wire$171340 + wire $delete_wire$171341 + wire $delete_wire$171350 + wire $delete_wire$171351 + wire $delete_wire$171352 + wire $delete_wire$171353 + wire $delete_wire$171354 + wire $delete_wire$171355 + wire $delete_wire$171356 + wire $delete_wire$171357 + wire $delete_wire$171358 + wire $delete_wire$171359 + wire $delete_wire$171360 + wire width 2 $delete_wire$171361 + wire $delete_wire$171362 + wire $delete_wire$171371 + wire $delete_wire$171372 + wire $delete_wire$171373 + wire $delete_wire$171374 + wire $delete_wire$171387 + wire $delete_wire$171388 + wire $delete_wire$171389 + wire $delete_wire$171390 + wire $delete_wire$171408 + wire $delete_wire$171409 + wire $delete_wire$171441 + wire $delete_wire$171442 + wire $delete_wire$171449 + wire $delete_wire$171450 + wire $delete_wire$171454 + wire $delete_wire$171455 + wire $delete_wire$171471 + wire $delete_wire$171472 + wire $delete_wire$171477 + wire $delete_wire$171501 + wire $delete_wire$171502 + wire $delete_wire$171507 + wire $delete_wire$171512 + wire $delete_wire$171513 + wire $delete_wire$171514 + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216088$6333_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216089$6332_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216090$6331_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216091$6330_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216092$6329_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216093$6328_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216094$6327_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216095$6326_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216096$6325_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216097$6324_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216098$6323_Y + wire width 32 $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216099$6322_Y + wire \N2231 + wire \N3558 + attribute \unused_bits "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + wire width 32 \dcsr_d + wire \dcsr_q_prv__0_ + cell $adff $procdff$137332 + parameter \ARST_POLARITY 0 + parameter \ARST_VALUE 1'1 + parameter \CLK_POLARITY 1'1 + parameter \WIDTH 1 + connect \ARST $delete_wire$170004 + connect \CLK $delete_wire$171206 + connect \D \dcsr_d [0] + connect \Q \dcsr_q_prv__0_ + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216087$6334 + parameter \WIDTH 32 + connect \A { $delete_wire$170875 $delete_wire$171472 $delete_wire$171181 $delete_wire$169735 $delete_wire$169994 $delete_wire$169963 $delete_wire$170670 $delete_wire$169702 $delete_wire$169975 $delete_wire$170002 $delete_wire$169641 $delete_wire$171011 $delete_wire$171006 $delete_wire$171173 $delete_wire$171387 $delete_wire$170769 $delete_wire$170881 $delete_wire$169743 $delete_wire$169731 $delete_wire$169710 $delete_wire$169756 $delete_wire$170018 $delete_wire$169779 $delete_wire$170037 $delete_wire$169794 $delete_wire$171339 $delete_wire$171256 $delete_wire$171205 $delete_wire$169670 $delete_wire$171352 $delete_wire$171315 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216088$6333_Y + connect \S $delete_wire$169809 + connect \Y { $delete_wire$169830 $delete_wire$171390 $delete_wire$169837 $delete_wire$169679 $delete_wire$169677 $delete_wire$169767 $delete_wire$169828 $delete_wire$170038 $delete_wire$169704 $delete_wire$169653 $delete_wire$169707 $delete_wire$169684 $delete_wire$169986 $delete_wire$169712 $delete_wire$170033 $delete_wire$169759 $delete_wire$171513 $delete_wire$170012 $delete_wire$170876 $delete_wire$170007 $delete_wire$169965 $delete_wire$169825 $delete_wire$169655 $delete_wire$169839 $delete_wire$170034 $delete_wire$170042 $delete_wire$171338 $delete_wire$169833 $delete_wire$169762 $delete_wire$169835 $delete_wire$169764 \N2231 } + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216088$6333 + parameter \WIDTH 32 + connect \A { $delete_wire$170014 $delete_wire$171257 $delete_wire$170770 $delete_wire$169725 $delete_wire$170873 $delete_wire$171514 $delete_wire$170870 $delete_wire$169692 $delete_wire$171184 $delete_wire$170045 $delete_wire$169649 $delete_wire$170859 $delete_wire$171455 $delete_wire$170053 $delete_wire$170044 $delete_wire$170768 $delete_wire$170659 $delete_wire$169742 $delete_wire$169730 $delete_wire$169718 $delete_wire$169741 $delete_wire$170019 $delete_wire$169778 $delete_wire$170024 $delete_wire$169807 $delete_wire$171046 $delete_wire$171374 $delete_wire$170861 $delete_wire$169669 $delete_wire$171182 $delete_wire$171388 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216089$6332_Y + connect \S $delete_wire$169810 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216088$6333_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216089$6332 + parameter \WIDTH 32 + connect \A { $delete_wire$169820 $delete_wire$171507 $delete_wire$171441 $delete_wire$169724 $delete_wire$169992 $delete_wire$169691 $delete_wire$171356 $delete_wire$169700 $delete_wire$169977 $delete_wire$170046 $delete_wire$169648 $delete_wire$170774 $delete_wire$171038 $delete_wire$171005 $delete_wire$170057 $delete_wire$171226 $delete_wire$170874 $delete_wire$169755 $delete_wire$169729 $delete_wire$171450 $delete_wire$169754 $delete_wire$170020 $delete_wire$169777 $delete_wire$170025 $delete_wire$169806 $delete_wire$170643 $delete_wire$171177 $delete_wire$171449 $delete_wire$169668 $delete_wire$171512 $delete_wire$171501 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216090$6331_Y + connect \S $delete_wire$169997 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216089$6332_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216090$6331 + parameter \WIDTH 32 + connect \A { $delete_wire$169819 $delete_wire$170772 $delete_wire$170773 $delete_wire$169723 $delete_wire$169990 $delete_wire$169690 $delete_wire$170669 $delete_wire$169699 $delete_wire$169978 $delete_wire$170047 $delete_wire$169647 $delete_wire$171049 $delete_wire$171007 $delete_wire$170872 $delete_wire$170056 $delete_wire$171004 $delete_wire$171202 $delete_wire$169793 $delete_wire$169728 $delete_wire$170664 $delete_wire$169753 $delete_wire$170021 $delete_wire$169776 $delete_wire$170026 $delete_wire$169805 $delete_wire$170644 $delete_wire$171008 $delete_wire$170657 $delete_wire$169658 $delete_wire$171003 $delete_wire$170749 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216091$6330_Y + connect \S $delete_wire$169826 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216090$6331_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216091$6330 + parameter \WIDTH 32 + connect \A { 4'0100 $delete_wire$169823 2'00 $delete_wire$169822 1'0 $delete_wire$169781 $delete_wire$170757 } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216092$6329_Y + connect \S $delete_wire$170006 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216091$6330_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216092$6329 + parameter \WIDTH 32 + connect \A { $delete_wire$169818 $delete_wire$170762 $delete_wire$170868 $delete_wire$169722 $delete_wire$169991 $delete_wire$169689 $delete_wire$170642 $delete_wire$169698 $delete_wire$169979 $delete_wire$170048 $delete_wire$169646 $delete_wire$171409 $delete_wire$171259 $delete_wire$170043 $delete_wire$170055 $delete_wire$171471 $delete_wire$170646 $delete_wire$169792 $delete_wire$169727 $delete_wire$169740 $delete_wire$169752 $delete_wire$170022 $delete_wire$169775 $delete_wire$170027 $delete_wire$169804 $delete_wire$170666 $delete_wire$170663 $delete_wire$171179 $delete_wire$169666 $delete_wire$171186 $delete_wire$170655 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216093$6328_Y + connect \S $delete_wire$169972 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216092$6329_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216093$6328 + parameter \WIDTH 32 + connect \A { $delete_wire$169817 $delete_wire$171190 $delete_wire$171408 $delete_wire$169721 $delete_wire$171002 $delete_wire$169688 $delete_wire$171270 $delete_wire$169697 $delete_wire$169980 $delete_wire$169982 $delete_wire$169645 $delete_wire$171180 $delete_wire$169771 $delete_wire$170860 $delete_wire$170054 $delete_wire$171009 $delete_wire$170639 $delete_wire$169791 $delete_wire$170879 $delete_wire$169739 $delete_wire$169751 $delete_wire$170049 $delete_wire$169774 $delete_wire$170028 $delete_wire$169803 $delete_wire$170645 $delete_wire$169989 $delete_wire$171272 $delete_wire$169665 $delete_wire$171174 $delete_wire$170653 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216094$6327_Y + connect \S $delete_wire$170036 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216093$6328_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216094$6327 + parameter \WIDTH 32 + connect \A { $delete_wire$169816 $delete_wire$171228 $delete_wire$171454 $delete_wire$169720 $delete_wire$170997 $delete_wire$169687 $delete_wire$170649 $delete_wire$169696 $delete_wire$169981 $delete_wire$170050 $delete_wire$169644 $delete_wire$170760 $delete_wire$169786 $delete_wire$170862 $delete_wire$170032 $delete_wire$171389 $delete_wire$171043 $delete_wire$169790 $delete_wire$169749 $delete_wire$169738 $delete_wire$169750 $delete_wire$169983 $delete_wire$169773 $delete_wire$170029 $delete_wire$169802 $delete_wire$170660 $delete_wire$169962 $delete_wire$170658 $delete_wire$169664 $delete_wire$170751 $delete_wire$170640 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216095$6326_Y + connect \S $delete_wire$169705 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216094$6327_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216095$6326 + parameter \WIDTH 32 + connect \A { $delete_wire$169815 $delete_wire$171372 $delete_wire$171175 $delete_wire$169719 $delete_wire$171044 $delete_wire$169686 $delete_wire$171231 $delete_wire$169695 $delete_wire$170008 $delete_wire$170052 $delete_wire$169643 $delete_wire$170996 $delete_wire$169785 $delete_wire$170865 $delete_wire$170636 $delete_wire$171185 $delete_wire$171188 $delete_wire$169789 $delete_wire$171045 $delete_wire$169737 $delete_wire$169765 $delete_wire$171268 $delete_wire$169772 $delete_wire$170016 $delete_wire$169801 $delete_wire$170665 $delete_wire$170058 $delete_wire$169961 $delete_wire$169663 $delete_wire$171502 $delete_wire$170652 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216096$6325_Y + connect \S $delete_wire$170650 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216095$6326_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216096$6325 + parameter \WIDTH 32 + connect \A { $delete_wire$169814 $delete_wire$170753 $delete_wire$171176 $delete_wire$169709 $delete_wire$170999 $delete_wire$169685 $delete_wire$170647 $delete_wire$169694 $delete_wire$169999 $delete_wire$170051 $delete_wire$169642 $delete_wire$170863 $delete_wire$169784 $delete_wire$170755 $delete_wire$170754 $delete_wire$171204 $delete_wire$171373 $delete_wire$169788 $delete_wire$170059 $delete_wire$169736 $delete_wire$169748 $delete_wire$171351 $delete_wire$169787 $delete_wire$171340 $delete_wire$169800 $delete_wire$170638 $delete_wire$169970 $delete_wire$169974 $delete_wire$169662 $delete_wire$170764 $delete_wire$170651 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216097$6324_Y + connect \S $delete_wire$169996 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216096$6325_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216097$6324 + parameter \WIDTH 32 + connect \A { $delete_wire$169813 $delete_wire$171229 $delete_wire$170765 $delete_wire$169717 $delete_wire$171035 $delete_wire$169675 $delete_wire$170637 $delete_wire$169693 $delete_wire$170000 $delete_wire$171000 $delete_wire$169650 $delete_wire$171442 $delete_wire$169783 $delete_wire$170882 $delete_wire$171318 $delete_wire$171010 $delete_wire$171357 $delete_wire$169821 $delete_wire$170880 $delete_wire$169726 $delete_wire$169747 $delete_wire$169984 $delete_wire$169770 $delete_wire$170031 $delete_wire$169799 $delete_wire$171048 $delete_wire$169969 $delete_wire$170656 $delete_wire$169661 $delete_wire$171354 $delete_wire$170661 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216098$6323_Y + connect \S $delete_wire$169671 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216097$6324_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216098$6323 + parameter \WIDTH 32 + connect \A { $delete_wire$169998 $delete_wire$171350 $delete_wire$170759 $delete_wire$169716 $delete_wire$169993 $delete_wire$169683 $delete_wire$170654 $delete_wire$169667 $delete_wire$170001 $delete_wire$171371 $delete_wire$169674 $delete_wire$170667 $delete_wire$169782 $delete_wire$171187 $delete_wire$170878 $delete_wire$170766 $delete_wire$171254 $delete_wire$169796 $delete_wire$170877 $delete_wire$169734 $delete_wire$169746 $delete_wire$171207 $delete_wire$169769 $delete_wire$170009 $delete_wire$169798 $delete_wire$171172 $delete_wire$169968 $delete_wire$171271 $delete_wire$169660 $delete_wire$171232 $delete_wire$170662 \dcsr_q_prv__0_ } + connect \B $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216099$6322_Y + connect \S $delete_wire$169831 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216098$6323_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216099$6322 + parameter \WIDTH 32 + connect \A { $delete_wire$169811 $delete_wire$171355 $delete_wire$170763 $delete_wire$169715 $delete_wire$171039 $delete_wire$169682 $delete_wire$169988 $delete_wire$169657 $delete_wire$170015 $delete_wire$171317 $delete_wire$169673 $delete_wire$170866 $delete_wire$169768 $delete_wire$170998 $delete_wire$170752 $delete_wire$170023 $delete_wire$170767 $delete_wire$169795 $delete_wire$170030 $delete_wire$169733 $delete_wire$169745 $delete_wire$171360 $delete_wire$169834 $delete_wire$170010 $delete_wire$169976 $delete_wire$171203 $delete_wire$169967 $delete_wire$171477 $delete_wire$169659 $delete_wire$171230 $delete_wire$170641 \dcsr_q_prv__0_ } + connect \B $delete_wire$169713 + connect \S $delete_wire$171001 + connect \Y $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:216099$6322_Y + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:217099$7332 + parameter \WIDTH 32 + connect \A { $delete_wire$170013 $delete_wire$171353 $delete_wire$171037 $delete_wire$169714 $delete_wire$171269 $delete_wire$169681 $delete_wire$170668 $delete_wire$169656 $delete_wire$170003 $delete_wire$171189 $delete_wire$169672 $delete_wire$169960 $delete_wire$169780 $delete_wire$170775 $delete_wire$170871 $delete_wire$170864 $delete_wire$171183 $delete_wire$169808 $delete_wire$170017 $delete_wire$169732 $delete_wire$169744 $delete_wire$171359 $delete_wire$171041 $delete_wire$171040 $delete_wire$170761 $delete_wire$170635 $delete_wire$169966 $delete_wire$169971 $delete_wire$169701 $delete_wire$170648 $delete_wire$171255 \dcsr_q_prv__0_ } + connect \B { $delete_wire$169829 $delete_wire$171316 $delete_wire$169836 $delete_wire$169678 $delete_wire$169676 $delete_wire$169766 $delete_wire$169827 $delete_wire$170039 $delete_wire$169703 $delete_wire$169652 $delete_wire$169706 $delete_wire$169708 $delete_wire$169987 $delete_wire$169711 $delete_wire$169985 $delete_wire$169758 $delete_wire$169973 $delete_wire$170011 $delete_wire$170869 $delete_wire$169812 $delete_wire$169964 $delete_wire$169824 $delete_wire$169654 $delete_wire$169838 $delete_wire$170035 $delete_wire$169840 $delete_wire$171341 $delete_wire$169832 $delete_wire$169761 $delete_wire$169797 $delete_wire$169763 \N2231 } + connect \S $delete_wire$169760 + connect \Y { \dcsr_d [31:9] $delete_wire$171047 $delete_wire$170040 $delete_wire$170750 \dcsr_d [5:2] $delete_wire$171227 \N3558 } + end + cell $mux $ternary$/workspace/OpenROAD-flow-scripts/flow/designs/src/ariane133/ariane.sv2v.v:217233$7498 + parameter \WIDTH 5 + connect \A { $delete_wire$169995 $delete_wire$169651 $delete_wire$171042 $delete_wire$171201 } + connect \B { $delete_wire$170867 $delete_wire$170041 $delete_wire$171178 $delete_wire$171358 \N3558 } + connect \S $delete_wire$170771 + connect \Y { \dcsr_d [8:6] \dcsr_d [1:0] } + end +end diff --git a/tests/opt/opt_dff-simplify.ys b/tests/opt/opt_dff-simplify.ys new file mode 100644 index 000000000..699bfe065 --- /dev/null +++ b/tests/opt/opt_dff-simplify.ys @@ -0,0 +1,58 @@ +# 5279 issue +# Check only for complimentary patterns elimination + +read_rtlil opt_dff-simplify.il + +select -assert-count 0 t:$adffe +select -assert-count 1 t:$adff +select -assert-count 0 t:$ne + +opt_dff + +select -assert-count 1 t:$adffe +select -assert-count 0 t:$adff + +select -assert-count 8 t:$ne r:A_WIDTH=3 %i +select -assert-count 5 t:$ne r:A_WIDTH=2 %i + +select -assert-none t:$ne r:A_WIDTH=13 %i +select -assert-none t:$ne r:A_WIDTH=14 %i +select -assert-none t:$ne r:A_WIDTH=15 %i +select -assert-none t:$ne r:A_WIDTH=10 %i +select -assert-none t:$ne r:A_WIDTH=12 %i +select -assert-none t:$ne r:A_WIDTH=11 %i + +# Check for both complimentary and redundancy elimination + +read_verilog << EOT +module test(input clk, input h, input i, input m, output reg p); + wire D; + wire a; + wire j; + wire c; + wire mux_test; + wire n; + + always @(posedge clk) + p <= D; + assign j = n ? 1'hx : a; + assign a = i ? mux_test : p; + assign D = m ? h : j; + assign c = n ? 1'hx : p; + assign mux_test = m ? 1'hx : c; +endmodule +EOT + +cd test +proc + +select -assert-count 0 t:$dffe +select -assert-count 1 t:$dff +select -assert-count 0 t:$ne + +opt_dff + +select -assert-count 1 t:$dffe +select -assert-count 0 t:$dff +select -assert-count 1 t:$ne r:A_WIDTH=2 %i +select -assert-none t:$ne r:A_WIDTH=3 %i diff --git a/tests/unit/kernel/bitpatternTest.cc b/tests/unit/kernel/bitpatternTest.cc new file mode 100644 index 000000000..001d47060 --- /dev/null +++ b/tests/unit/kernel/bitpatternTest.cc @@ -0,0 +1,32 @@ +#include + +#include "kernel/bitpattern.h" +#include "kernel/rtlil.h" + +YOSYS_NAMESPACE_BEGIN + +TEST(BitpatternTest, has) +{ + SigSpec _aaa = {RTLIL::Sa, RTLIL::Sa, RTLIL::Sa}; + SigSpec _01a = {RTLIL::S0, RTLIL::S1, RTLIL::Sa}; + SigSpec _011 = {RTLIL::S0, RTLIL::S1, RTLIL::S1}; + SigSpec _111 = {RTLIL::S1, RTLIL::S1, RTLIL::S1}; + + EXPECT_TRUE(BitPatternPool(_aaa).has_any(_01a)); + EXPECT_TRUE(BitPatternPool(_01a).has_any(_01a)); + // 011 overlaps with 01a + EXPECT_TRUE(BitPatternPool(_011).has_any(_01a)); + // overlap is symmetric + EXPECT_TRUE(BitPatternPool(_01a).has_any(_011)); + EXPECT_FALSE(BitPatternPool(_111).has_any(_01a)); + + EXPECT_TRUE(BitPatternPool(_aaa).has_all(_01a)); + EXPECT_TRUE(BitPatternPool(_01a).has_all(_01a)); + // 011 is covered by 01a + EXPECT_TRUE(BitPatternPool(_01a).has_all(_011)); + // 01a is not covered by 011 + EXPECT_FALSE(BitPatternPool(_011).has_all(_01a)); + EXPECT_FALSE(BitPatternPool(_111).has_all(_01a)); +} + +YOSYS_NAMESPACE_END diff --git a/tests/unit/kernel/hashTest.cc b/tests/unit/kernel/hashTest.cc new file mode 100644 index 000000000..319d064a7 --- /dev/null +++ b/tests/unit/kernel/hashTest.cc @@ -0,0 +1,67 @@ +#include +#include "kernel/yosys_common.h" + +#include + +YOSYS_NAMESPACE_BEGIN + +static Hasher hash(int x) +{ + Hasher h; + h.eat(x); + return h; +} + +TEST(CommutativeTest, basic) +{ + hashlib::commutative_hash comm1; + comm1.eat(hash(1)); + comm1.eat(hash(2)); + hashlib::commutative_hash comm2; + comm2.eat(hash(2)); + comm2.eat(hash(1)); + EXPECT_EQ(comm1.hash_into(Hasher()).yield(), comm2.hash_into(Hasher()).yield()); +} + +TEST(PoolHashTest, collisions) +{ + uint64_t collisions = 0; + std::unordered_set hashes; + for (int i = 0; i < 1000; ++i) { + for (int j = i + 1; j < 1000; ++j) { + pool p1; + p1.insert(i); + p1.insert(j); + auto h = p1.hash_into(Hasher()).yield(); + if (!hashes.insert(h).second) { + ++collisions; + } + } + } + std::cout << "pool collisions: " << collisions << std::endl; + EXPECT_LT(collisions, 10'000); +} + +TEST(PoolHashTest, subset_collisions) +{ + uint64_t collisions = 0; + std::unordered_set hashes; + for (int i = 0; i < 1000 * 1000; ++i) { + + pool p1; + for (int b = 0; i >> b; ++b) { + if ((i >> b) & 1) { + p1.insert(b); + } + } + auto h = p1.hash_into(Hasher()).yield(); + if (!hashes.insert(h).second) { + ++collisions; + } + + } + std::cout << "pool subset collisions: " << collisions << std::endl; + EXPECT_LT(collisions, 100); +} + +YOSYS_NAMESPACE_END diff --git a/tests/unit/kernel/ioTest.cc b/tests/unit/kernel/ioTest.cc index 7cb8498cb..43a71eb79 100644 --- a/tests/unit/kernel/ioTest.cc +++ b/tests/unit/kernel/ioTest.cc @@ -69,4 +69,14 @@ TEST(KernelStringfTest, dynamicWidthAndPrecision) EXPECT_EQ(stringf("%*.*f", 8, 4, 1.0), " 1.0000"); } +TEST(KernelStringfTest, dynamicPrecisionInt) +{ + EXPECT_EQ(stringf("%.*d", 4, 7), "0007"); +} + +TEST(KernelStringfTest, dynamicWidthAndPrecisionInt) +{ + EXPECT_EQ(stringf("%*.*d", 8, 4, 7), " 0007"); +} + YOSYS_NAMESPACE_END diff --git a/tests/various/abstract_initstates.ys b/tests/various/abstract_initstates.ys new file mode 100644 index 000000000..35d17cf62 --- /dev/null +++ b/tests/various/abstract_initstates.ys @@ -0,0 +1,30 @@ +read_verilog <