Merge from master for release.

This commit is contained in:
Wilson Snyder 2021-06-12 13:01:37 -04:00
commit fa3c072dd1
177 changed files with 8331 additions and 2817 deletions

View File

@ -16,7 +16,6 @@ env:
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
jobs:
@ -31,7 +30,7 @@ jobs:
- id: generate
name: Run 'generate_matrix.sh'
run: |
if [ '${{ github.event_name}}' = 'pull_request' ]; then
if [ '${{ github.event_name }}' = 'pull_request' ]; then
matrix='[ "ubuntu-20.04" ]'
else
matrix='[ "ubuntu-16.04", "ubuntu-18.04", "ubuntu-20.04" ]'
@ -57,7 +56,8 @@ jobs:
CI_MAKE_SRC_TARGET: ${{ matrix.debug }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}
CACHE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
steps:
- name: Checkout
@ -77,7 +77,9 @@ jobs:
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz ./bin
- uses: actions/upload-artifact@v2
@ -89,7 +91,6 @@ jobs:
needs: [ Matrix, Build ]
strategy:
fail-fast: false
max-parallel: 8
matrix:
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
compiler:
@ -98,6 +99,7 @@ jobs:
suite:
- dist-vlt-0
- dist-vlt-1
- dist-vlt-2
- vltmt-0
- vltmt-1
runs-on: ${{ matrix.os }}
@ -107,7 +109,8 @@ jobs:
CI_RUNS_ON: ${{ matrix.os }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_KEY: ${{ matrix.os }}-${{ matrix.compiler.cc }}
CACHE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
steps:
- name: Checkout
@ -134,4 +137,7 @@ jobs:
- name: Test
env:
TESTS: ${{ matrix.suite }}
run: ./ci/ci-script.bash
run: |
ccache -z
./ci/ci-script.bash
ccache -s

View File

@ -13,7 +13,6 @@ env:
CI_COMMIT: ${{ github.sha }}
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
COVERAGE: 1
jobs:
@ -24,7 +23,8 @@ jobs:
env:
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
CACHE_KEY: coverage-build
CCACHE_MAXSIZE: 512Mi
steps:
- name: Checkout
@ -37,7 +37,7 @@ jobs:
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: coverage-${{ env.cache-name }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
- name: Install packages for build
env:
@ -46,7 +46,9 @@ jobs:
- name: Build
run: |
ccache -z
./ci/ci-script.bash
ccache -s
tar cvzf verilator-${CI_COMMIT}-coverage.tgz bin src/obj*/*.o src/obj*/*.gcno
- uses: actions/upload-artifact@v2
@ -80,7 +82,8 @@ jobs:
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ubuntu-20.04
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
CACHE_KEY: coverage-test-${{ matrix.test }}${{ matrix.num }}
CCACHE_MAXSIZE: 512Mi # Per build matrix entry
steps:
- name: Checkout
@ -93,7 +96,7 @@ jobs:
with:
path: ${{ github.workspace }}/.ccache
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
restore-keys: coverage-${{ env.cache-name }}
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
- uses: actions/download-artifact@v2
@ -106,4 +109,7 @@ jobs:
- name: Test
env:
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
run: ./ci/ci-script.bash
run: |
ccache -z
./ci/ci-script.bash
ccache -s

View File

@ -1,7 +1,7 @@
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: clang-format
name: format
on:
push:
@ -38,6 +38,6 @@ jobs:
- name: Push
run: |
if [ -n "$(git status --porcelain)" ]; then
git commit . -m "Apply clang-format" &&
git commit . -m "Apply 'make format'" &&
git push origin
fi

37
Changes
View File

@ -8,6 +8,41 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.204 2021-06-12
==========================
**Minor:**
* Add 'make ccache-report' (#3011). [Geza Lore]
* Add --reloop-limit argument (#2943) (#2960). [Geza Lore]
* Add --expand-limit argument (#3005). [Julien Margetts]
* Add TRACE_THREADS to CMake (#2934). [Jonathan Drolet]
* Optimize large lookup tables to static data (#2925). [Geza Lore]
* Optimize reloop to accept constant index offsets (#2939). [Geza Lore]
* Split always blocks to better respect --output-split-cfuncs. [Geza Lore]
* Support ignoring "`pragma protect ..." (#2886). [Udi Finkelstein]
* Support --trace-fst for SystemC with CMake (#2927). [Jonathan Drolet]
* Update cmake latest C++ Standard Compilation flag (#2951). [Ameya Vikram Singh]
* Prep work towards better ccache hashing/performance. [Geza Lore]
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
* Fix VPI memory access for packed arrays (#2922). [Todd Strader]
* Fix MCD close also closing stdout (#2931). [Alexander Grobman]
* Fix split procedures to better respect --output-split-cfuncs (#2942). [Geza Lore]
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
* Fix constant function calls with uninit value (#2995). [yanx21]
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
Verilator 4.202 2021-04-24
==========================
@ -331,7 +366,7 @@ Verilator 4.034 2020-05-03
* Support event data type (with some restrictions).
* Support $root. (#2150) [Keyi Zhang]
* Add error if use SystemC 2.2 and earlier (pre-2011) as is deprecated.
* Fix build of fast path tracing code to use OPT_FAST. (#2245) [Geza Lore]
* Add support of --trace-structs for CMake (#2986). [Martin Schmidt]
* Fix arrayed instances connecting to slices. (#2263) [Don/engr248]
* Fix error on unpacked connecting to packed. (#2288) [Joseph Shaker]
* Fix logical not optimization with empty begin. (#2291) [Baltazar Ortiz]

View File

@ -44,6 +44,7 @@
srcdir = @srcdir@
VPATH = @srcdir@
HOST = @HOST@
EXEEXT = @EXEEXT@
DOXYGEN = doxygen
INSTALL = @INSTALL@
@ -130,6 +131,7 @@ DISTFILES1 = $(INFOS) .gitignore \
verilator-config.cmake.in \
verilator-config-version.cmake.in \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_difftree \
bin/verilator_gantt \
@ -193,6 +195,7 @@ DISTFILES2 = \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_ccache_report \
bin/verilator_coverage \
bin/verilator_gantt \
bin/verilator_includer \
@ -203,9 +206,9 @@ INST_PROJ_FILES = \
include/vltstd/*.[chv]* \
INST_PROJ_BIN_FILES = \
bin/verilator_bin \
bin/verilator_bin_dbg \
bin/verilator_coverage_bin_dbg \
bin/verilator_bin$(EXEEXT) \
bin/verilator_bin_dbg$(EXEEXT) \
bin/verilator_coverage_bin_dbg$(EXEEXT) \
EXAMPLES_FIRST = \
examples/make_hello_c \
@ -221,10 +224,10 @@ all: all_nomsg msg_test
all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
.PHONY:verilator_exe
.PHONY:verilator_bin
.PHONY:verilator_bin_dbg
.PHONY:verilator_coverage_bin_dbg
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
.PHONY:verilator_bin$(EXEEXT)
.PHONY:verilator_bin_dbg$(EXEEXT)
.PHONY:verilator_coverage_bin_dbg$(EXEEXT)
verilator_exe verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT):
@echo ------------------------------------------------------------
@echo "making verilator in src"
$(MAKE) -C src $(OBJCACHE_JOBS) $(CI_MAKE_SRC_TARGET)
@ -280,8 +283,8 @@ verilator.pdf: Makefile
$(MAKE) -C docs verilator.pdf
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
verilator_ccache_report verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
# Some scripts go into both the search path and pkgdatadir,
# so they can be found by the user, and under $VERILATOR_ROOT.
@ -305,11 +308,12 @@ installbin:
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
# Man files can either be part of the original kit, or built in current directory
# So important we use $^ so VPATH is searched
@ -458,7 +462,7 @@ clang-tidy: $(CLANGTIDY_DEP)
analyzer-src:
-rm -rf src/obj_dbg
scan-build $(MAKE) -k verilator_coverage_bin_dbg verilator_bin_dbg
scan-build $(MAKE) -k verilator_coverage_bin_dbg$(EXEEXT) verilator_bin_dbg$(EXEEXT)
analyzer-include:
-rm -rf examples/*/obj*
@ -476,12 +480,12 @@ clang-format:
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
PY_PROGRAMS = \
bin/verilator_ccache_report \
examples/xml_py/vl_file_copy \
examples/xml_py/vl_hier_graph \
docs/guide/conf.py \
docs/guide/vl_sphinx_extract \
docs/guide/vl_sphinx_extract \
docs/guide/vl_doxygen_filter \
docs/bin/vl_sphinx_extract \
docs/bin/vl_sphinx_fix \
src/astgen \
src/bisonpre \
src/config_rev \

View File

@ -315,6 +315,7 @@ detailed descriptions of these arguments.
-E Preprocess, but do not compile
--error-limit <value> Abort after this number of errors
--exe Link to create executable
--expand-limit <value> Set expand optimization limit
-F <file> Parse arguments from a file, relatively
-f <file> Parse arguments from a file
-FI <file> Force include of a file
@ -378,6 +379,7 @@ detailed descriptions of these arguments.
--quiet-exit Don't print the command on failure
--relative-includes Resolve includes relative to current file
--no-relative-cfuncs Disallow 'this->' in generated functions
--reloop-limit Minimum iterations for forming loops
--report-unoptflat Extra diagnostics for UNOPTFLAT
--rr Run Verilator and record with rr
--savable Enable model save-restore

93
bin/verilator_ccache_report Executable file
View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0301,R0902,R0913,R0914,R0912,R0915,W0621
######################################################################
import argparse
import collections
import pathlib
import re
from datetime import datetime
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Report ccache behavior of a Verilated model build.""",
epilog=
"""Copyright 2002-2021 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
parser.add_argument('-o',
type=argparse.FileType('w'),
metavar="OUTFILE",
required=True,
help='output file')
parser.add_argument('logdir', type=pathlib.Path, help='log directory')
args = parser.parse_args()
results = {}
elapsed = {}
def toDateTime(s):
return datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f")
for logfile in args.logdir.iterdir():
with logfile.open() as fh:
start = None
obj = None
for line in fh:
line = line.strip()
match = re.match(r'\[(\S+)\s.*Object file: (.*)$', line)
if match:
start = toDateTime(match.group(1))
obj = match.group(2)
match = re.match(r'\[(\S+)\s.*Result: (.*)$', line)
if match:
assert obj is not None
elapsed[obj] = toDateTime(match.group(1)) - start
results[obj] = match.group(2)
args.o.write("#" * 80 + "\n")
args.o.write("ccache report (from verilator_ccache_report) :\n")
if not results:
args.o.write("\nAll object files up to date\n")
else:
args.o.write("\nCompiled object files:\n")
wnames = max(len(_) for _ in results) + 1
wresults = max(len(_) for _ in results.values()) + 1
for k in sorted(results.keys()):
args.o.write("{:{wnames}} : {:{wresults}} : {}s\n".format(
k,
results[k],
elapsed[k].total_seconds(),
wnames=wnames,
wresults=wresults))
args.o.write("\nSummary:\n")
counts = collections.Counter(_ for _ in results.values())
total = sum(counts.values())
for k in sorted(counts.keys()):
c = counts[k]
args.o.write("{:{width}}| {} ({:.2%})\n".format(k,
c,
c / total,
width=wresults))
args.o.write("\nLongest:\n")
longest = sorted(list(elapsed.items()),
key=lambda kv: -kv[1].total_seconds())
for i, (k, v) in enumerate(longest):
args.o.write("{:{width}}| {}s\n".format(k,
v.total_seconds(),
width=wnames))
if i > 4: break
args.o.write("#" * 80 + "\n")

View File

@ -85,10 +85,13 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
# Run the specified test
case $TESTS in
dist-vlt-0)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/2
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
;;
dist-vlt-1)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/2
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
;;
dist-vlt-2)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
;;
vltmt-0)
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2

View File

@ -7,7 +7,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.202 2021-04-24],
AC_INIT([Verilator],[4.204 2021-06-12],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
@ -272,13 +272,15 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# gnu++17 code is clean, but SystemC in 2018 doesn't link with it (bug1339)
# gnu++14 is the newest that Verilator supports
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
# gnu++17 is the newest that Verilator supports
# std++03 is the oldest that Verilator supports
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
@ -289,11 +291,13 @@ AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
# Flags for compiling Verilator internals including parser, and Verilated files

View File

@ -6,6 +6,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
Ahmed El-Mahmoudy
Alex Chadwick
Àlex Torregrosa
Ameya Vikram Singh
Andreas Kuster
Chris Randall
Conor McCullough
@ -38,6 +39,7 @@ Jean Berniolles
Jeremy Bennett
John Coiner
John Demme
Jonathan Drolet
Josh Redford
Julien Margetts
Kaleb Barrett
@ -53,9 +55,11 @@ Marco Widmer
Markus Krause
Marlon James
Marshal Qiao
Martin Schmidt
Matthew Ballance
Michael Killough
Mike Popoloski
Miodrag Milanović
Morten Borup Petersen
Nandu Raj
Nathan Kohagen

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
# pylint: disable=C0112,C0114,C0116,C0301,R0903
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0201,R0903
# -*- Python -*- See copyright, etc below
######################################################################
@ -8,6 +8,7 @@ import re
#######################################################################
class VlSphinxExtract:
debug = 0
SkipBasenames = {}

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
# pylint: disable=C0112,C0114,C0116,C0301,R0903
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0903
# -*- Python -*- See copyright, etc below
######################################################################

View File

@ -0,0 +1,11 @@
.. comment: generated by t_lint_multidriven_bad
.. code-block:: sv
:linenos:
:emphasize-lines: 2,5
always @(posedge clk) begin
out2[7:0] <= d0; // <--- Warning
end
always @(negedge clk) begin
out2[15:8] <= d0; // <--- Warning
end

View File

@ -0,0 +1,6 @@
.. comment: generated by t_lint_multidriven_bad
.. code-block::
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
example.v:1:7 ... Location of first driving block
example.v:1:7 ... Location of other driving block

View File

@ -1,4 +1,4 @@
# pylint: disable=E402
# pylint: disable=C0103,C0114,C0116,E0402,W0622
#
# Configuration file for Verilator's Sphinx documentation builder.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
@ -12,11 +12,10 @@
from datetime import datetime
import os
import re
import shutil
import sys
sys.path.insert(0, os.path.abspath('./_ext'))
import sphinx_rtd_theme
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
def get_vlt_version():
@ -94,7 +93,7 @@ today_fmt = datetime.now().strftime("%F")
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# TODO could use this for internals<->guide references
# Could use this for internals<->guide references
# intersphinx_mapping = { 'sphinx': ('https://sphinx-doc.org/', None), }
# ----------------------------------------------------------------------
@ -208,7 +207,7 @@ spelling_ignore_contributor_names = True
# ----------------------------------------------------------------------
# -- Options for doxygen
#if shutil.which("doxygen"):
# if shutil.which("doxygen"):
# breathe_projects = {
# "verilated": "../_build/doxygen/verilated/xml",
# }

View File

@ -403,6 +403,12 @@ Summary:
files on the command line that implement the main loop for your
simulation.
.. option:: --expand-limit <value>
Rarely needed. Fine-tune optimizations to set the maximum size of an
expression in 32-bit words to expand into separate word-based
statements.
.. option:: -F <file>
Read the specified file, and act as if all text inside it was specified
@ -418,8 +424,11 @@ Summary:
fairly standard across Verilog tools.
The file may contain :code:`//` comments which are ignored to the end of
the line. Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be
replaced with the specified environment variable.
the line. It may also contain :code:`/* .. */` comments which are
ignored, be cautious that wildcards are not handled in -f files, and
that :code:`directory/*` is the beginning of a comment, not a wildcard.
Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be replaced
with the specified environment variable.
.. option:: -FI <file>
@ -915,6 +924,15 @@ Summary:
the path of the referencing file, instead of relative to the current
directory.
.. option:: --reloop-limit
Rarely needed. Verilator attempts to turn some common sequences of
statements into loops in the output. This argument specifies the minimum
number of iterations the resulting loop needs to have in order to perform
this transformation. Default limit is 40. A smaller number may slightly
improve C++ compilation time on designs where these sequences are common,
however effect on model performance requires benchmarking.
.. option:: --report-unoptflat
Extra diagnostics for UNOPTFLAT warnings. This includes for each loop,

View File

@ -118,9 +118,9 @@ A. Pass the :vlopt:`--trace` option to Verilator, and in your top level C
B. Or, for finer-grained control, or C++ files with multiple Verilated
modules you may also create the trace purely from C++. Create a
VerilatedVcdC object, and in your main loop call
``trace_object->dump(time)`` every time step, and finally call
``trace_object->close()``.
VerilatedVcdC object, and in your main loop right after ``eval()`` call
``trace_object->dump(contextp->time())`` every time step, and finally
call ``trace_object->close()``.
.. code-block:: C++
:emphasize-lines: 1,5-8,12
@ -128,15 +128,17 @@ B. Or, for finer-grained control, or C++ files with multiple Verilated
#include "verilated_vcd_c.h"
...
int main(int argc, char** argv, char** env) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
...
Verilated::traceEverOn(true);
VerilatedVcdC* tfp = new VerilatedVcdC;
topp->trace(tfp, 99); // Trace 99 levels of hierarchy
tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
...
while (Verilated::time() < sim_time && !Verilated::gotFinish()) {
Verilated::timeInc(1);
tfp->dump(main_time);
while (contextp->time() < sim_time && !contextp->gotFinish()) {
contextp->timeInc(1);
topp->eval();
tfp->dump(contextp->time());
}
tfp->close();
}
@ -385,7 +387,7 @@ How do I get faster build times?
identical source builds, even across different users. If ccache was
installed when Verilator was built it is used, or see OBJCACHE
environment variable to override this. Also see the
:vlopt:`--output-split` option.
:vlopt:`--output-split` option and :ref: `Profiling ccache efficiency`
* To reduce the compile time of classes that use a Verilated module (e.g. a
top CPP file) you may wish to add a

View File

@ -316,6 +316,35 @@ statistics.
For more information see :command:`verilator_gantt`.
.. _Profiling ccache efficiency:
Profiling ccache efficiency
===========================
The Verilator generated Makefile provides support for basic profiling of
ccache behavior during the build. This can be used to track down files that
might be unnecessarily rebuilt, though as of today even small code changes
will usually require rebuilding a large number of files. Improving ccache
efficiency during the edit/compile/test loop is an active area of
development.
To get a basic report of how well ccache is doing, add the `ccache-report`
target when invoking the generated Makefile:
.. code-block:: bash
make -C obj_dir -f Vout.mk Vout ccache-report
This will print a report based on all executions of ccache during this
invocation of Make. The report is also written to a file, in this example
`obj_dir/Vout__cache_report.txt`.
To use the `ccache-report` target, at least one other explicit build target
must be specified, and OBJCACHE must be set to 'ccache'.
This feature is currently experimental and might change in subsequent
releases.
.. _Save/Restore:
Save/Restore

View File

@ -360,7 +360,8 @@ Verilate in CMake
verilate(target SOURCES source ... [TOP_MODULE top] [PREFIX name]
[TRACE] [TRACE_FST] [SYSTEMC] [COVERAGE]
[INCLUDE_DIRS dir ...] [OPT_SLOW ...] [OPT_FAST ...]
[OPT_GLOBAL ..] [DIRECTORY dir] [VERILATOR_ARGS ...])
[OPT_GLOBAL ..] [DIRECTORY dir] [THREADS num]
[TRACE_THREADS num] [VERILATOR_ARGS ...])
Lowercase and ... should be replaced with arguments, the uppercase parts
delimit the arguments and can be passed in any order, or left out entirely
@ -429,6 +430,15 @@ SystemC include directories and link to the SystemC libraries.
the SystemC library. This can be specified using the SYSTEMC_CXX_FLAGS
environment variable.
.. describe:: THREADS
Optional. Generated a multi-threaded model, same as "--threads".
.. describe:: TRACE_THREADS
Optional. Generated multi-threaded trace dumping, same as
"--trace-threads".
.. describe:: TOP_MODULE
Optional. Sets the name of the top module. Defaults to the name of the

View File

@ -128,6 +128,13 @@ List Of Warnings
simulate correctly.
.. option:: BADSTDPRAGMA
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
.. option:: BLKANDNBLK
.. TODO better example
@ -796,15 +803,25 @@ List Of Warnings
.. option:: MULTIDRIVEN
.. TODO better example
Warns that the specified signal comes from multiple always blocks each
with different clocking. This warning does not look at individual bits
(see example below).
Warns that the specified signal comes from multiple always blocks. This
is often unsupported by synthesis tools, and is considered bad style.
It will also cause longer simulation runtimes due to reduced
optimizations.
This is considered bad style, as the consumer of a given signal may be
unaware of the inconsistent clocking, causing clock domain crossing
or timing bugs.
Faulty example:
.. include:: ../../docs/gen/ex_MULTIDRIVEN_faulty.rst
Results in:
.. include:: ../../docs/gen/ex_MULTIDRIVEN_msg.rst
Ignoring this warning will only slow simulations, it will simulate
correctly.
correctly. It may however cause longer simulation runtimes due to
reduced optimizations.
.. option:: MULTITOP
@ -988,6 +1005,16 @@ List Of Warnings
a var/reg must be used as the target of procedural assignments.
.. option:: PROTECTED
Warning that a '`pragma protected' section was encountered. The code
inside the protected region will be partly checked for correctness, but is
otherwise ignored.
Suppressing the warning may make Verilator differ from a simulator that
accepts the protected code.
.. option:: RANDC
Warns that the :code:`randc` keyword is currently unsupported, and that

View File

@ -20,7 +20,7 @@
# cmake --build .
cmake_minimum_required(VERSION 3.8)
project(cmake_hello_sc)
project(cmake_hello_sc CXX)
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
if (NOT verilator_FOUND)
@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
# Create a new executable target that will contain all your sources
add_executable(example ../make_hello_sc/sc_main.cpp)
set_property(
TARGET example
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
)
# Add the Verilated circuit to the target
verilate(example SYSTEMC
INCLUDE_DIRS "../make_hello_sc"

View File

@ -20,7 +20,7 @@
# cmake --build .
cmake_minimum_required(VERSION 3.8)
project(cmake_tracing_sc_example)
project(cmake_tracing_sc_example CXX)
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
if (NOT verilator_FOUND)
@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
# Create a new executable target that will contain all your sources
add_executable(example ../make_tracing_sc/sc_main.cpp)
set_property(
TARGET example
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
)
# Add the Verilated circuit to the target
verilate(example SYSTEMC COVERAGE TRACE
INCLUDE_DIRS "../make_tracing_sc"

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2009-2018 Tony Bybell.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#ifndef WIN_UNISTD_H
#define WIN_UNISTD_H
#include <stdlib.h>
#ifdef _WIN64
#include <io.h>
#else
#include <sys/io.h>
#endif
#include <process.h>
#define ftruncate _chsize_s
#define unlink _unlink
#define fileno _fileno
#define lseek _lseeki64
#ifdef _WIN64
#define ssize_t __int64
#define SSIZE_MAX 9223372036854775807i64
#else
#define ssize_t long
#define SSIZE_MAX 2147483647L
#endif
#include "stdint.h"
#endif //WIN_UNISTD_H

View File

@ -80,6 +80,12 @@
#define PATH_MAX (4096)
#endif
#if defined(_MSC_VER)
typedef int64_t fst_off_t;
#else
typedef off_t fst_off_t;
#endif
/* note that Judy versus Jenkins requires more experimentation: they are */
/* functionally equivalent though it appears Jenkins is slightly faster. */
/* in addition, Jenkins is not bound by the LGPL. */
@ -155,8 +161,8 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
#ifdef __MINGW32__
#include <io.h>
#ifndef HAVE_FSEEKO
#define ftello ftell
#define fseeko fseek
#define ftello _ftelli64
#define fseeko _fseeki64
#endif
#endif
@ -284,7 +290,7 @@ static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp)
return(fwrite(buf, siz, cnt, fp));
}
static int fstFtruncate(int fd, off_t length)
static int fstFtruncate(int fd, fst_off_t length)
{
return(ftruncate(fd, length));
}
@ -329,12 +335,12 @@ return(NULL);
#define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off))
#define fstMunmap(__addr,__len) free(__addr)
static void *fstMmap2(size_t __len, int __fd, off_t __off)
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
{
(void)__off;
unsigned char *pnt = (unsigned char *)malloc(__len);
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
size_t i;
lseek(__fd, 0, SEEK_SET);
@ -734,7 +740,7 @@ FILE *tchn_handle;
unsigned char *vchg_mem;
off_t hier_file_len;
fst_off_t hier_file_len;
uint32_t *valpos_mem;
unsigned char *curval_mem;
@ -754,7 +760,7 @@ unsigned fourpack : 1;
unsigned fastpack : 1;
int64_t timezero;
off_t section_header_truncpos;
fst_off_t section_header_truncpos;
uint32_t tchn_cnt, tchn_idx;
uint64_t curtime;
uint64_t firsttime;
@ -762,7 +768,7 @@ uint32_t vchg_siz;
uint32_t vchg_alloc_siz;
uint32_t secnum;
off_t section_start;
fst_off_t section_start;
uint32_t numscopes;
double nan; /* nan value for uninitialized doubles */
@ -820,7 +826,7 @@ fstEnumHandle max_enumhandle;
};
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, off_t offset, int whence)
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, fst_off_t offset, int whence)
{
int rc = fseeko(stream, offset, whence);
@ -987,7 +993,7 @@ if(pnt == MAP_FAILED)
static void fstWriterCreateMmaps(struct fstWriterContext *xc)
{
off_t curpos = ftello(xc->handle);
fst_off_t curpos = ftello(xc->handle);
fflush(xc->hier_handle);
@ -1041,7 +1047,7 @@ if(xc->curval_mem)
{
unsigned char *pnt = xc->curval_mem;
int __fd = fileno(xc->curval_handle);
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
size_t i;
size_t __len = xc->maxvalpos;
@ -1282,14 +1288,14 @@ int cnt = 0;
unsigned int i;
unsigned char *vchg_mem;
FILE *f;
off_t fpos, indxpos, endpos;
fst_off_t fpos, indxpos, endpos;
uint32_t prevpos;
int zerocnt;
unsigned char *scratchpad;
unsigned char *scratchpnt;
unsigned char *tmem;
off_t tlen;
off_t unc_memreq = 0; /* for reader */
fst_off_t tlen;
fst_off_t unc_memreq = 0; /* for reader */
unsigned char *packmem;
unsigned int packmemlen;
uint32_t *vm4ip;
@ -1733,7 +1739,7 @@ if(tmem)
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
if((rc == Z_OK) && (((off_t)destlen) < tlen))
if((rc == Z_OK) && (((fst_off_t)destlen) < tlen))
{
fstFwrite(dmem, destlen, 1, xc->handle);
}
@ -1781,7 +1787,7 @@ fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET);
xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */
if(xc->dump_size_limit)
{
if(endpos >= ((off_t)xc->dump_size_limit))
if(endpos >= ((fst_off_t)xc->dump_size_limit))
{
xc2->skip_writing_section_hdr = 1;
xc2->size_limit_locked = 1;
@ -1931,7 +1937,7 @@ if(xc)
if(xc && !xc->already_in_close && !xc->already_in_flush)
{
unsigned char *tmem = NULL;
off_t fixup_offs, tlen, hlen;
fst_off_t fixup_offs, tlen, hlen;
xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */
@ -1991,7 +1997,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
if((rc != Z_OK) || (((off_t)destlen) > tlen))
if((rc != Z_OK) || (((fst_off_t)destlen) > tlen))
{
destlen = tlen;
}
@ -2002,7 +2008,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
fstWriterUint64(xc->handle, tlen); /* uncompressed */
/* compressed len is section length - 24 */
fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */
fstFwrite((((off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
fstFwrite((((fst_off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
fflush(xc->handle);
fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
@ -2018,7 +2024,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
if(xc->num_blackouts)
{
uint64_t cur_bl = 0;
off_t bpos, eos;
fst_off_t bpos, eos;
uint32_t i;
fixup_offs = ftello(xc->handle);
@ -2051,7 +2057,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
if(xc->compress_hier)
{
off_t hl, eos;
fst_off_t hl, eos;
gzFile zhandle;
int zfd;
int fourpack_duo = 0;
@ -2174,7 +2180,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
if(xc->repack_on_close)
{
FILE *fp;
off_t offpnt, uclen;
fst_off_t offpnt, uclen;
int flen = strlen(xc->filename);
char *hf = (char *)calloc(1, flen + 5);
@ -2281,7 +2287,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
if(xc)
{
char s[FST_HDR_DATE_SIZE];
off_t fpos = ftello(xc->handle);
fst_off_t fpos = ftello(xc->handle);
int len = strlen(dat);
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET);
@ -2300,7 +2306,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
if(xc && vers)
{
char s[FST_HDR_SIM_VERSION_SIZE];
off_t fpos = ftello(xc->handle);
fst_off_t fpos = ftello(xc->handle);
int len = strlen(vers);
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET);
@ -2320,7 +2326,7 @@ if(xc)
{
if(/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX))
{
off_t fpos = ftello(xc->handle);
fst_off_t fpos = ftello(xc->handle);
xc->filetype = filetype;
@ -2461,7 +2467,7 @@ void fstWriterSetTimescale(void *ctx, int ts)
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
if(xc)
{
off_t fpos = ftello(xc->handle);
fst_off_t fpos = ftello(xc->handle);
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET);
fputc(ts & 255, xc->handle);
fflush(xc->handle);
@ -2519,7 +2525,7 @@ void fstWriterSetTimezero(void *ctx, int64_t tim)
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
if(xc)
{
off_t fpos = ftello(xc->handle);
fst_off_t fpos = ftello(xc->handle);
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET);
fstWriterUint64(xc->handle, (xc->timezero = tim));
fflush(xc->handle);
@ -3361,7 +3367,7 @@ char date[FST_HDR_DATE_SIZE + 1];
int64_t timezero;
char *filename, *filename_unpacked;
off_t hier_pos;
fst_off_t hier_pos;
uint32_t num_blackouts;
uint64_t *blackout_times;
@ -3376,10 +3382,10 @@ uint64_t *rvat_time_table;
uint64_t rvat_beg_tim, rvat_end_tim;
unsigned char *rvat_frame_data;
uint64_t rvat_frame_maxhandle;
off_t *rvat_chain_table;
fst_off_t *rvat_chain_table;
uint32_t *rvat_chain_table_lengths;
uint64_t rvat_vc_maxhandle;
off_t rvat_vc_start;
fst_off_t rvat_vc_start;
uint32_t *rvat_sig_offs;
int rvat_packtype;
@ -3418,7 +3424,7 @@ char *fh_nam;
};
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, off_t offset, int whence)
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, fst_off_t offset, int whence)
{
int rc = fseeko(stream, offset, whence);
@ -3911,11 +3917,11 @@ int pass_status = 1;
if(!xc->fh)
{
off_t offs_cache = ftello(xc->f);
fst_off_t offs_cache = ftello(xc->f);
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
off_t hl, uclen;
off_t clen = 0;
fst_off_t hl, uclen;
fst_off_t clen = 0;
gzFile zhandle = NULL;
int zfd;
int htyp = FST_BL_SKIP;
@ -4535,8 +4541,8 @@ return(1);
*/
int fstReaderInit(struct fstReaderContext *xc)
{
off_t blkpos = 0;
off_t endfile;
fst_off_t blkpos = 0;
fst_off_t endfile;
uint64_t seclen;
int sectype;
uint64_t vc_section_count_actual = 0;
@ -4548,7 +4554,7 @@ sectype = fgetc(xc->f);
if(sectype == FST_BL_ZWRAPPER)
{
FILE *fcomp;
off_t offpnt, uclen;
fst_off_t offpnt, uclen;
char gz_membuf[FST_GZIO_LEN];
gzFile zhandle;
int zfd;
@ -4981,15 +4987,15 @@ uint64_t *time_table = NULL;
uint64_t tsec_nitems;
unsigned int secnum = 0;
int blocks_skipped = 0;
off_t blkpos = 0;
fst_off_t blkpos = 0;
uint64_t seclen, beg_tim;
#ifdef FST_DEBUG
uint64_t end_tim;
#endif
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
off_t vc_start;
off_t indx_pntr, indx_pos;
off_t *chain_table = NULL;
fst_off_t vc_start;
fst_off_t indx_pntr, indx_pos;
fst_off_t *chain_table = NULL;
uint32_t *chain_table_lengths = NULL;
unsigned char *chain_cmem;
unsigned char *pnt;
@ -5105,7 +5111,7 @@ for(;;)
destlen = tsec_uclen;
sourcelen = tsec_clen;
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
if(tsec_uclen != tsec_clen)
{
@ -5346,11 +5352,11 @@ for(;;)
}
free(mu);
fstReaderFseeko(xc, xc->f, -((off_t)frame_clen), SEEK_CUR);
fstReaderFseeko(xc, xc->f, -((fst_off_t)frame_clen), SEEK_CUR);
}
}
fstReaderFseeko(xc, xc->f, (off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
fstReaderFseeko(xc, xc->f, (fst_off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
vc_maxhandle = fstReaderVarint64(xc->f);
vc_start = ftello(xc->f); /* points to '!' character */
@ -5380,7 +5386,7 @@ for(;;)
free(chain_table_lengths);
vc_maxhandle_largest = vc_maxhandle;
chain_table = (off_t *)calloc((vc_maxhandle+1), sizeof(off_t));
chain_table = (fst_off_t *)calloc((vc_maxhandle+1), sizeof(fst_off_t));
chain_table_lengths = (uint32_t *)calloc((vc_maxhandle+1), sizeof(uint32_t));
}
@ -6001,7 +6007,7 @@ return(buf);
char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf)
{
struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
off_t blkpos = 0, prev_blkpos;
fst_off_t blkpos = 0, prev_blkpos;
uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
int sectype;
unsigned int secnum = 0;
@ -6012,7 +6018,7 @@ uint64_t frame_uclen, frame_clen;
#ifdef FST_DEBUG
uint64_t mem_required_for_traversal;
#endif
off_t indx_pntr, indx_pos;
fst_off_t indx_pntr, indx_pos;
long chain_clen;
unsigned char *chain_cmem;
unsigned char *pnt;
@ -6074,7 +6080,7 @@ for(;;)
{
if((tim == end_tim) && (tim != xc->end_time))
{
off_t cached_pos = ftello(xc->f);
fst_off_t cached_pos = ftello(xc->f);
fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
sectype = fgetc(xc->f);
@ -6136,7 +6142,7 @@ ucdata = (unsigned char *)malloc(tsec_uclen);
destlen = tsec_uclen;
sourcelen = tsec_clen;
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
if(tsec_uclen != tsec_clen)
{
cdata = (unsigned char *)malloc(tsec_clen);
@ -6221,7 +6227,7 @@ chain_cmem = (unsigned char *)malloc(chain_clen);
fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET);
fstFread(chain_cmem, chain_clen, 1, xc->f);
xc->rvat_chain_table = (off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t));
xc->rvat_chain_table = (fst_off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(fst_off_t));
xc->rvat_chain_table_lengths = (uint32_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t));
pnt = chain_cmem;

View File

@ -35,7 +35,11 @@ extern "C" {
#include <ctype.h>
#include <zlib.h>
#include <inttypes.h>
#include <unistd.h>
#if defined(_MSC_VER)
#include "fst_win_unistd.h"
#else
#include <unistd.h>
#endif
#include <time.h>
#define FST_RDLOAD "FSTLOAD | "

View File

@ -637,27 +637,80 @@ std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
return output;
}
std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) {
// Double may lose precision, but sc_time_stamp has similar limit
std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix();
int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N
int prec = Verilated::threadContextp()->timeprecision(); // 0..-15
int shift = prec - userUnits + fracDigits; // 0..-15
double shiftd = vl_time_multiplier(shift);
double scaled = ld * shiftd;
QData fracDiv = static_cast<QData>(vl_time_multiplier(fracDigits));
QData whole = static_cast<QData>(scaled) / fracDiv;
QData fraction = static_cast<QData>(scaled) % fracDiv;
template <typename T>
std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) {
const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
const std::string suffix = ctxImpp->timeFormatSuffix();
const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15
const int fracDigits = ctxImpp->timeFormatPrecision(); // 0..N
const int shift = -userUnits + fracDigits + timeunit; // 0..-15
int digits = 0;
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", whole,
suffix.c_str());
if (std::numeric_limits<T>::is_integer) {
constexpr int b = 128;
constexpr int w = VL_WORDS_I(b);
WData tmp0[w], tmp1[w], tmp2[w], tmp3[w];
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
if (shift < 0) {
WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
} else {
WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
shifted = VL_MUL_W(w, tmp2, shifted, pow10);
}
WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
WDataInP max64Bit
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<vluint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
WData zero[w], ten[w];
VL_ZERO_W(b, zero);
VL_EXTEND_WI(b, 0, ten, 10);
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
char* ptr = buf + sizeof(buf) - 1;
*ptr = '\0';
while (VL_GT_W(w, v, zero)) {
--ptr;
WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
*ptr = "0123456789"[VL_SET_QW(mod)];
WData divided[w];
VL_DIV_WWW(b, divided, v, ten);
VL_ASSIGN_W(b, v, divided);
}
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s%s", ptr, suffix.c_str());
} else {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s.%0*" VL_PRI64 "u%s", ptr,
fracDigits, VL_SET_QW(frac), suffix.c_str());
}
} else {
const vluint64_t integer64 = VL_SET_QW(integer);
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", integer64,
suffix.c_str());
} else {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH,
"%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", integer64, fracDigits,
VL_SET_QW(frac), suffix.c_str());
}
}
} else {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s",
whole, fracDigits, fraction, suffix.c_str());
double shiftd = vl_time_multiplier(shift);
double scaled = ld * shiftd;
const double fracDiv = vl_time_multiplier(fracDigits);
const double whole = scaled / fracDiv;
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.0f%s", whole, suffix.c_str());
} else {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.*f%s", fracDigits, whole,
suffix.c_str());
}
}
int needmore = width - digits;
const int needmore = width - digits;
std::string padding;
if (needmore > 0) padding.append(needmore, ' '); // Pad with spaces
return left ? (tmp + padding) : (padding + tmp);
@ -752,7 +805,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
if (lbits) {} // UNUSED - always 64
if (fmt == '^') { // Realtime
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
output += _vl_vsformat_time(t_tmp, d, left, width);
const int timeunit = va_arg(ap, int);
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
} else {
std::string fmts(pctp, pos - pctp + 1);
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
@ -851,7 +905,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
case 't': { // Time
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
output += _vl_vsformat_time(t_tmp, static_cast<double>(ld), left, width);
const int timeunit = va_arg(ap, int);
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
break;
}
case 'b':
@ -2154,6 +2209,30 @@ double vl_time_multiplier(int scale) VL_PURE {
return pow10[scale];
}
}
vluint64_t vl_time_pow10(int n) {
static const vluint64_t pow10[20] = {
1ULL,
10ULL,
100ULL,
1000ULL,
10000ULL,
100000ULL,
1000000ULL,
10000000ULL,
100000000ULL,
1000000000ULL,
10000000000ULL,
100000000000ULL,
1000000000000ULL,
10000000000000ULL,
100000000000000ULL,
1000000000000000ULL,
10000000000000000ULL,
100000000000000000ULL,
1000000000000000000ULL,
};
return pow10[n];
}
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE {

View File

@ -1187,6 +1187,8 @@ inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
// Return time precision as multiplier of time units
double vl_time_multiplier(int scale) VL_PURE;
// Return power of 10. e.g. returns 100 if n==2
vluint64_t vl_time_pow10(int n) VL_PURE;
#ifdef VL_DEBUG
/// Evaluate statement if Verilated::debug() enabled
@ -1793,7 +1795,7 @@ static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP lwp) VL_M
}
return owp;
}
static void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
EData carry = 1;
for (int i = 0; i < words; ++i) {
EData word = ~owp_lwp[i] + carry;
@ -2487,8 +2489,7 @@ static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, WDataInP
}
return VL_CLEAN_II(obits, obits, lhs << rwp[0]);
}
static inline IData VL_SHIFTL_IIQ(int obits, int lbits, int rbits, IData lhs,
QData rhs) VL_MT_SAFE {
static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return VL_CLEAN_II(obits, obits, lhs << rhs);
}
@ -2501,8 +2502,7 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, WDataInP
// Above checks rwp[1]==0 so not needed in below shift
return VL_CLEAN_QQ(obits, obits, lhs << (static_cast<QData>(rwp[0])));
}
static inline QData VL_SHIFTL_QQQ(int obits, int lbits, int rbits, QData lhs,
QData rhs) VL_MT_SAFE {
static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
return VL_CLEAN_QQ(obits, obits, lhs << rhs);
}
@ -2567,11 +2567,11 @@ static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, WDataInP
// Above checks rwp[1]==0 so not needed in below shift
return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast<QData>(rwp[0])));
}
static inline IData VL_SHIFTR_IIQ(int obits, int, int rbits, IData lhs, QData rhs) VL_MT_SAFE {
static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
return VL_CLEAN_QQ(obits, obits, lhs >> rhs);
}
static inline QData VL_SHIFTR_QQQ(int obits, int, int rbits, QData lhs, QData rhs) VL_MT_SAFE {
static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
return VL_CLEAN_QQ(obits, obits, lhs >> rhs);
}
@ -2705,7 +2705,7 @@ static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP lwp, IData
static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP lwp, IData lsb,
IData width) VL_MT_SAFE {
int msb = lsb + width - 1;
if (VL_UNLIKELY(msb > lbits)) {
if (VL_UNLIKELY(msb >= lbits)) {
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
return VL_BITRSHIFT_W(lwp, lsb);

View File

@ -10,6 +10,7 @@
######################################################################
PERL = @PERL@
PYTHON3 = @PYTHON3@
CXX = @CXX@
LINK = @CXX@
AR = ar
@ -35,6 +36,7 @@ CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
VERILATOR_COVERAGE = $(PERL) $(VERILATOR_ROOT)/bin/verilator_coverage
VERILATOR_INCLUDER = $(PERL) $(VERILATOR_ROOT)/bin/verilator_includer
VERILATOR_CCACHE_REPORT = $(PYTHON3) $(VERILATOR_ROOT)/bin/verilator_ccache_report
######################################################################
# Make checks
@ -202,16 +204,19 @@ else
endif
# When archiving just objects (.o), use single $(AR) run
# 1. Make .tmp file with list of objects so don't exceed commend line
# 1. Make .verilator_deplist.tmp file with list of objects so don't exceed
# the command line limits when calling $(AR).
# The approach to write the dependency file is compatible with GNU Make 3,
# and can be simplified using the file function once GNU Make 4.x becomes
# the minimum supported version.
# When merging objects (.o) and archives (.a) additionally:
# 1. Extract object files from .a
# 2. Create a new archive from extracted .o and given .o
%.a:
%.a: | %.verilator_deplist.tmp
$(info Archive $(AR) -rcs $@ $^)
$(file >$@.tmp)
$(foreach L, $(filter-out %.a,$^), $(file >>$@.tmp, $L))
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>$@.verilator_deplist.tmp))
@if test $(words $(filter %.a,$^)) -eq 0; then \
$(AR) -rcs $@ @$@.tmp; \
$(AR) -rcs $@ @$@.verilator_deplist.tmp; \
else \
$(RM) -rf $@.tmpdir; \
for archive in $(filter %.a,$^); do \
@ -220,9 +225,13 @@ endif
$(AR) -x ../../$${archive}; \
cd ../..; \
done; \
$(AR) -rcs $@ @$@.tmp $@.tmpdir/*/*.o; \
$(AR) -rcs $@ @$@.verilator_deplist.tmp $@.tmpdir/*/*.o; \
fi \
; $(RM) -rf $@.tmp $@.tmpdir
; $(RM) -rf $@.verilator_deplist.tmp $@.tmpdir
# Truncate the dependency list file used in the %.a target above.
%.verilator_deplist.tmp:
echo "" > $@
$(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
@ -247,6 +256,42 @@ endif
#.cpp.o:
# $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
######################################################################
### ccache report
ifneq ($(findstring ccache-report,$(MAKECMDGOALS)),)
ifneq ($(OBJCACHE),ccache)
$(error ccache-report requires OBJCACHE to equal 'ccache')
endif
VK_OTHER_GOALS := $(strip $(subst ccache-report,,$(MAKECMDGOALS)))
ifeq ($(VK_OTHER_GOALS),)
$(error ccache-report must be used with at least one other explicit target)
endif
# Report ccache behaviour for this invocation of make
VK_CCACHE_LOGDIR := ccache-logs
VK_CCACHE_REPORT := $(VM_PREFIX)__ccache_report.txt
# Remove previous logfiles and report
$(shell rm -rf $(VK_CCACHE_LOGDIR) $(VK_CCACHE_REPORT))
$(VK_CCACHE_LOGDIR):
mkdir -p $@
$(VK_OBJS): | $(VK_CCACHE_LOGDIR)
$(VK_OBJS): export CCACHE_LOGFILE=$(VK_CCACHE_LOGDIR)/$@.log
$(VK_CCACHE_REPORT): $(VK_OBJS)
$(VERILATOR_CCACHE_REPORT) -o $@ $(VK_CCACHE_LOGDIR)
.PHONY: ccache-report
ccache-report: $(VK_CCACHE_REPORT)
@cat $<
# ccache-report runs last
ccache-report: $(VK_OTHER_GOALS)
endif
######################################################################
### Debugging

View File

@ -152,6 +152,8 @@ public:
/// Flush dump
void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.

View File

@ -27,6 +27,7 @@
#include "verilated.h"
#include <algorithm>
#include <array>
#include <deque>
#include <map>
#include <memory>

View File

@ -245,7 +245,7 @@ public: // But only for verilated*.cpp
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
// METHODS - timeformat
int timeFormatUnits() VL_MT_SAFE {
int timeFormatUnits() const VL_MT_SAFE {
if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE)
return timeprecision();
return m_s.m_timeFormatUnits;
@ -255,7 +255,7 @@ public: // But only for verilated*.cpp
void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; }
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
std::string timeFormatSuffix() VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock(m_timeDumpMutex);
return m_timeFormatSuffix;
}
@ -333,17 +333,20 @@ public: // But only for verilated*.cpp
}
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock(m_fdMutex);
if ((fdi & (1 << 31)) != 0) {
if (VL_BITISSET_I(fdi, 31)) {
// Non-MCD case
IData idx = VL_MASK_I(31) & fdi;
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
std::fclose(m_fdps[idx]);
m_fdps[idx] = (FILE*)0;
m_fdFree.push_back(idx);
} else {
// MCD case
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
// Starts at 1 to skip stdout
fdi >>= 1;
for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) {
std::fclose(m_fdps[i]);
m_fdps[i] = nullptr;
@ -375,7 +378,9 @@ private:
}
} else {
// MCD Case
for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
if (fdi & 1) fp.push_back(stdout);
fdi >>= 1;
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
}
}

View File

@ -99,7 +99,6 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
m_wrBufp = new char[m_wrChunkSize * 8];
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
m_writep = m_wrBufp;
m_suffixesp = nullptr;
}
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
@ -114,9 +113,6 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
dumpHeader();
// Get the direct access pointer to the code strings
m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data();
// When using rollover, the first chunk contains the header only.
if (m_rolloverMB) openNextImp(true);
}
@ -586,8 +582,10 @@ void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, i
//=============================================================================
// Trace rendering prinitives
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
const char* const suffixp = m_suffixesp + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
static inline void
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
// Copy the whole suffix (this avoid having hard to predict branches which
// helps a lot). Note: The maximum length of the suffix is
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
@ -605,6 +603,12 @@ void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
writep[5] = suffixp[5];
writep[6] = '\n'; // The 6th index is always '\n' if it's relevant, no need to fetch it.
#endif
}
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
// Now write back the write pointer incremented by the actual size of the
// suffix, which was stored in the last byte of the suffix buffer entry.
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];

View File

@ -80,7 +80,6 @@ private:
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
std::vector<char> m_suffixes; // VCD line end string codes + metadata
const char* m_suffixesp; // Pointer to first element of above
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
@ -369,6 +368,8 @@ public:
/// Flush dump
void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.

View File

@ -43,6 +43,12 @@
# define VL_ATTR_COLD __attribute__((cold))
# define VL_ATTR_HOT __attribute__((hot))
# define VL_ATTR_NORETURN __attribute__((noreturn))
// clang and gcc-8.0+ support no_sanitize("string") style attribute
# if defined(__clang__) || (__GNUC__ >= 8)
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment")))
#else // The entire undefined sanitizer has to be disabled for older gcc
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined))
#endif
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
# define VL_ATTR_PURE __attribute__((pure))
# define VL_ATTR_UNUSED __attribute__((unused))
@ -85,6 +91,9 @@
#ifndef VL_ATTR_NORETURN
# define VL_ATTR_NORETURN ///< Attribute that function does not ever return
#endif
#ifndef VL_ATTR_NO_SANITIZE_ALIGN
# define VL_ATTR_NO_SANITIZE_ALIGN ///< Attribute that the function contains intended unaligned access
#endif
#ifndef VL_ATTR_PRINTF
# define VL_ATTR_PRINTF(fmtArgNum) ///< Attribute for function with printf format checking
#endif
@ -428,6 +437,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
/// Return mask for EData with 1's where relevant bits are (0=all bits)
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors

View File

@ -47,25 +47,25 @@ obj_dbg:
.SUFFIXES:
.PHONY: ../bin/verilator_bin ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
.PHONY: ../bin/verilator_bin$(EXEEXT) ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
opt: ../bin/verilator_bin
opt: ../bin/verilator_bin$(EXEEXT)
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
../bin/verilator_bin: ../bin/verilator_bin_dbg
-cp -p $<$(EXEEXT) $@$(EXEEXT).tmp
-mv -f $@$(EXEEXT).tmp $@$(EXEEXT)
../bin/verilator_bin$(EXEEXT): ../bin/verilator_bin_dbg$(EXEEXT)
-cp -p $< $@.tmp
-mv -f $@.tmp $@
else
../bin/verilator_bin: obj_opt ../bin prefiles
../bin/verilator_bin$(EXEEXT): obj_opt ../bin prefiles
$(MAKE) -C obj_opt -j 1 TGT=../$@ -f ../Makefile_obj serial
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
endif
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
../bin/verilator_coverage_bin_dbg: obj_dbg ../bin prefiles
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj

View File

@ -72,7 +72,7 @@ CFG_LIBS = @CFG_LIBS@
#### End of system configuration section. ####
VPATH += . $(bldsrc) $(srcdir)
TGT = ../../verilator_bin
TGT = ../../verilator_bin$(EXEEXT)
#################
ifeq ($(VL_DEBUG),)
@ -181,6 +181,7 @@ RAW_OBJS = \
V3Depth.o \
V3DepthBlock.o \
V3Descope.o \
V3DupFinder.o \
V3EmitC.o \
V3EmitCInlines.o \
V3EmitCSyms.o \
@ -202,7 +203,8 @@ RAW_OBJS = \
V3GraphDfa.o \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hashed.o \
V3Hash.o \
V3Hasher.o \
V3HierBlock.o \
V3Inline.o \
V3Inst.o \
@ -221,6 +223,7 @@ RAW_OBJS = \
V3MergeCond.o \
V3Name.o \
V3Number.o \
V3OptionParser.o \
V3Options.o \
V3Order.o \
V3Os.o \

View File

@ -309,16 +309,16 @@ private:
}
}
virtual void visit(AstNodeIf* nodep) {
LatchDetectGraphVertex* parentp = m_graph.currentp();
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextNull(nodep->elsesp());
m_graph.currentp(parentp);
if (!nodep->isBoundsCheck()) {
LatchDetectGraphVertex* parentp = m_graph.currentp();
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextNull(nodep->elsesp());
m_graph.currentp(parentp);
}
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep) {}
//--------------------
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }

View File

@ -108,6 +108,7 @@ private:
// Like newFireAssert() but omits the asserts-on check
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message,
nullptr, nullptr);
dispp->fmtp()->timeunit(m_modp->timeunit());
AstNode* bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
bodysp->addNext(new AstStop(nodep->fileline(), true));

View File

@ -970,20 +970,6 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
&& (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)));
}
//======================================================================
// Static utilities
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
return os << std::hex << std::setw(2) << std::setfill('0') << rhs.depth() << "_"
<< std::setw(6) << std::setfill('0') << rhs.hshval();
}
V3Hash::V3Hash(const string& name) {
uint32_t val = 0;
for (const auto& c : name) val = val * 31 + c;
setBoth(1, val);
}
//======================================================================
// Debugging

View File

@ -1271,6 +1271,8 @@ public:
virtual ~AstNVisitor() { doDeletes(); }
/// Call visit()s on nodep
void iterate(AstNode* nodep);
/// Call visit()s on nodep
void iterateNull(AstNode* nodep);
/// Call visit()s on nodep's children
void iterateChildren(AstNode* nodep);
/// Call visit()s on nodep's children in backp() order
@ -1321,59 +1323,6 @@ inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) {
return os;
}
//######################################################################
// V3Hash -- Node hashing for V3Combine
class V3Hash final {
// A hash of a tree of nodes, consisting of 8 bits with the number of nodes in the hash
// and 24 bit value hash of relevant information about the node.
// A value of 0 is illegal
uint32_t m_both;
static const uint32_t M24 = ((1 << 24) - 1);
void setBoth(uint32_t depth, uint32_t hshval) {
if (depth == 0) depth = 1;
if (depth > 255) depth = 255;
m_both = (depth << 24) | (hshval & M24);
}
public:
// METHODS
bool isIllegal() const { return m_both == 0; }
uint32_t fullValue() const { return m_both; }
uint32_t depth() const { return (m_both >> 24) & 255; }
uint32_t hshval() const { return m_both & M24; }
// OPERATORS
bool operator==(const V3Hash& rh) const { return m_both == rh.m_both; }
bool operator!=(const V3Hash& rh) const { return m_both != rh.m_both; }
bool operator<(const V3Hash& rh) const { return m_both < rh.m_both; }
// CONSTRUCTORS
class Illegal {}; // for creator type-overload selection
class FullValue {}; // for creator type-overload selection
explicit V3Hash(Illegal) { m_both = 0; }
// Saving and restoring inside a userp
explicit V3Hash(const VNUser& u) { m_both = u.toInt(); }
V3Hash operator+=(const V3Hash& rh) {
setBoth(depth() + rh.depth(), (hshval() * 31 + rh.hshval()));
return *this;
}
// Creating from raw data (sameHash functions)
V3Hash() { setBoth(1, 0); }
// cppcheck-suppress noExplicitConstructor
V3Hash(uint32_t val) { setBoth(1, val); }
// cppcheck-suppress noExplicitConstructor
V3Hash(const void* vp) { setBoth(1, cvtToHash(vp)); }
// cppcheck-suppress noExplicitConstructor
V3Hash(const string& name);
V3Hash(V3Hash h1, V3Hash h2) { setBoth(1, h1.hshval() * 31 + h2.hshval()); }
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3) {
setBoth(1, (h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval());
}
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
setBoth(1, ((h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval()) * 31 + h4.hshval());
}
};
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
//######################################################################
// Callback base class to determine if node matches some formula
@ -1835,9 +1784,6 @@ public:
// statement is unlikely to be taken
virtual bool isUnlikely() const { return false; }
virtual int instrCount() const { return 0; }
virtual V3Hash sameHash() const {
return V3Hash(V3Hash::Illegal()); // Not a node that supports it
}
virtual bool same(const AstNode*) const { return true; }
// Iff has a data type; dtype() must be non null
virtual bool hasDType() const { return false; }
@ -1912,9 +1858,11 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
class AstNodeMath VL_NOT_FINAL : public AstNode {
// Math -- anything that's part of an expression tree
public:
protected:
AstNodeMath(AstType t, FileLine* fl)
: AstNode{t, fl} {}
public:
ASTNODE_BASE_FUNCS(NodeMath)
// METHODS
virtual void dump(std::ostream& str) const override;
@ -1932,9 +1880,11 @@ public:
class AstNodeTermop VL_NOT_FINAL : public AstNodeMath {
// Terminal operator -- a operator with no "inputs"
public:
protected:
AstNodeTermop(AstType t, FileLine* fl)
: AstNodeMath{t, fl} {}
public:
ASTNODE_BASE_FUNCS(NodeTermop)
// Know no children, and hot function, so skip iterator for speed
// See checkTreeIter also that asserts no children
@ -1945,12 +1895,14 @@ public:
class AstNodeUniop VL_NOT_FINAL : public AstNodeMath {
// Unary math
public:
protected:
AstNodeUniop(AstType t, FileLine* fl, AstNode* lhsp)
: AstNodeMath{t, fl} {
dtypeFrom(lhsp);
setOp1p(lhsp);
}
public:
ASTNODE_BASE_FUNCS(NodeUniop)
AstNode* lhsp() const { return op1p(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
@ -1965,18 +1917,19 @@ public:
virtual bool signedFlavor() const { return false; }
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
virtual int instrCount() const override { return widthInstrs(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstNodeBiop VL_NOT_FINAL : public AstNodeMath {
// Binary math
public:
protected:
AstNodeBiop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeMath{t, fl} {
setOp1p(lhs);
setOp2p(rhs);
}
public:
ASTNODE_BASE_FUNCS(NodeBiop)
// Clone single node, just get same type back.
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
@ -1997,19 +1950,20 @@ public:
virtual bool signedFlavor() const { return false; }
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
virtual int instrCount() const override { return widthInstrs(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstNodeTriop VL_NOT_FINAL : public AstNodeMath {
// Trinary math
public:
protected:
AstNodeTriop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNodeMath{t, fl} {
setOp1p(lhs);
setOp2p(rhs);
setOp3p(ths);
}
public:
ASTNODE_BASE_FUNCS(NodeTriop)
AstNode* lhsp() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
@ -2030,13 +1984,12 @@ public:
virtual bool sizeMattersRhs() const = 0; // True if output result depends on rhs size
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
virtual int instrCount() const override { return widthInstrs(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstNodeQuadop VL_NOT_FINAL : public AstNodeMath {
// Quaternary math
public:
protected:
AstNodeQuadop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths, AstNode* fhs)
: AstNodeMath{t, fl} {
setOp1p(lhs);
@ -2044,6 +1997,8 @@ public:
setOp3p(ths);
setOp4p(fhs);
}
public:
ASTNODE_BASE_FUNCS(NodeQuadop)
AstNode* lhsp() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
@ -2067,27 +2022,31 @@ public:
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size
virtual int instrCount() const override { return widthInstrs(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop {
// Binary math with commutative properties
public:
protected:
AstNodeBiCom(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiop{t, fl, lhs, rhs} {}
public:
ASTNODE_BASE_FUNCS(NodeBiCom)
};
class AstNodeBiComAsv VL_NOT_FINAL : public AstNodeBiCom {
// Binary math with commutative & associative properties
public:
protected:
AstNodeBiComAsv(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiCom{t, fl, lhs, rhs} {}
public:
ASTNODE_BASE_FUNCS(NodeBiComAsv)
};
class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
public:
protected:
AstNodeCond(AstType t, FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
: AstNodeTriop{t, fl, condp, expr1p, expr2p} {
if (expr1p) {
@ -2096,6 +2055,8 @@ public:
dtypeFrom(expr2p);
}
}
public:
ASTNODE_BASE_FUNCS(NodeCond)
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
const V3Number& ths) override;
@ -2124,13 +2085,15 @@ class AstNodeBlock VL_NOT_FINAL : public AstNode {
private:
string m_name; // Name of block
bool m_unnamed; // Originally unnamed (name change does not affect this)
public:
protected:
AstNodeBlock(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
: AstNode{t, fl}
, m_name{name} {
addNOp1p(stmtsp);
m_unnamed = (name == "");
}
public:
ASTNODE_BASE_FUNCS(NodeBlock)
virtual void dump(std::ostream& str) const override;
virtual string name() const override { return m_name; } // * = Block name
@ -2143,13 +2106,15 @@ public:
class AstNodePreSel VL_NOT_FINAL : public AstNode {
// Something that becomes an AstSel
public:
protected:
AstNodePreSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* rhs, AstNode* ths)
: AstNode{t, fl} {
setOp1p(fromp);
setOp2p(rhs);
setNOp3p(ths);
}
public:
ASTNODE_BASE_FUNCS(NodePreSel)
AstNode* fromp() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
@ -2160,17 +2125,18 @@ public:
void thsp(AstNode* nodep) { return setOp3p(nodep); }
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
// METHODS
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
// IEEE procedure: initial, final, always
public:
protected:
AstNodeProcedure(AstType t, FileLine* fl, AstNode* bodysp)
: AstNode{t, fl} {
addNOp2p(bodysp);
}
public:
ASTNODE_BASE_FUNCS(NodeProcedure)
// METHODS
virtual void dump(std::ostream& str) const override;
@ -2182,10 +2148,12 @@ public:
class AstNodeStmt VL_NOT_FINAL : public AstNode {
// Statement -- anything that's directly under a function
bool m_statement; // Really a statement (e.g. not a function with return)
public:
protected:
AstNodeStmt(AstType t, FileLine* fl, bool statement = true)
: AstNode{t, fl}
, m_statement{statement} {}
public:
ASTNODE_BASE_FUNCS(NodeStmt)
// METHODS
bool isStatement() const { return m_statement; } // Really a statement
@ -2198,13 +2166,15 @@ public:
};
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
public:
protected:
AstNodeAssign(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeStmt{t, fl} {
setOp1p(rhsp);
setOp2p(lhsp);
dtypeFrom(lhsp);
}
public:
ASTNODE_BASE_FUNCS(NodeAssign)
// Clone single node, just get same type back.
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
@ -2216,14 +2186,13 @@ public:
virtual bool hasDType() const override { return true; }
virtual bool cleanRhs() const { return true; }
virtual int instrCount() const override { return widthInstrs(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
virtual string verilogKwd() const override { return "="; }
virtual bool brokeLhsMustBeLvalue() const = 0;
};
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
public:
protected:
AstNodeFor(AstType t, FileLine* fl, AstNode* initsp, AstNode* condp, AstNode* incsp,
AstNode* bodysp)
: AstNodeStmt{t, fl} {
@ -2232,6 +2201,8 @@ public:
addNOp3p(incsp);
addNOp4p(bodysp);
}
public:
ASTNODE_BASE_FUNCS(NodeFor)
AstNode* initsp() const { return op1p(); } // op1 = initial statements
AstNode* condp() const { return op2p(); } // op2 = condition to continue
@ -2239,20 +2210,23 @@ public:
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
private:
VBranchPred m_branchPred; // Branch prediction as taken/untaken?
public:
bool m_isBoundsCheck; // True if this if node was inserted for array bounds checking
protected:
AstNodeIf(AstType t, FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
: AstNodeStmt{t, fl} {
setOp1p(condp);
addNOp2p(ifsp);
addNOp3p(elsesp);
isBoundsCheck(false);
}
public:
ASTNODE_BASE_FUNCS(NodeIf)
AstNode* condp() const { return op1p(); } // op1 = condition
AstNode* ifsp() const { return op2p(); } // op2 = list of true statements
@ -2263,19 +2237,22 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isGateDedupable() const override { return true; }
virtual int instrCount() const override { return instrCountBranch(); }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode* samep) const override { return true; }
void branchPred(VBranchPred flag) { m_branchPred = flag; }
VBranchPred branchPred() const { return m_branchPred; }
void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; }
bool isBoundsCheck() const { return m_isBoundsCheck; }
};
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
public:
protected:
AstNodeCase(AstType t, FileLine* fl, AstNode* exprp, AstNode* casesp)
: AstNodeStmt{t, fl} {
setOp1p(exprp);
addNOp2p(casesp);
}
public:
ASTNODE_BASE_FUNCS(NodeCase)
virtual int instrCount() const override { return instrCountBranch(); }
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
@ -2299,7 +2276,7 @@ private:
string m_hiernameToUnprot; // Scope converted into name-> for emitting
bool m_hierThis = false; // Hiername points to "this" function
public:
protected:
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
: AstNodeMath{t, fl}
, m_access{access}
@ -2313,6 +2290,8 @@ public:
// May have varp==nullptr
this->varp(varp);
}
public:
ASTNODE_BASE_FUNCS(NodeVarRef)
virtual void dump(std::ostream& str) const override;
virtual bool hasDType() const override { return true; }
@ -2346,15 +2325,16 @@ class AstNodeText VL_NOT_FINAL : public AstNode {
private:
string m_text;
public:
protected:
// Node that simply puts text into the output stream
AstNodeText(AstType t, FileLine* fl, const string& textp)
: AstNode{t, fl} {
m_text = textp; // Copy it
}
public:
ASTNODE_BASE_FUNCS(NodeText)
virtual void dump(std::ostream& str = std::cout) const override;
virtual V3Hash sameHash() const override { return V3Hash(text()); }
virtual bool same(const AstNode* samep) const override {
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
return text() == asamep->text();
@ -2375,7 +2355,7 @@ private:
// Unique number assigned to each dtype during creation for IEEE matching
static int s_uniqueNum;
public:
protected:
// CONSTRUCTORS
AstNodeDType(AstType t, FileLine* fl)
: AstNode{t, fl} {
@ -2383,6 +2363,8 @@ public:
m_widthMin = 0;
m_generic = false;
}
public:
ASTNODE_BASE_FUNCS(NodeDType)
// ACCESSORS
virtual void dump(std::ostream& str) const override;
@ -2473,16 +2455,21 @@ private:
bool m_packed;
bool m_isFourstate;
MemberNameMap m_members;
const int m_uniqueNum;
public:
protected:
AstNodeUOrStructDType(AstType t, FileLine* fl, VSigning numericUnpack)
: AstNodeDType{t, fl} {
: AstNodeDType{t, fl}
, m_uniqueNum{uniqueNumInc()} {
// VSigning::NOSIGN overloaded to indicate not packed
m_packed = (numericUnpack != VSigning::NOSIGN);
m_isFourstate = false; // V3Width computes
numeric(VSigning::fromBool(numericUnpack.isSigned()));
}
public:
ASTNODE_BASE_FUNCS(NodeUOrStructDType)
int uniqueNum() const { return m_uniqueNum; }
virtual const char* broken() const override;
virtual void dump(std::ostream& str) const override;
virtual bool isCompound() const override { return false; } // Because don't support unpacked
@ -2534,9 +2521,11 @@ class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
private:
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable
public:
protected:
AstNodeArrayDType(AstType t, FileLine* fl)
: AstNodeDType{t, fl} {}
public:
ASTNODE_BASE_FUNCS(NodeArrayDType)
virtual void dump(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const override;
@ -2559,9 +2548,6 @@ public:
&& rangenp()->sameTree(asamep->rangenp())
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
}
virtual V3Hash sameHash() const override {
return V3Hash(V3Hash(m_refDTypep), V3Hash(hi()), V3Hash(lo()));
}
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
@ -2594,9 +2580,11 @@ public:
class AstNodeSel VL_NOT_FINAL : public AstNodeBiop {
// Single bit range extraction, perhaps with non-constant selection or array selection
public:
protected:
AstNodeSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* bitp)
: AstNodeBiop{t, fl, fromp, bitp} {}
public:
ASTNODE_BASE_FUNCS(NodeSel)
AstNode* fromp() const {
return op1p();
@ -2610,11 +2598,13 @@ public:
class AstNodeStream VL_NOT_FINAL : public AstNodeBiop {
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
public:
protected:
AstNodeStream(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeBiop{t, fl, lhsp, rhsp} {
if (lhsp->dtypep()) dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED);
}
public:
ASTNODE_BASE_FUNCS(NodeStream)
};
@ -2629,28 +2619,19 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
string m_hiernameToUnprot;
string m_argTypes;
public:
protected:
AstNodeCCall(AstType t, FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr)
: AstNodeStmt{t, fl, true}
, m_funcp{funcp} {
addNOp2p(argsp);
}
// Replacement form for V3Combine
// Note this removes old attachments from the oldp
AstNodeCCall(AstType t, AstNodeCCall* oldp, AstCFunc* funcp)
: AstNodeStmt{t, oldp->fileline(), true}
, m_funcp{funcp} {
m_hiernameToProt = oldp->hiernameToProt();
m_hiernameToUnprot = oldp->hiernameToUnprot();
m_argTypes = oldp->argTypes();
if (oldp->argsp()) addNOp2p(oldp->argsp()->unlinkFrBackWithNext());
}
public:
ASTNODE_BASE_FUNCS(NodeCCall)
virtual void dump(std::ostream& str = std::cout) const override;
virtual void cloneRelink() override;
virtual const char* broken() const override;
virtual int instrCount() const override { return instrCountCall(); }
virtual V3Hash sameHash() const override { return V3Hash(funcp()); }
virtual bool same(const AstNode* samep) const override {
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
@ -2696,7 +2677,7 @@ private:
bool m_pureVirtual : 1; // Pure virtual
bool m_virtual : 1; // Virtual method in class
VLifetime m_lifetime; // Lifetime
public:
protected:
AstNodeFTask(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
: AstNode{t, fl}
, m_name{name}
@ -2720,6 +2701,8 @@ public:
addNOp3p(stmtsp);
cname(name); // Might be overridden by dpi import/export
}
public:
ASTNODE_BASE_FUNCS(NodeFTask)
virtual void dump(std::ostream& str = std::cout) const override;
virtual string name() const override { return m_name; } // * = Var name
@ -2796,7 +2779,7 @@ private:
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
bool m_pli = false; // Pli system call ($name)
public:
protected:
AstNodeFTaskRef(AstType t, FileLine* fl, bool statement, AstNode* namep, AstNode* pinsp)
: AstNodeStmt{t, fl, statement} {
setOp1p(namep);
@ -2807,6 +2790,8 @@ public:
, m_name{name} {
addNOp3p(pinsp);
}
public:
ASTNODE_BASE_FUNCS(NodeFTaskRef)
virtual const char* broken() const override;
virtual void cloneRelink() override {
@ -2862,7 +2847,7 @@ private:
VLifetime m_lifetime; // Lifetime
VTimescale m_timeunit; // Global time unit
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
public:
protected:
AstNodeModule(AstType t, FileLine* fl, const string& name)
: AstNode{t, fl}
, m_name{name}
@ -2875,6 +2860,8 @@ public:
, m_internal{false}
, m_recursive{false}
, m_recursiveClone{false} {}
public:
ASTNODE_BASE_FUNCS(NodeModule)
virtual void dump(std::ostream& str) const override;
virtual bool maybePointedTo() const override { return true; }
@ -2922,21 +2909,22 @@ public:
class AstNodeRange VL_NOT_FINAL : public AstNode {
// A range, sized or unsized
public:
protected:
AstNodeRange(AstType t, FileLine* fl)
: AstNode{t, fl} {}
public:
ASTNODE_BASE_FUNCS(NodeRange)
virtual void dump(std::ostream& str) const override;
};
//######################################################################
#include "V3AstNodes__gen.h"
//######################################################################
// Inline AstNVisitor METHODS
inline void AstNVisitor::iterate(AstNode* nodep) { nodep->accept(*this); }
inline void AstNVisitor::iterateNull(AstNode* nodep) {
if (VL_LIKELY(nodep)) nodep->accept(*this);
}
inline void AstNVisitor::iterateChildren(AstNode* nodep) { nodep->iterateChildren(*this); }
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
nodep->iterateChildrenBackwards(*this);
@ -2955,71 +2943,7 @@ inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
}
//######################################################################
// Inline ACCESSORS
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}
inline int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
}
inline bool AstNode::isDouble() const {
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
}
inline bool AstNode::isString() const {
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
}
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
inline bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
}
inline bool AstNode::isNeqZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
}
inline bool AstNode::isOne() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
}
inline bool AstNode::isAllOnes() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
}
inline bool AstNode::isAllOnesV() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
}
inline bool AstNode::sameTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, false);
}
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, true);
}
inline void AstNodeVarRef::varp(AstVar* varp) {
m_varp = varp;
dtypeFrom(varp);
}
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
inline const char* AstNodeFTaskRef::broken() const {
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
inline void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
}
#include "V3AstNodes.h"
#endif // Guard

93
src/V3AstInlines.h Normal file
View File

@ -0,0 +1,93 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Ast node inline functions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3ASTINLINES_H_
#define VERILATOR_V3ASTINLINES_H_
#ifndef VERILATOR_V3ASTNODES_H_
#error "Use V3Ast.h as the include"
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
#endif
//######################################################################
// Inline ACCESSORS
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}
inline int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
}
inline bool AstNode::isDouble() const {
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
}
inline bool AstNode::isString() const {
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
}
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
inline bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
}
inline bool AstNode::isNeqZero() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
}
inline bool AstNode::isOne() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
}
inline bool AstNode::isAllOnes() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
}
inline bool AstNode::isAllOnesV() const {
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
}
inline bool AstNode::sameTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, false);
}
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, true);
}
inline void AstNodeVarRef::varp(AstVar* varp) {
m_varp = varp;
dtypeFrom(varp);
}
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
inline const char* AstNodeFTaskRef::broken() const {
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
inline void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
}
#endif // Guard

View File

@ -25,6 +25,8 @@
#include "V3String.h"
#include "V3EmitCBase.h"
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
#include <iomanip>
#include <vector>
@ -214,7 +216,7 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
AstExecGraph::AstExecGraph(FileLine* fileline)
: AstNode{AstType::atExecGraph, fileline} {
: ASTGEN_SUPER_ExecGraph(fileline) {
m_depGraphp = new V3Graph;
}
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }

File diff suppressed because it is too large Load Diff

View File

@ -32,63 +32,99 @@
#include "V3CCtors.h"
#include <algorithm>
#include <map>
#include <list>
class VCtorType final {
public:
enum en : uint8_t { MODULE, CLASS, COVERAGE };
class V3CCtorsVisitor final {
private:
string m_basename;
string m_argsp;
string m_callargsp;
AstNodeModule* m_modp; // Current module
AstCFunc* m_tlFuncp; // Top level function being built
AstCFunc* m_funcp; // Current function
enum en m_e;
public:
// cppcheck-suppress noExplicitConstructor
inline VCtorType(en _e)
: m_e{_e} {}
bool isClass() const { return m_e == CLASS; }
bool isCoverage() const { return m_e == COVERAGE; }
};
class V3CCtorsBuilder final {
private:
AstNodeModule* const m_modp; // Current module/class
const string m_basename;
const VCtorType m_type; // What kind of constructor are we creating
std::list<AstCFunc*> m_newFunctions; // Created functions, latest is at back
int m_numStmts = 0; // Number of statements output
int m_funcNum = 0; // Function number being built
AstCFunc* makeNewFunc() {
const int funcNum = m_newFunctions.size();
const string funcName = m_basename + "_" + cvtToStr(funcNum);
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
funcp->isStatic(!m_type.isClass()); // Class constructors are non static
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
string preventUnusedStmt;
if (m_type.isClass()) {
funcp->argTypes(EmitCBaseVisitor::symClassVar());
preventUnusedStmt = "if (false && vlSymsp) {}";
} else if (m_type.isCoverage()) {
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self, "
+ EmitCBaseVisitor::symClassVar() + ", bool first");
preventUnusedStmt = "if (false && self && vlSymsp && first) {}";
} else { // Module
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self");
preventUnusedStmt = "if (false && self) {}";
}
preventUnusedStmt += " // Prevent unused\n";
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
m_modp->addStmtp(funcp);
m_numStmts = 0;
return funcp;
}
public:
void add(AstNode* nodep) {
if (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < m_numStmts) {
m_funcp = nullptr;
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
m_newFunctions.push_back(makeNewFunc());
}
if (!m_funcp) {
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum),
nullptr, "void");
m_funcp->isStatic(false);
m_funcp->declPrivate(true);
m_funcp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
m_funcp->argTypes(m_argsp);
m_modp->addStmtp(m_funcp);
// Add a top call to it
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
callp->argTypes(m_callargsp);
m_tlFuncp->addStmtsp(callp);
m_numStmts = 0;
}
m_funcp->addStmtsp(nodep);
m_newFunctions.back()->addStmtsp(nodep);
m_numStmts += 1;
}
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp = "",
const string& callargsp = "", const string& stmt = "") {
m_basename = basename;
m_argsp = argsp;
m_callargsp = callargsp;
m_modp = nodep;
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, nullptr, "void");
m_tlFuncp->declPrivate(true);
m_tlFuncp->isStatic(false);
m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
m_tlFuncp->argTypes(m_argsp);
if (stmt != "") m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
m_funcp = m_tlFuncp;
m_modp->addStmtp(m_tlFuncp);
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
: m_modp(nodep)
, m_basename{basename}
, m_type(type) {
// Note: The constructor is always called, even if empty, so we must always create at least
// one.
m_newFunctions.push_back(makeNewFunc());
}
~V3CCtorsVisitor() = default;
~V3CCtorsBuilder() {
if (m_newFunctions.size() == 1) {
// No split was necessary, rename the one function to the basename
m_newFunctions.front()->name(m_basename);
} else {
// Split was necessary, create root function and call all others from that
AstCFunc* const rootFuncp = makeNewFunc();
rootFuncp->name(m_basename);
for (AstCFunc* const funcp : m_newFunctions) {
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
if (m_type.isClass()) {
callp->argTypes("vlSymsp");
} else if (m_type.isCoverage()) {
callp->argTypes("self, vlSymsp, first");
} else { // Module
callp->argTypes("self");
}
rootFuncp->addStmtsp(callp);
}
}
};
private:
VL_UNCOPYABLE(V3CCtorsVisitor);
VL_UNCOPYABLE(V3CCtorsBuilder);
};
//######################################################################
@ -140,32 +176,24 @@ void V3CCtors::cctorsAll() {
modp = VN_CAST(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsVisitor var_reset(
modp, "_ctor_var_reset",
(VN_IS(modp, Class) ? EmitCBaseVisitor::symClassVar() : ""),
(VN_IS(modp, Class) ? "vlSymsp" : ""),
(VN_IS(modp, Class) ? "if (false && vlSymsp) {} // Prevent unused\n" : ""));
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
var_reset.add(
new AstCReset(varp->fileline(),
new AstVarRef(varp->fileline(), varp, VAccess::WRITE)));
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
var_reset.add(new AstCReset(varp->fileline(), vrefp));
}
}
}
}
if (v3Global.opt.coverage()) {
V3CCtorsVisitor configure_coverage(
modp, "_configure_coverage", EmitCBaseVisitor::symClassVar() + ", bool first",
"vlSymsp, first", "if (false && vlSymsp && first) {} // Prevent unused\n");
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
AstNode* backp = coverp->backp();
coverp->unlinkFrBack();
configure_coverage.add(coverp);
np = backp;
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
np = coverp->backp();
configure_coverage.add(coverp->unlinkFrBack());
}
}
}

View File

@ -301,20 +301,6 @@ private:
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
virtual void visit(AstAlwaysPost* nodep) override {
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
nodep->addNextHere(stmtsp);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
virtual void visit(AstAlwaysPostponed* nodep) override {
if (AstNode* stmtsp = nodep->bodysp()) {
stmtsp->unlinkFrBackWithNext();
nodep->addNextHere(stmtsp);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
virtual void visit(AstCoverToggle* nodep) override {
// nodep->dumpTree(cout, "ct:");
// COVERTOGGLE(INC, ORIG, CHANGE) ->

View File

@ -15,19 +15,8 @@
//*************************************************************************
// V3Combine's Transformations:
//
// For every function that we spit out
// Examine code to find largest common blocks
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
// GO through table
// Lookup in hash, while next of each statement match, grow that common block
// Foreach common block
// If common block large enough (> 20 statements) & used 2x or more
// Make new function
// Move common block to function
// Replace each common block ref with funccall
//
// Combine identical CFuncs by retaining only a single copy
// Also drop empty CFuncs
//*************************************************************************
#include "config_build.h"
@ -35,7 +24,7 @@
#include "V3Global.h"
#include "V3Combine.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include "V3Ast.h"
@ -45,12 +34,6 @@
//######################################################################
#ifdef VL_COMBINE_STATEMENTS
constexpr int COMBINE_MIN_STATEMENTS = 50; // Min # of statements to be worth making a function
#endif
//######################################################################
class CombBaseVisitor VL_NOT_FINAL : public AstNVisitor {
protected:
// STATE
@ -67,8 +50,7 @@ class CombCallVisitor final : CombBaseVisitor {
// Find all CCALLS of each CFUNC, so that we can later rename them
private:
// NODE STATE
using CallMmap = std::multimap<AstCFunc*, AstCCall*>;
CallMmap m_callMmap; // Associative array of {function}{call}
std::multimap<AstCFunc*, AstCCall*> m_callMmap; // Associative array of {function}{call}
// METHODS
public:
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
@ -81,44 +63,32 @@ public:
// Note: m_callMmap modified in loop, so not using equal_range.
for (auto it = m_callMmap.find(oldfuncp); it != m_callMmap.end();
it = m_callMmap.find(oldfuncp)) {
AstCCall* callp = it->second;
if (!callp->user3()) { // !already done
UINFO(4, " Called " << callp << endl);
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
"Call list broken, points to call w/different func");
if (newfuncp) {
AstCCall* newp = new AstCCall(callp, newfuncp);
// Special new AstCCall form above transfers children of callp to newfuncp
callp->replaceWith(newp);
addCall(newp); // Fix the table
} else { // Just deleting empty function
callp->unlinkFrBack();
}
callp->user3(true); // Dead now
VL_DO_DANGLING(pushDeletep(callp), callp);
AstCCall* const oldp = it->second;
UINFO(4, " Called " << oldp << endl);
UASSERT_OBJ(oldp->funcp() == oldfuncp, oldp,
"Call list broken, points to call w/different func");
if (newfuncp) {
// Replace call to oldfuncp with call to newfuncp
AstNode* const argsp
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
newp->hiernameToProt(oldp->hiernameToProt());
newp->hiernameToUnprot(oldp->hiernameToUnprot());
newp->argTypes(oldp->argTypes());
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
oldp->replaceWith(newp);
} else {
// Just deleting empty function
oldp->unlinkFrBack();
}
// It is safe to unconditionally remove this entry here as the above
// 'if' would never be entered again for this entry (we set user3).
// The only other place where m_callMmap is looked up is deleteCall
// below, but that is only ever called straight after an addCall
// of the node being deleted, so it won't miss this entry.
m_callMmap.erase(it); // Fix the table
VL_DO_DANGLING(pushDeletep(oldp), oldp);
m_callMmap.erase(it); // Fix the table, This call has been replaced
}
}
// METHODS
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
void deleteCall(AstCCall* nodep) {
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
= m_callMmap.equal_range(nodep->funcp());
for (auto nextit = eqrange.first; nextit != eqrange.second;) {
const auto eqit = nextit++;
AstCCall* callp = eqit->second;
if (callp == nodep) {
m_callMmap.erase(eqit);
return;
}
}
nodep->v3fatalSrc("deleteCall node not found in table");
void addCall(AstCCall* nodep) {
if (nodep->funcp()->dontCombine()) return;
m_callMmap.emplace(nodep->funcp(), nodep);
}
private:
@ -136,26 +106,6 @@ public:
void main(AstNetlist* nodep) { iterate(nodep); }
};
//######################################################################
// Combine marking function
class CombMarkVisitor final : CombBaseVisitor {
// Mark all nodes under specified one.
private:
// OUTPUT:
// AstNode::user3() -> bool. True to indicate duplicated
// VISITORS
virtual void visit(AstNode* nodep) override {
nodep->user3(true);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit CombMarkVisitor(AstNode* nodep) { iterate(nodep); }
virtual ~CombMarkVisitor() override = default;
};
//######################################################################
// Combine state, as a visitor of each AstNode
@ -163,312 +113,100 @@ class CombineVisitor final : CombBaseVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNodeStmt::user() -> bool. True if iterated already
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is
// illegal)
AstUser1InUse m_inuser1;
AstUser3InUse m_inuser3;
// AstUser4InUse part of V3Hashed
AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs
// AstUser4InUse part of V3Hasher in V3DupFinder
// STATE
enum CombineState : uint8_t { STATE_IDLE, STATE_HASH, STATE_DUP };
VDouble0 m_statCombs; // Statistic tracking
CombineState m_state = STATE_IDLE; // Major state
AstNodeModule* m_modp = nullptr; // Current module
AstCFunc* m_cfuncp = nullptr; // Current function
VDouble0 m_cfuncsCombined; // Statistic tracking
CombCallVisitor m_call; // Tracking of function call users
int m_modNFuncs = 0; // Number of functions made
#ifdef VL_COMBINE_STATEMENTS
AstNode* m_walkLast1p = nullptr; // Final node that is the same in duplicate list
#endif
AstNode* m_walkLast2p = nullptr; // Final node that is the same in duplicate list
V3Hashed m_hashed; // Hash for every node in module
V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module
// METHODS
void hashStatement(AstNode* nodep) {
// Compute hash on entire tree of this statement
m_hashed.hashAndInsert(nodep);
// UINFO(9, " stmthash " << hex << nodep->user4() << " " << nodep << endl);
}
#ifdef VL_COMBINE_STATEMENTS
void hashFunctions(AstCFunc* nodep) {
// Compute hash of all statement trees in the function
VL_RESTORER(m_state);
{
m_state = STATE_HASH;
iterate(nodep);
}
}
#endif
void walkEmptyFuncs() {
for (const auto& itr : m_hashed) {
AstNode* node1p = itr.second;
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
if (oldfuncp && oldfuncp->emptyBody() && !oldfuncp->dontCombine()) {
UINFO(5, " EmptyFunc " << std::hex << V3Hash(oldfuncp->user4p()) << " "
<< oldfuncp << endl);
// Mark user3p on entire old tree, so we don't process it more
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, nullptr);
oldfuncp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
}
for (const auto& itr : m_dupFinder) {
AstCFunc* const oldfuncp = VN_CAST(itr.second, CFunc);
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
if (!oldfuncp->emptyBody()) continue;
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
"dontCombine function should not be in hash");
// Remove calls to empty function
UASSERT_OBJ(!oldfuncp->user3(), oldfuncp, "Should not be processed yet");
UINFO(5, " Drop empty CFunc " << itr.first << " " << oldfuncp << endl);
oldfuncp->user3SetOnce(); // Mark replaced
m_call.replaceFunc(oldfuncp, nullptr);
oldfuncp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
}
}
void walkDupFuncs() {
// Do non-slow first as then favors naming functions based on fast name
for (int slow = 0; slow < 2; ++slow) {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* node1p = it->second;
AstCFunc* cfunc1p = VN_CAST(node1p, CFunc);
if (!cfunc1p) continue;
// cppcheck-suppress compareBoolExpressionWithInt
if (cfunc1p->slow() != slow) continue;
V3Hash hashval = it->first;
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
AstNode* node2p = eqit->second;
if (!(eqit->first == hashval)) break;
if (node1p == node2p) continue; // Identical iterator
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
// Replace AstCCall's that point here
replaceFuncWFunc(VN_CAST(node2p, CFunc), cfunc1p);
// Replacement may promote a slow routine to fast path
if (!VN_CAST(node2p, CFunc)->slow()) cfunc1p->slow(false);
}
for (const bool slow : {false, true}) {
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
AstCFunc* const newfuncp = VN_CAST(newIt->second, CFunc);
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
if (newfuncp->user3()) continue; // Already replaced
if (newfuncp->slow() != slow) continue;
auto oldIt = newIt;
++oldIt; // Skip over current position
for (; oldIt != m_dupFinder.end(); ++oldIt) {
AstCFunc* const oldfuncp = VN_CAST(oldIt->second, CFunc);
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
"Same function hashed multiple times");
if (newIt->first != oldIt->first) break; // Iterate over same hashes only
if (oldfuncp->user3()) continue; // Already replaced
if (!newfuncp->sameTree(oldfuncp)) continue; // Different functions
// Replace calls to oldfuncp with calls to newfuncp
UINFO(5, " Replace CFunc " << newIt->first << " " << newfuncp << endl);
UINFO(5, " with " << oldIt->first << " " << oldfuncp << endl);
++m_cfuncsCombined;
oldfuncp->user3SetOnce(); // Mark replaced
m_call.replaceFunc(oldfuncp, newfuncp);
oldfuncp->unlinkFrBack();
// Replacement may promote a slow routine to fast path
if (!oldfuncp->slow()) newfuncp->slow(false);
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
}
}
}
}
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
UINFO(5, " DupFunc " << std::hex << V3Hash(newfuncp->user4p()) << " " << newfuncp
<< endl);
UINFO(5, " and " << std::hex << V3Hash(oldfuncp->user4p()) << " " << oldfuncp
<< endl);
// Mark user3p on entire old tree, so we don't process it more
++m_statCombs;
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, newfuncp);
oldfuncp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
}
#ifdef VL_COMBINE_STATEMENTS
void replaceOnlyCallFunc(AstCCall* nodep) {
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
// oldfuncp->dumpTree(cout, "MAYDEL: ");
if (nodep->nextp() == nullptr && oldfuncp->initsp() == nullptr
&& oldfuncp->stmtsp() == nodep && oldfuncp->finalsp() == nullptr) {
UINFO(9, " Function only has call " << oldfuncp << endl);
m_call.deleteCall(nodep);
CombMarkVisitor visitor(oldfuncp);
VL_DO_DANGLING(replaceFuncWFunc(oldfuncp, nodep->funcp()), nodep);
}
}
}
void walkDupCodeStart(AstNode* node1p) {
V3Hash hashval(node1p->user4p());
// UINFO(4," STMT " << hashval << " " << node1p << endl);
//
int bestDepth = 0; // Best substitution found in the search
AstNode* bestNode2p = nullptr;
AstNode* bestLast1p = nullptr;
AstNode* bestLast2p = nullptr;
//
std::pair<V3Hashed::iterator, V3Hashed::iterator> eqrange
= m_hashed.mmap().equal_range(hashval);
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (node1p == node2p) continue;
//
// We need to mark iteration to prevent matching code inside
// code (abab matching in ababab)
AstNode::user1ClearTree(); // user1p() used on entire tree
m_walkLast1p = nullptr;
m_walkLast2p = nullptr;
int depth = walkDupCodeNext(node1p, node2p, 1);
if (depth > COMBINE_MIN_STATEMENTS && depth > bestDepth) {
bestDepth = depth;
bestNode2p = node2p;
bestLast1p = m_walkLast1p;
bestLast2p = m_walkLast2p;
}
}
if (bestDepth) {
// Found a replacement
UINFO(5, " Duplicate of depth " << bestDepth << endl);
UINFO(5, " DupFunc "
<< " " << node1p << endl);
UINFO(5, " and "
<< " " << bestNode2p << endl);
UINFO(5, " Through "
<< " " << bestLast1p << endl);
UINFO(5, " and "
<< " " << bestLast2p << endl);
//
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
}
}
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
// Find number of common statements between the two node1p_nextp's...
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
V3Hash hashval(node1p->user4p());
// UINFO(9, " wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
// UINFO(9, " wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
m_walkLast1p = node1p;
m_walkLast2p = node2p;
node1p->user1(true);
node2p->user1(true);
if (node1p->nextp() && node2p->nextp()) {
return hashval.depth() + walkDupCodeNext(node1p->nextp(), node2p->nextp(), level + 1);
}
return hashval.depth();
}
void walkReplace(AstNode* node1p, AstNode* node2p, AstNode* last1p,
AstNode* last2p) { // Final node in linked list, maybe null if all statements
// to be grabbed
// Make new function
string oldname = m_cfuncp->name();
string::size_type pos;
if ((pos = oldname.find("_common")) != string::npos) oldname.erase(pos);
if ((pos = oldname.find("__")) != string::npos) oldname.erase(pos);
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
oldname + "_common" + cvtToStr(++m_modNFuncs), nullptr);
m_modp->addStmtp(newfuncp);
// Create calls
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
// Grab statement bodies
AstNRelinker relink1Handle;
AstNRelinker relink2Handle;
for (AstNode *nextp, *walkp = node1p; true; walkp = nextp) {
nextp = walkp->nextp();
if (walkp == node1p) {
walkp->unlinkFrBack(&relink1Handle);
} else {
walkp->unlinkFrBack();
node1p->addNext(walkp);
}
if (walkp == last1p) break;
}
for (AstNode *nextp, *walkp = node2p; true; walkp = nextp) {
nextp = walkp->nextp();
if (walkp == node2p) {
walkp->unlinkFrBack(&relink2Handle);
} else {
walkp->unlinkFrBack();
node2p->addNext(walkp);
}
if (walkp == last2p) break;
}
// Move node1 statements to new function
newfuncp->addStmtsp(node1p);
// newfuncp->dumpTree(cout, " newfunctree: ");
// Mark node2 statements as dead
CombMarkVisitor visitor(node2p);
pushDeletep(node2p); // Delete later
// Link in new function
relink1Handle.relink(call1p);
relink2Handle.relink(call2p);
// Hash the new function
hashFunctions(newfuncp);
m_call.addCall(call1p);
m_call.addCall(call2p);
// If either new statement makes a func with only a single call, replace
// the above callers to call it directly
VL_DO_DANGLING(replaceOnlyCallFunc(call1p), call1p);
VL_DO_DANGLING(replaceOnlyCallFunc(call2p), call2p);
}
#endif
// VISITORS
virtual void visit(AstNetlist* nodep) override {
// Track all callers of each function
m_call.main(nodep);
//
// In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
// Iterate modules backwards, in bottom-up order.
// Required so that a module instantiating another can benefit from collapsing.
iterateChildrenBackwards(nodep);
m_call.main(nodep); // Track all call sites of each function
iterateChildren(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
UINFO(4, " MOD " << nodep << endl);
m_modp = nodep;
m_modNFuncs = 0;
m_walkLast2p = nullptr;
m_hashed.clear();
// Compute hash of all statement trees in the function
m_state = STATE_HASH;
m_dupFinder.clear();
// Compute hash of all CFuncs in the module
iterateChildren(nodep);
m_state = STATE_IDLE;
if (debug() >= 9) m_hashed.dumpFilePrefixed("combine");
if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine");
// Walk the hashes removing empty functions
walkEmptyFuncs();
// Walk the hashes looking for duplicate functions
walkDupFuncs();
// Walk the statements looking for large replicated code sections
// Note this is disabled, it still needed work
// Also repair it for DPI functions; when make __common need to ensure proper
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
// get split between functions causing the text to have a dangling reference.
#ifdef VL_COMBINE_STATEMENTS
{
m_state = STATE_DUP;
iterateChildren(nodep);
m_state = STATE_IDLE;
}
#endif
m_modp = nullptr;
}
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
if (!nodep->dontCombine()) {
if (m_state == STATE_HASH) {
hashStatement(nodep); // Hash the entire function - it might be identical
}
#ifdef VL_COMBINE_STATEMENTS
else if (m_state == STATE_DUP) {
iterateChildren(nodep);
}
#endif
}
}
virtual void visit(AstNodeStmt* nodep) override {
if (!nodep->isStatement()) {
iterateChildren(nodep);
return;
}
if (m_state == STATE_HASH && m_cfuncp) {
hashStatement(nodep);
}
#ifdef VL_COMBINE_STATEMENTS
else if (m_state == STATE_DUP && m_cfuncp) {
walkDupCodeStart(nodep);
}
#endif
if (nodep->dontCombine()) return;
// Hash the entire function
m_dupFinder.insert(nodep);
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*) override {}
virtual void visit(AstTraceDecl*) override {}
virtual void visit(AstTraceInc*) override {}
virtual void visit(AstVar*) override {} // Accelerate
virtual void visit(AstNodeStmt* nodep) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~CombineVisitor() override { //
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
virtual ~CombineVisitor() override {
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
}
};

View File

@ -96,18 +96,37 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
ConstBitOpTreeVisitor* m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo
AstVarRef* m_refp; // Points the variable that this VarInfo covers
V3Number m_bitPolarity; // Coefficient of each bit
static int widthOfRef(AstVarRef* refp) {
if (AstWordSel* selp = VN_CAST(refp->backp(), WordSel)) return selp->width();
if (AstCCast* castp = VN_CAST(refp->backp(), CCast)) return castp->width();
return refp->width();
}
public:
// METHODS
bool hasConstantResult() const { return m_constResult >= 0; }
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->sameGateTree(otherp); }
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); }
void setPolarity(bool compBit, int bit) {
UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult);
UASSERT_OBJ(bit < VL_QUADSIZE, m_refp,
"bit:" << bit << " is too big after V3Expand"
<< " back:" << m_refp->backp());
if (bit >= m_bitPolarity.width()) { // Need to expand m_bitPolarity
const V3Number oldPol = std::move(m_bitPolarity);
// oldPol.width() is 8, 16, or 32 because this visitor is called after V3Expand
// newWidth is increased by 2x because
// - CCast will cast to such bitwidth anyway
// - can avoid frequent expansion
int newWidth = oldPol.width();
while (bit >= newWidth) newWidth *= 2;
m_bitPolarity = V3Number{m_refp, newWidth};
UASSERT_OBJ(newWidth == 16 || newWidth == 32 || newWidth == 64, m_refp,
"bit:" << bit << " newWidth:" << newWidth);
m_bitPolarity.setAllBitsX();
for (int i = 0; i < oldPol.width(); ++i) {
if (oldPol.bitIs0(i))
m_bitPolarity.setBit(i, '0');
else if (oldPol.bitIs1(i))
m_bitPolarity.setBit(i, '1');
}
}
UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp,
"bit:" << bit << " width:" << m_bitPolarity.width() << m_refp);
if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set
m_bitPolarity.setBit(bit, compBit);
} else { // Priviously set the bit
@ -129,7 +148,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
FileLine* fl = m_refp->fileline();
AstNode* srcp = VN_CAST(m_refp->backp(), WordSel);
if (!srcp) srcp = m_refp;
const int width = widthOfRef(m_refp);
const int width = m_bitPolarity.width();
if (hasConstantResult())
return new AstConst{fl,
@ -160,7 +179,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp)
: m_parentp(parent)
, m_refp(refp)
, m_bitPolarity(refp, widthOfRef(refp)) {
, m_bitPolarity(refp, refp->isWide() ? VL_EDATASIZE : refp->width()) {
m_bitPolarity.setAllBitsX();
}
};
@ -1700,7 +1719,7 @@ private:
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (replaceAssignMultiSel(nodep)) {
} else if (m_doV && replaceAssignMultiSel(nodep)) {
return true;
}
return false;

View File

@ -22,7 +22,7 @@
#include "V3Global.h"
#include "V3CoverageJoin.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <vector>
@ -33,10 +33,7 @@
class CoverageJoinVisitor final : public AstNVisitor {
private:
// NODE STATE
// V3Hashed
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
// AstUser4InUse In V3Hashed
// AstUser4InUse In V3Hasher via V3DupFinder
// STATE
std::vector<AstCoverToggle*> m_toggleps; // List of of all AstCoverToggle's
@ -49,9 +46,9 @@ private:
void detectDuplicates() {
UINFO(9, "Finding duplicates\n");
// Note uses user4
V3Hashed hashed; // Duplicate code detection
V3DupFinder dupFinder; // Duplicate code detection
// Hash all of the original signals we toggle cover
for (AstCoverToggle* nodep : m_toggleps) hashed.hashAndInsert(nodep->origp());
for (AstCoverToggle* nodep : m_toggleps) dupFinder.insert(nodep->origp());
// Find if there are any duplicates
for (AstCoverToggle* nodep : m_toggleps) {
// nodep->backp() is null if we already detected it's a duplicate and unlinked it.
@ -60,10 +57,10 @@ private:
// This prevents making chains where a->b, then c->d, then b->c, as we'll
// find a->b, a->c, a->d directly.
while (true) {
const auto dupit = hashed.findDuplicate(nodep->origp());
if (dupit == hashed.end()) break;
const auto dupit = dupFinder.findDuplicate(nodep->origp());
if (dupit == dupFinder.end()) break;
//
AstNode* duporigp = hashed.iteratorNodep(dupit);
AstNode* duporigp = dupit->second;
// Note hashed will point to the original variable (what's
// duplicated), not the covertoggle, but we need to get back to the
// covertoggle which is immediately above, so:
@ -82,7 +79,7 @@ private:
removep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(removep), removep);
// Remove node from comparison so don't hit it again
hashed.erase(dupit);
dupFinder.erase(dupit);
++m_statToggleJoins;
}
}

View File

@ -351,7 +351,7 @@ private:
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
nullptr, nullptr);
UINFO(9, " Created " << postLogicp << endl);
finalp->addBodysp(postLogicp);
finalp->addStmtp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
}

92
src/V3DupFinder.cpp Normal file
View File

@ -0,0 +1,92 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hashed common code into functions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3DupFinder.h"
#include "V3Ast.h"
#include "V3File.h"
#include <algorithm>
#include <iomanip>
#include <map>
#include <memory>
//######################################################################
// V3DupFinder class functions
V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp) {
const auto& er = equal_range(m_hasher(nodep));
for (iterator it = er.first; it != er.second; ++it) {
AstNode* const node2p = it->second;
if (nodep == node2p) continue; // Same node is not a duplicate
if (checkp && !checkp->isSame(nodep, node2p)) continue; // User says it is not a duplicate
if (!nodep->sameTree(node2p)) continue; // Not the same trees
// Found duplicate!
return it;
}
return end();
}
void V3DupFinder::dumpFile(const string& filename, bool tree) {
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> dist;
V3Hash lasthash;
int num_in_bucket = 0;
for (auto it = cbegin(); true; ++it) {
if (it == cend() || lasthash != it->first) {
if (it != cend()) lasthash = it->first;
if (num_in_bucket) {
if (dist.find(num_in_bucket) == dist.end()) {
dist.emplace(num_in_bucket, 1);
} else {
++dist[num_in_bucket];
}
}
num_in_bucket = 0;
}
if (it == cend()) break;
num_in_bucket++;
}
*logp << "\n*** STATS:\n\n";
*logp << " #InBucket Occurrences\n";
for (const auto& i : dist) {
*logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n';
}
*logp << "\n*** Dump:\n\n";
for (const auto& it : *this) {
if (lasthash != it.first) {
lasthash = it.first;
*logp << " " << it.first << '\n';
}
*logp << "\t" << it.second << '\n';
// Dumping the entire tree may make nearly N^2 sized dumps,
// because the nodes under this one may also be in the hash table!
if (tree) it.second->dumpTree(*logp, " ");
}
}
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
if (v3Global.opt.dumpTree()) { //
dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
}
}

78
src/V3DupFinder.h Normal file
View File

@ -0,0 +1,78 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// Datastructure for finding duplicate AstNode trees via hashing
//
//*************************************************************************
#ifndef VERILATOR_V3DUPFINDER_H_
#define VERILATOR_V3DUPFINDER_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Hasher.h"
#include <map>
//============================================================================
struct V3DupFinderUserSame {
// Functor for V3DupFinder::findDuplicate
virtual bool isSame(AstNode*, AstNode*) = 0;
V3DupFinderUserSame() = default;
virtual ~V3DupFinderUserSame() = default;
};
// This really is just a multimap from 'node hash' to 'node pointer', with some minor extensions.
class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
using Super = std::multimap<V3Hash, AstNode*>;
// MEMBERS
const V3Hasher m_hasher;
public:
// CONSTRUCTORS
V3DupFinder(){};
~V3DupFinder() = default;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Expose minimal set of superclass interface
using Super::begin;
using Super::cbegin;
using Super::cend;
using Super::clear;
using Super::const_iterator;
using Super::empty;
using Super::end;
using Super::erase;
using Super::iterator;
// Insert node into data structure
iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); }
// Return duplicate, if one was inserted, with optional user check for sameness
iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr);
// Dump for debug
void dumpFile(const string& filename, bool tree);
void dumpFilePrefixed(const string& nameComment, bool tree = false);
};
#endif // Guard

View File

@ -459,7 +459,7 @@ public:
iterateChildren(nodep);
}
virtual void visit(AstCoverDecl* nodep) override {
puts("__vlCoverInsert("); // As Declared in emitCoverageDecl
puts("self->__vlCoverInsert("); // As Declared in emitCoverageDecl
puts("&(vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->dataDeclThisp()->binNum()));
puts("])");
@ -812,11 +812,19 @@ public:
if (!nodep->branchPred().unknown()) puts(")");
puts(") {\n");
iterateAndNextNull(nodep->ifsp());
if (nodep->elsesp()) {
puts("} else {\n");
iterateAndNextNull(nodep->elsesp());
puts("}");
if (!nodep->elsesp()) {
puts("\n");
} else {
if (VN_IS(nodep->elsesp(), NodeIf) && !nodep->elsesp()->nextp()) {
puts(" else ");
iterateAndNextNull(nodep->elsesp());
} else {
puts(" else {\n");
iterateAndNextNull(nodep->elsesp());
puts("}\n");
}
}
puts("}\n");
}
virtual void visit(AstExprStmt* nodep) override {
// GCC allows compound statements in expressions, but this is not standard.
@ -1335,6 +1343,7 @@ unsigned EmitVarTspSorter::s_serialNext = 0;
class EmitCImp final : EmitCStmts {
// MEMBERS
AstNodeModule* m_modp = nullptr;
AstNodeModule* m_fileModp = nullptr; // Files (names, headers) constructed using this module
std::vector<AstChangeDet*> m_blkChangeDetVec; // All encountered changes in block
bool m_slow = false; // Creating __Slow file
bool m_fast = false; // Creating non __Slow file (or both)
@ -1383,8 +1392,8 @@ class EmitCImp final : EmitCStmts {
}
}
V3OutCFile* newOutCFile(AstNodeModule* modp, bool slow, bool source, int filenum = 0) {
string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp);
V3OutCFile* newOutCFile(bool slow, bool source, int filenum = 0) {
string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp);
if (filenum) filenameNoExt += "__" + cvtToStr(filenum);
filenameNoExt += (slow ? "__Slow" : "");
V3OutCFile* ofp = nullptr;
@ -1405,7 +1414,7 @@ class EmitCImp final : EmitCStmts {
}
ofp->putsHeader();
if (modp->isTop() && !source) {
if (m_fileModp->isTop() && !source) {
ofp->puts("// DESCR"
"IPTION: Verilator output: Primary design header\n");
ofp->puts("//\n");
@ -1493,7 +1502,10 @@ class EmitCImp final : EmitCStmts {
}
virtual void visit(AstMTaskBody* nodep) override {
ExecMTask* mtp = nodep->execMTaskp();
maybeSplit();
splitSizeInc(10);
const ExecMTask* const mtp = nodep->execMTaskp();
puts("\n");
puts("void ");
puts(prefixNameProtect(m_modp) + "::" + protect(mtp->cFuncName()));
@ -1517,6 +1529,8 @@ class EmitCImp final : EmitCStmts {
if (nodep->dpiImport()) return;
if (!(nodep->slow() ? m_slow : m_fast)) return;
maybeSplit();
m_blkChangeDetVec.clear();
splitSizeInc(nodep);
@ -1739,7 +1753,9 @@ class EmitCImp final : EmitCStmts {
}
void emitVarReset(AstVar* varp) {
AstNodeDType* dtypep = varp->dtypep()->skipRefp();
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
const string varNameProtected
= VN_IS(m_modp, Class) ? varp->nameProtect() : "self->" + varp->nameProtect();
if (varp->isIO() && m_modp->isTop() && optSystemC()) {
// System C top I/O doesn't need loading, as the lower level subinst code does it.}
} else if (varp->isParam()) {
@ -1752,53 +1768,53 @@ class EmitCImp final : EmitCStmts {
if (initarp->defaultp()) {
puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst()));
puts("; ++__Vi) {\n");
emitSetVarConstant(varp->nameProtect() + "[__Vi]",
emitSetVarConstant(varNameProtected + "[__Vi]",
VN_CAST(initarp->defaultp(), Const));
puts("}\n");
}
const AstInitArray::KeyItemMap& mapr = initarp->map();
for (const auto& itr : mapr) {
AstNode* valuep = itr.second->valuep();
emitSetVarConstant(varp->nameProtect() + "[" + cvtToStr(itr.first) + "]",
emitSetVarConstant(varNameProtected + "[" + cvtToStr(itr.first) + "]",
VN_CAST(valuep, Const));
}
} else {
varp->v3fatalSrc("InitArray under non-arrayed var");
}
} else {
puts(emitVarResetRecurse(varp, dtypep, 0, ""));
puts(emitVarResetRecurse(varp, varNameProtected, dtypep, 0, ""));
}
}
string emitVarResetRecurse(AstVar* varp, AstNodeDType* dtypep, int depth,
const string& suffix) {
string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
AstNodeDType* dtypep, int depth, const string& suffix) {
dtypep = dtypep->skipRefp();
AstBasicDType* basicp = dtypep->basicp();
// Returns string to do resetting, empty to do nothing (which caller should handle)
if (AstAssocArrayDType* adtypep = VN_CAST(dtypep, AssocArrayDType)) {
// Access std::array as C array
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
".atDefault()" + cvtarray);
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (VN_IS(dtypep, ClassRefDType)) {
return ""; // Constructor does it
} else if (AstDynArrayDType* adtypep = VN_CAST(dtypep, DynArrayDType)) {
// Access std::array as C array
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
".atDefault()" + cvtarray);
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) {
// Access std::array as C array
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
".atDefault()" + cvtarray);
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
"Should have swapped msb & lsb earlier.");
string ivar = string("__Vi") + cvtToStr(depth);
string pre = ("for (int " + ivar + "=" + cvtToStr(0) + "; " + ivar + "<"
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
string below = emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1,
suffix + "[" + ivar + "]");
string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + "[" + ivar + "]");
string post = "}\n";
return below.empty() ? "" : pre + below + post;
} else if (basicp && basicp->keyword() == AstBasicDTypeKwd::STRING) {
@ -1814,16 +1830,21 @@ class EmitCImp final : EmitCStmts {
splitSizeInc(1);
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
string out;
if (zeroit) {
out += "VL_ZERO_RESET_W(";
if (varp->valuep()) {
AstConst* const constp = VN_CAST(varp->valuep(), Const);
if (!constp) varp->v3fatalSrc("non-const initializer for variable");
for (int w = 0; w < varp->widthWords(); ++w) {
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
}
} else {
out += "VL_RAND_RESET_W(";
out += zeroit ? "VL_ZERO_RESET_W(" : "VL_RAND_RESET_W(";
out += cvtToStr(dtypep->widthMin());
out += ", " + varNameProtected + suffix + ");\n";
}
out += cvtToStr(dtypep->widthMin());
out += ", " + varp->nameProtect() + suffix + ");\n";
return out;
} else {
string out = varp->nameProtect() + suffix;
string out = varNameProtected + suffix;
// If --x-initial-edge is set, we want to force an initial
// edge on uninitialized clocks (from 'X' to whatever the
// first value is). Since the class is instantiated before
@ -1855,8 +1876,8 @@ class EmitCImp final : EmitCStmts {
void emitSavableImp(AstNodeModule* modp);
void emitTextSection(AstType type);
// High level
void emitImpTop(AstNodeModule* modp);
void emitImp(AstNodeModule* fileModp, AstNodeModule* modp);
void emitImpTop();
void emitImp(AstNodeModule* modp);
void emitSettleLoop(const std::string& eval_call, bool initial);
void emitWrapEval(AstNodeModule* modp);
void emitWrapFast(AstNodeModule* modp);
@ -1864,7 +1885,7 @@ class EmitCImp final : EmitCStmts {
void emitMTaskVertexCtors(bool* firstp);
void emitIntTop(AstNodeModule* modp);
void emitInt(AstNodeModule* modp);
void maybeSplit(AstNodeModule* modp);
void maybeSplit();
public:
EmitCImp() = default;
@ -2263,6 +2284,19 @@ void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
}
emitDispState.pushArg(fmtLetter, argp, "");
if (fmtLetter == 't' || fmtLetter == '^') {
AstSFormatF* fmtp = nullptr;
if (AstDisplay* nodep = VN_CAST(dispp, Display))
fmtp = nodep->fmtp();
else if (AstSFormat* nodep = VN_CAST(dispp, SFormat))
fmtp = nodep->fmtp();
else
fmtp = VN_CAST(dispp, SFormatF);
UASSERT_OBJ(fmtp, dispp,
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
}
} else {
emitDispState.pushArg(fmtLetter, nullptr, "");
}
@ -2449,7 +2483,7 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) {
puts("\n");
}
putsDecoration("// Reset structure values\n");
puts(protect("_ctor_var_reset") + "();\n");
puts(protect("_ctor_var_reset") + "(this);\n");
emitTextSection(AstType::atScCtor);
if (modp->isTop() && v3Global.opt.mtasks()) {
@ -2492,7 +2526,9 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
puts("if (false && first) {} // Prevent unused\n");
puts("this->__VlSymsp = vlSymsp;\n"); // First, as later stuff needs it.
puts("if (false && this->__VlSymsp) {} // Prevent unused\n");
if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); }
if (v3Global.opt.coverage()) {
puts(protect("_configure_coverage") + "(this, vlSymsp, first);\n");
}
if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) {
puts("vlSymsp->_vm_contextp__->timeunit("
+ cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) + ");\n");
@ -3088,10 +3124,7 @@ void EmitCImp::emitIntTop(AstNodeModule*) {
}
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) {
puts("#include \"verilated_cov.h\"\n");
if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together");
}
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.dpi()) {
// do this before including our main .h file so that any references to
// types defined in svdpi.h are available
@ -3338,9 +3371,9 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
//----------------------------------------------------------------------
void EmitCImp::emitImpTop(AstNodeModule* fileModp) {
void EmitCImp::emitImpTop() {
puts("\n");
puts("#include \"" + prefixNameProtect(fileModp) + ".h\"\n");
puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n");
puts("#include \"" + symClassName() + ".h\"\n");
if (v3Global.dpi()) {
@ -3348,13 +3381,13 @@ void EmitCImp::emitImpTop(AstNodeModule* fileModp) {
puts("#include \"verilated_dpi.h\"\n");
}
emitModCUse(fileModp, VUseType::IMP_INCLUDE);
emitModCUse(fileModp, VUseType::IMP_FWD_CLASS);
emitModCUse(m_fileModp, VUseType::IMP_INCLUDE);
emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS);
emitTextSection(AstType::atScImpHdr);
}
void EmitCImp::emitImp(AstNodeModule* fileModp, AstNodeModule* modp) {
void EmitCImp::emitImp(AstNodeModule* modp) {
puts("\n//==========\n");
if (m_slow) {
string section;
@ -3376,37 +3409,33 @@ void EmitCImp::emitImp(AstNodeModule* fileModp, AstNodeModule* modp) {
// Blocks
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
maybeSplit(fileModp);
mainDoFunc(funcp);
}
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { mainDoFunc(funcp); }
}
}
//######################################################################
void EmitCImp::maybeSplit(AstNodeModule* fileModp) {
if (splitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
// Open a new file
m_ofp = newOutCFile(fileModp, !m_fast, true /*source*/, splitFilenumInc());
emitImpTop(fileModp);
}
splitSizeInc(10); // Even blank functions get a file with a low csplit
void EmitCImp::maybeSplit() {
if (!splitNeeded()) return;
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
// Open a new file
m_ofp = newOutCFile(!m_fast, true /*source*/, splitFilenumInc());
emitImpTop();
}
void EmitCImp::mainInt(AstNodeModule* modp) {
AstNodeModule* fileModp = modp; // Filename constructed using this module
m_modp = modp;
m_fileModp = modp;
m_slow = true;
m_fast = true;
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
m_ofp = newOutCFile(fileModp, false /*slow*/, false /*source*/);
m_ofp = newOutCFile(false /*slow*/, false /*source*/);
emitIntTop(modp);
emitInt(modp);
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
@ -3421,23 +3450,23 @@ void EmitCImp::mainInt(AstNodeModule* modp) {
void EmitCImp::mainImp(AstNodeModule* modp, bool slow) {
// Output a module
AstNodeModule* fileModp = modp; // Filename constructed using this module
m_modp = modp;
m_fileModp = modp;
m_slow = slow;
m_fast = !slow;
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
m_ofp = newOutCFile(fileModp, !m_fast, true /*source*/);
emitImpTop(fileModp);
emitImp(fileModp, modp);
m_ofp = newOutCFile(!m_fast, true /*source*/);
emitImpTop();
emitImp(modp);
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
// Put the non-static class implementation in same C++ files as
// often optimizations are possible when both are seen by the
// compiler together
m_modp = packagep->classp();
emitImp(fileModp, packagep->classp());
emitImp(packagep->classp());
m_modp = modp;
}
@ -3450,7 +3479,6 @@ void EmitCImp::mainImp(AstNodeModule* modp, bool slow) {
vxp = vxp->verticesNextp()) {
const ExecMTask* mtaskp = dynamic_cast<const ExecMTask*>(vxp);
if (mtaskp->threadRoot()) {
maybeSplit(fileModp);
// Only define one function for all the mtasks packed on
// a given thread. We'll name this function after the
// root mtask though it contains multiple mtasks' worth
@ -3925,21 +3953,29 @@ void V3EmitC::emitc() {
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
// clang-format off
EmitCImp cint; cint.mainInt(nodep);
cint.mainImp(nodep, true);
{ EmitCImp fast; fast.mainImp(nodep, false); }
// clang-format on
{
EmitCImp cint;
cint.mainInt(nodep);
cint.mainImp(nodep, true);
}
{
EmitCImp fast;
fast.mainImp(nodep, false);
}
}
}
void V3EmitC::emitcTrace() {
UINFO(2, __FUNCTION__ << ": " << endl);
if (v3Global.opt.trace()) {
// clang-format off
{ EmitCTrace slow(true); slow.main(); }
{ EmitCTrace fast(false); fast.main(); }
// clang-format on
{
EmitCTrace slow(true);
slow.main();
}
{
EmitCTrace fast(false);
fast.main();
}
}
}

View File

@ -113,6 +113,10 @@ class CMakeEmitter final {
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0");
*of << "# Threaded output mode? 0/1/N threads (from --threads)\n";
cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads()));
*of << "# Threaded tracing output mode? 0/1/N threads (from --trace-threads)\n";
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.traceThreads()));
*of << "# Struct output mode? 0/1 (from --trace-structs)\n";
cmake_set_raw(*of, name + "_TRACE_STRUCTS", cvtToStr(v3Global.opt.traceStructs()));
*of << "# VCD Tracing output mode? 0/1 (from --trace)\n";
cmake_set_raw(*of, name + "_TRACE_VCD",
(v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))
@ -167,11 +171,6 @@ class CMakeEmitter final {
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceBase()
+ "_c.cpp");
if (v3Global.opt.systemC()) {
if (v3Global.opt.traceFormat() != TraceFormat::VCD) {
v3warn(E_UNSUPPORTED,
"Unsupported: This trace format is not supported in SystemC, "
"use VCD format.");
}
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceLang()
+ ".cpp");
}

View File

@ -170,7 +170,12 @@ class EmitCSyms final : EmitCBaseVisitor {
const auto scpit = m_vpiScopeCandidates.find(scp);
if ((scpit != m_vpiScopeCandidates.end())
&& (m_scopeNames.find(scp) == m_scopeNames.end())) {
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
if (scopeNameit == m_scopeNames.end()) {
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
} else {
scopeNameit->second.m_type = scpit->second.m_type;
}
}
string::size_type pos = scp.rfind("__DOT__");
if (pos == string::npos) {
@ -233,20 +238,20 @@ class EmitCSyms final : EmitCBaseVisitor {
++it) {
if (it->second.m_type != "SCOPE_MODULE") continue;
string name = it->second.m_prettyName;
if (name.substr(0, 4) == "TOP.") name.replace(0, 4, "");
string symName = it->second.m_symName;
string above = symName;
if (above.substr(0, 4) == "TOP.") above.replace(0, 4, "");
string above = name;
while (!above.empty()) {
string::size_type pos = above.rfind('.');
string::size_type pos = above.rfind("__");
if (pos == string::npos) break;
above.resize(pos);
if (m_vpiScopeHierarchy.find(above) != m_vpiScopeHierarchy.end()) {
m_vpiScopeHierarchy[above].push_back(name);
m_vpiScopeHierarchy[above].push_back(symName);
break;
}
}
m_vpiScopeHierarchy[name] = std::vector<string>();
m_vpiScopeHierarchy[symName] = std::vector<string>();
}
}
@ -757,6 +762,7 @@ void EmitCSyms::emitSymImp() {
AstScope* scopep = it->second.m_scopep;
AstVar* varp = it->second.m_varp;
//
int pwidth = 1;
int pdim = 0;
int udim = 0;
string bounds;
@ -768,6 +774,7 @@ void EmitCSyms::emitSymImp() {
bounds += ",";
bounds += cvtToStr(basicp->lo());
pdim++;
pwidth *= basicp->elements();
}
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
dtypep
@ -779,6 +786,7 @@ void EmitCSyms::emitSymImp() {
bounds += cvtToStr(adtypep->right());
if (VN_IS(dtypep, PackArrayDType)) {
pdim++;
pwidth *= adtypep->elementsConst();
} else {
udim++;
}
@ -788,8 +796,14 @@ void EmitCSyms::emitSymImp() {
}
}
}
//
if (udim > 1 && (pdim && udim)) {
// TODO: actually expose packed arrays as vpiRegArray
if (pdim > 1 && udim == 0) {
bounds = ", ";
bounds += cvtToStr(pwidth - 1);
bounds += ",0";
pdim = 1;
}
if (pdim > 1 || udim > 1) {
puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays
}
puts(protect("__Vscope_" + it->second.m_scopeName) + ".varInsert(__Vfinal,");

View File

@ -64,6 +64,7 @@ public:
ALWCOMBORDER, // Always_comb with unordered statements
ASSIGNDLY, // Assignment delays
ASSIGNIN, // Assigning to input
BADSTDPRAGMA, // Any error related to pragmas
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
BLKLOOPINIT, // Delayed assignment to array inside for loops
BLKSEQ, // Blocking assignments in sequential block
@ -109,6 +110,7 @@ public:
PINNOTFOUND, // instance port name not found in it's module
PKGNODECL, // Error: Package/class needs to be predeclared
PROCASSWIRE, // Procedural assignment on wire
PROTECTED, // detected `pragma protected
RANDC, // Unsupported: 'randc' converted to 'rand'
REALCVT, // Real conversion
REDEFMACRO, // Redefining existing define macro
@ -159,7 +161,7 @@ public:
"DETECTARRAY", "ENCAPSULATED", "PORTSHORT", "UNSUPPORTED", "TASKNSVAR",
// Warnings
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG",
@ -171,7 +173,7 @@ public:
"LATCH", "LITENDIAN", "MODDUP",
"MULTIDRIVEN", "MULTITOP","NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"RANDC", "REALCVT", "REDEFMACRO",
"PROTECTED", "RANDC", "REALCVT", "REDEFMACRO",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT", "TIMESCALEMOD",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
@ -192,8 +194,8 @@ public:
// Warnings we'll present to the user as errors
// Later -Werror- options may make more of these.
bool pretendError() const {
return (m_e == ASSIGNIN || m_e == BLKANDNBLK || m_e == BLKLOOPINIT || m_e == CONTASSREG
|| m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BLKANDNBLK || m_e == BLKLOOPINIT
|| m_e == CONTASSREG || m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL
|| m_e == PROCASSWIRE); // Says IEEE
}
// Warnings to mention manual

View File

@ -30,6 +30,7 @@
#include "V3Global.h"
#include "V3Expand.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include <algorithm>
@ -45,10 +46,24 @@ private:
// STATE
AstNode* m_stmtp = nullptr; // Current statement
VDouble0 m_statWides; // Statistic tracking
VDouble0 m_statWideWords; // Statistic tracking
VDouble0 m_statWideLimited; // Statistic tracking
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool doExpand(AstNode* nodep) {
++m_statWides;
if (nodep->widthWords() <= v3Global.opt.expandLimit()) {
m_statWideWords += nodep->widthWords();
return true;
} else {
++m_statWideLimited;
return false;
}
}
int longOrQuadWidth(AstNode* nodep) {
return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1);
}
@ -204,6 +219,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) {
UINFO(8, " Wordize ASSIGN(CONST) " << nodep << endl);
if (!doExpand(nodep)) return false;
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}}
if (rhsp->num().isFourState()) {
rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible?
@ -219,6 +235,7 @@ private:
//-------- Uniops
bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) {
UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl);
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
@ -228,6 +245,7 @@ private:
UINFO(8, " Wordize ASSIGN(ARRAYSEL) " << nodep << endl);
UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep,
"ArraySel with unpacked arrays should have been removed in V3Slice");
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
@ -236,6 +254,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) {
UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl);
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }}
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w,
new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w)));
@ -245,6 +264,7 @@ private:
//-------- Biops
bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) {
UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl);
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w,
new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
@ -254,6 +274,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) {
UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl);
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w,
new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
@ -263,6 +284,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) {
UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl);
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w,
new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w),
@ -273,6 +295,7 @@ private:
//-------- Triops
bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) {
UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl);
if (!doExpand(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); w++) {
addWordAssign(nodep, w,
new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true),
@ -417,6 +440,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) {
UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch");
if (!doExpand(nodep)) return false;
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) {
int lsb = rhsp->lsbConst();
UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl);
@ -647,6 +671,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) {
UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl);
if (!doExpand(rhsp)) return false;
// Lhs or Rhs may be word, long, or quad.
// newAstWordSelClone nicely abstracts the difference.
int rhsshift = rhsp->rhsp()->widthMin();
@ -701,6 +726,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) {
UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl);
if (!doExpand(rhsp)) return false;
AstNode* lhsp = rhsp->lhsp();
int lhswidth = lhsp->widthMin();
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
@ -857,6 +883,7 @@ private:
iterateChildren(nodep);
bool did = false;
if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel)))
&& ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel)))
&& !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC
&& !AstVar::scVarRecurse(nodep->rhsp())) {
if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) {
@ -897,7 +924,11 @@ private:
public:
// CONSTRUCTORS
explicit ExpandVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ExpandVisitor() override = default;
virtual ~ExpandVisitor() override {
V3Stats::addStat("Optimizations, expand wides", m_statWides);
V3Stats::addStat("Optimizations, expand wide words", m_statWideWords);
V3Stats::addStat("Optimizations, expand limited", m_statWideLimited);
}
};
//----------------------------------------------------------------------

View File

@ -692,7 +692,7 @@ int V3OutFormatter::endLevels(const char* strg) {
}
void V3OutFormatter::puts(const char* strg) {
if (m_prependIndent) {
if (m_prependIndent && strg[0] != '\n') {
putsNoTracking(indentSpaces(endLevels(strg)));
m_prependIndent = false;
}
@ -715,9 +715,13 @@ void V3OutFormatter::puts(const char* strg) {
break;
case ' ': wordstart = true; break;
case '\t': wordstart = true; break;
case '"':
wordstart = false;
m_inStringLiteral = !m_inStringLiteral;
break;
case '/':
if (m_lang == LA_C || m_lang == LA_VERILOG) {
if (cp > strg && cp[-1] == '/') {
if (cp > strg && cp[-1] == '/' && !m_inStringLiteral) {
// Output ignoring contents to EOL
cp++;
while (*cp && cp[1] && cp[1] != '\n') putcNoTracking(*cp++);
@ -1012,7 +1016,8 @@ public:
}
}
string protectWordsIf(const string& old, bool doIt) {
// Split at " " (for traces), "." (for scopes), or "->" (for scopes)
// Split at " " (for traces), "." (for scopes), "->" (for scopes), "::" (for superclass
// reference)
if (!(doIt && v3Global.opt.protectIds())) return old;
string out;
string::size_type start = 0;
@ -1024,6 +1029,7 @@ public:
trySep(old, start, " ", pos /*ref*/, separator /*ref*/);
trySep(old, start, ".", pos /*ref*/, separator /*ref*/);
trySep(old, start, "->", pos /*ref*/, separator /*ref*/);
trySep(old, start, "::", pos /*ref*/, separator /*ref*/);
if (pos == string::npos) break;
out += protectIf(old.substr(start, pos - start), true) + separator;
start = pos + separator.length();

View File

@ -121,6 +121,7 @@ private:
int m_column = 0;
int m_nobreak = false; // Basic operator or begin paren, don't break next
bool m_prependIndent = true;
bool m_inStringLiteral = false;
int m_indentLevel = 0; // Current {} indentation
std::stack<int> m_parenVec; // Stack of columns where last ( was
int m_bracketLevel = 0; // Intenting = { block, indicates number of {'s seen.

View File

@ -30,7 +30,7 @@
#include "V3Graph.h"
#include "V3Const.h"
#include "V3Stats.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include <algorithm>
#include <list>
@ -900,7 +900,7 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode*
//######################################################################
// Auxiliary hash class for GateDedupeVarVisitor
class GateDedupeHash final : public V3HashedUserSame {
class GateDedupeHash final : public V3DupFinderUserSame {
private:
// NODE STATE
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
@ -911,24 +911,36 @@ private:
// AstUser1InUse m_inuser1; (Allocated for use in GateVisitor)
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
AstUser3InUse m_inuser3;
// AstUser4InUse m_inuser4; (Allocated for use in V3Hashed)
// AstUser4InUse m_inuser4; (Allocated for use in V3Hasher via V3DupFinder)
AstUser5InUse m_inuser5;
V3Hashed m_hashed; // Hash, contains rhs of assigns
V3DupFinder m_dupFinder; // Duplicate finder for rhs of assigns
std::unordered_set<AstNode*> m_nodeDeleteds; // Any node in this hash was deleted
VL_DEBUG_FUNC; // Declare debug()
void hash(AstNode* nodep) {
// !nullptr && the object is hashable
if (nodep && !nodep->sameHash().isIllegal()) m_hashed.hash(nodep);
}
bool sameHash(AstNode* node1p, AstNode* node2p) {
return (node1p && node2p && !node1p->sameHash().isIllegal()
&& !node2p->sameHash().isIllegal() && m_hashed.sameNodes(node1p, node2p));
}
bool same(AstNode* node1p, AstNode* node2p) {
return node1p == node2p || sameHash(node1p, node2p);
// Regarding the complexity of this funcition 'same':
// Applying this comparison function to a a set of n trees pairwise is O(n^2) in the
// number of comparisons (number of pairs). AstNode::sameTree itself, is O(sizeOfTree) in
// the worst case, which happens if the operands of sameTree are indeed identical copies,
// which means this line is O(n^2*sizeOfTree), iff you are comparing identical copies of
// the same tree. In practice the identity comparison over the pointers, and the short
// circuiting in sameTree means that for comparing the same tree instance to itself, or
// trees of different types/shapes is a lot closer to O(1), so this 'same' function is
// Omega(n^2) and O(n^2*sizeOfTree), and in practice as we are mostly comparing the same
// instance to itself or different trees, the complexity should be closer to the lower
// bound.
//
// Also if you see where this 'same' function is used within isSame, it's only ever
// comparing AstActive nodes, which are very likely not to compare equals (and for the
// purposes of V3Gate, we probably only care about them either being identical instances,
// or having the same sensitivities anyway, so if this becomes a problem, it can be
// improved which should also speed things up), and AstNodeMath for if conditions, which
// are hopefully small, and to be safe they should probably be only considered same when
// identical instances (otherwise if writing the condition between 2 ifs don't really
// merge).
return node1p == node2p || (node1p && node1p->sameTree(node2p));
}
public:
@ -958,7 +970,7 @@ public:
|| (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end()));
}
// Callback from V3Hashed::findDuplicate
// Callback from V3DupFinder::findDuplicate
virtual bool isSame(AstNode* node1p, AstNode* node2p) override {
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
if (isReplaced(node1p) || isReplaced(node2p)) {
@ -977,25 +989,21 @@ public:
rhsp->user3p(extra1p);
rhsp->user5p(extra2p);
hash(extra1p);
hash(extra2p);
const auto inserted = m_hashed.hashAndInsert(rhsp);
const auto dupit = m_hashed.findDuplicate(rhsp, this);
// Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't
// return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate)
const auto inserted = m_dupFinder.insert(rhsp);
const auto dupit = m_dupFinder.findDuplicate(rhsp, this);
// Even though rhsp was just inserted, V3DupFinder::findDuplicate doesn't
// return anything in the hash that has the same pointer (V3DupFinder::findDuplicate)
// So dupit is either a different, duplicate rhsp, or the end of the hash.
if (dupit != m_hashed.end()) {
m_hashed.erase(inserted);
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
if (dupit != m_dupFinder.end()) {
m_dupFinder.erase(inserted);
return VN_CAST(dupit->second->user2p(), NodeAssign);
}
// Retain new inserted information
return nullptr;
}
void check() {
m_hashed.check();
for (const auto& itr : m_hashed) {
for (const auto& itr : m_dupFinder) {
AstNode* nodep = itr.second;
AstNode* activep = nodep->user3p();
AstNode* condVarp = nodep->user5p();
@ -1003,9 +1011,9 @@ public:
// This class won't break if activep isn't an active, or
// ifVar isn't a var, but this is checking the caller's construction.
UASSERT_OBJ(!activep || (!VN_DELETED(activep) && VN_IS(activep, Active)), nodep,
"V3Hashed check failed, lost active pointer");
"V3DupFinder check failed, lost active pointer");
UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp), nodep,
"V3Hashed check failed, lost if pointer");
"V3DupFinder check failed, lost if pointer");
}
}
}

27
src/V3Hash.cpp Normal file
View File

@ -0,0 +1,27 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash calculation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3Hash.h"
#include <functional>
#include <iomanip>
V3Hash::V3Hash(const std::string& val)
: m_value{static_cast<uint32_t>(std::hash<std::string>{}(val))} {}
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
return os << std::hex << std::setw(8) << std::setfill('0') << rhs.value();
}

69
src/V3Hash.h Normal file
View File

@ -0,0 +1,69 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash calculation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3HASH_H_
#define VERILATOR_V3HASH_H_
#include <cstdint>
#include <string>
//######################################################################
// V3Hash -- Generic hashing
class V3Hash final {
uint32_t m_value; // The 32-bit hash value.
public:
// CONSTRUCTORS
V3Hash()
: m_value{0} {}
explicit V3Hash(uint32_t val)
: m_value{val + 0x9e3779b9} {} // This is the same as 'V3Hash() + val'
explicit V3Hash(int32_t val)
: m_value{static_cast<uint32_t>(val)} {}
explicit V3Hash(size_t val)
: m_value{static_cast<uint32_t>(val)} {}
explicit V3Hash(const std::string& val);
// METHODS
uint32_t value() const { return m_value; }
// OPERATORS
// Comparisons
bool operator==(const V3Hash& rh) const { return m_value == rh.m_value; }
bool operator!=(const V3Hash& rh) const { return m_value != rh.m_value; }
bool operator<(const V3Hash& rh) const { return m_value < rh.m_value; }
// '+' combines hashes
V3Hash operator+(const V3Hash& that) const {
return V3Hash(m_value ^ (that.m_value + 0x9e3779b9 + (m_value << 6) + (m_value >> 2)));
}
V3Hash operator+(uint32_t value) const { return *this + V3Hash(value); }
V3Hash operator+(int32_t value) const { return *this + V3Hash(value); }
V3Hash operator+(size_t value) const { return *this + V3Hash(value); }
V3Hash operator+(const std::string& value) const { return *this + V3Hash(value); }
// '+=' combines in place
V3Hash& operator+=(const V3Hash& that) { return *this = *this + that; }
V3Hash& operator+=(uint32_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(int32_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(size_t value) { return *this += V3Hash(value); }
V3Hash& operator+=(const std::string& that) { return *this += V3Hash(that); }
};
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
#endif // Guard

View File

@ -1,207 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hashed common code into functions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Hashed's Transformations:
//
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Hashed.h"
#include "V3Ast.h"
#include "V3File.h"
#include <algorithm>
#include <iomanip>
#include <map>
#include <memory>
//######################################################################
// Hashed state, as a visitor of each AstNode
class HashedVisitor final : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
// AstUser4InUse in V3Hashed.h
// STATE
V3Hash m_lowerHash; // Hash of the statement we're building
bool m_cacheInUser4; // Use user4 to cache each V3Hash?
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void nodeHashIterate(AstNode* nodep) {
V3Hash thisHash;
if (!m_cacheInUser4 || !nodep->user4()) {
UASSERT_OBJ(
!(VN_IS(nodep->backp(), CFunc)
&& !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))),
nodep,
"Node " << nodep->prettyTypeName()
<< " in statement position but not marked stmt (node under function)");
VL_RESTORER(m_lowerHash);
{
m_lowerHash = nodep->sameHash();
UASSERT_OBJ(!m_lowerHash.isIllegal(), nodep,
"sameHash function undefined (returns 0) for node under CFunc.");
// For identical nodes, the type should be the same thus
// dtypep should be the same too
m_lowerHash
= V3Hash(m_lowerHash, V3Hash(nodep->type() << 6, V3Hash(nodep->dtypep())));
// Now update m_lowerHash for our children's (and next children) contributions
iterateChildren(nodep);
// Store the hash value
nodep->user4(m_lowerHash.fullValue());
// UINFO(9, " hashnode "<<m_lowerHash<<" "<<nodep<<endl);
thisHash = m_lowerHash;
}
}
// Update what will become the above node's hash
m_lowerHash += m_cacheInUser4 ? V3Hashed::nodeHash(nodep) : thisHash;
}
//--------------------
virtual void visit(AstVar*) override {}
virtual void visit(AstTypedef*) override {}
virtual void visit(AstParamTypeDType*) override {}
virtual void visit(AstNode* nodep) override {
// Hash not just iterate
nodeHashIterate(nodep);
}
public:
// CONSTRUCTORS
explicit HashedVisitor(AstNode* nodep)
: m_cacheInUser4{true} {
nodeHashIterate(nodep);
// UINFO(9," stmthash "<<hex<<V3Hashed::nodeHash(nodep)<<" "<<nodep<<endl);
}
explicit HashedVisitor(const AstNode* nodep)
: m_cacheInUser4{false} {
nodeHashIterate(const_cast<AstNode*>(nodep));
}
V3Hash finalHash() const { return m_lowerHash; }
virtual ~HashedVisitor() override = default;
};
//######################################################################
// Hashed class functions
V3Hash V3Hashed::uncachedHash(const AstNode* nodep) {
HashedVisitor visitor(nodep);
return visitor.finalHash();
}
V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) {
hash(nodep);
return m_hashMmap.emplace(nodeHash(nodep), nodep);
}
void V3Hashed::hash(AstNode* nodep) {
UINFO(8, " hashI " << nodep << endl);
if (!nodep->user4p()) { HashedVisitor visitor(nodep); }
}
bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
UASSERT_OBJ(node1p->user4p(), node1p, "Called isIdentical on non-hashed nodes");
UASSERT_OBJ(node2p->user4p(), node2p, "Called isIdentical on non-hashed nodes");
return (node1p->user4p() == node2p->user4p() // Same hash
&& node1p->sameTree(node2p));
}
void V3Hashed::erase(iterator it) {
AstNode* nodep = iteratorNodep(it);
UINFO(8, " erase " << nodep << endl);
UASSERT_OBJ(nodep->user4p(), nodep, "Called removeNode on non-hashed node");
m_hashMmap.erase(it);
nodep->user4p(nullptr); // So we don't allow removeNode again
}
void V3Hashed::check() {
for (const auto& itr : *this) {
AstNode* nodep = itr.second;
UASSERT_OBJ(nodep->user4p(), nodep, "V3Hashed check failed, non-hashed node");
}
}
void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) {
if (v3Global.opt.dumpTree()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
}
void V3Hashed::dumpFile(const string& filename, bool tree) {
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write " << filename);
std::unordered_map<int, int> dist;
V3Hash lasthash;
int num_in_bucket = 0;
for (HashMmap::iterator it = begin(); true; ++it) {
if (it == end() || lasthash != it->first) {
if (it != end()) lasthash = it->first;
if (num_in_bucket) {
if (dist.find(num_in_bucket) == dist.end()) {
dist.emplace(num_in_bucket, 1);
} else {
++dist[num_in_bucket];
}
}
num_in_bucket = 0;
}
if (it == end()) break;
num_in_bucket++;
}
*logp << "\n*** STATS:\n\n";
*logp << " #InBucket Occurrences\n";
for (const auto& i : dist) {
*logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n';
}
*logp << "\n*** Dump:\n\n";
for (const auto& itr : *this) {
if (lasthash != itr.first) {
lasthash = itr.first;
*logp << " " << itr.first << '\n';
}
*logp << "\t" << itr.second << '\n';
// Dumping the entire tree may make nearly N^2 sized dumps,
// because the nodes under this one may also be in the hash table!
if (tree) itr.second->dumpTree(*logp, " ");
}
}
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserSame* checkp) {
UINFO(8, " findD " << nodep << endl);
UASSERT_OBJ(nodep->user4p(), nodep, "Called findDuplicate on non-hashed node");
std::pair<HashMmap::iterator, HashMmap::iterator> eqrange
= mmap().equal_range(nodeHash(nodep));
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (nodep != node2p && (!checkp || checkp->isSame(nodep, node2p))
&& sameNodes(nodep, node2p)) {
return eqit;
}
}
return end();
}

View File

@ -1,92 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3HASHED_H_
#define VERILATOR_V3HASHED_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class VHashedBase VL_NOT_FINAL {
public:
// CONSTRUCTORS
VHashedBase() = default;
~VHashedBase() = default;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
};
//============================================================================
struct V3HashedUserSame {
// Functor for V3Hashed::findDuplicate
virtual bool isSame(AstNode*, AstNode*) = 0;
V3HashedUserSame() = default;
virtual ~V3HashedUserSame() = default;
};
class V3Hashed final : public VHashedBase {
// NODE STATE
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
AstUser4InUse m_inuser4;
// TYPES
public:
using HashMmap = std::multimap<V3Hash, AstNode*>;
using iterator = HashMmap::iterator;
private:
// MEMBERS
HashMmap m_hashMmap; // hashvalue -> nodes with that hash
public:
// CONSTRUCTORS
V3Hashed() { clear(); }
~V3Hashed() = default;
// ACCESSORS
HashMmap& mmap() { return m_hashMmap; } // Return map for iteration
iterator begin() { return m_hashMmap.begin(); }
iterator end() { return m_hashMmap.end(); }
// METHODS
void clear() {
m_hashMmap.clear();
AstNode::user4ClearTree();
}
void check(); // Check assertions on structure
// Hash the node, and insert into map. Return iterator to inserted
iterator hashAndInsert(AstNode* nodep);
static void hash(AstNode* nodep); // Only hash the node
// After hashing, and tell if identical
static bool sameNodes(AstNode* node1p, AstNode* node2p);
void erase(iterator it); // Remove node from structures
// Return duplicate in hash, if any, with optional user check for sameness
iterator findDuplicate(AstNode* nodep, V3HashedUserSame* checkp = nullptr);
AstNode* iteratorNodep(iterator it) { return it->second; }
void dumpFile(const string& filename, bool tree);
void dumpFilePrefixed(const string& nameComment, bool tree = false);
static V3Hash nodeHash(AstNode* nodep) { return V3Hash(nodep->user4p()); }
// Hash of the nodep tree, without caching in user4.
static V3Hash uncachedHash(const AstNode* nodep);
};
#endif // Guard

481
src/V3Hasher.cpp Normal file
View File

@ -0,0 +1,481 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode hash computation
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Hasher.h"
#include <functional>
//######################################################################
// Visitor that computes node hashes
class HasherVisitor final : public AstNVisitor {
private:
// NODE STATE
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
// AstUser4InUse in V3Hasher.h
// STATE
V3Hash m_hash; // Hash value accumulator
const bool m_cacheInUser4; // Use user4 to cache each V3Hash?
// METHODS
VL_DEBUG_FUNC; // Declare debug()
V3Hash hashNodeAndIterate(AstNode* nodep, bool hashDType, bool hashChildren,
std::function<void()>&& f) {
if (m_cacheInUser4 && nodep->user4()) {
return V3Hash(nodep->user4());
} else {
VL_RESTORER(m_hash);
// Reset accumulator
m_hash = V3Hash(nodep->type()); // Node type
f(); // Node specific hash
if (hashDType && nodep != nodep->dtypep()) iterateNull(nodep->dtypep()); // Node dtype
if (hashChildren) iterateChildrenConst(nodep); // Children
if (m_cacheInUser4) nodep->user4(m_hash.value());
return m_hash;
}
}
// VISITORS
constexpr static bool HASH_DTYPE = true;
constexpr static bool HASH_CHILDREN = true;
// Each visitor below contributes to the hash any node specific content
// that is not dependent on either of the following, as these are
// included by default by hashNode:
// - Node type (as given by AstNode::type())
// - Node dtype (unless !hashDType)
// - child nodes (unless !hashChildren)
//
// The hash must be stable, which means in particular it cannot rely on
// pointer values, or any other value that might differ between separate
// invocations of Verilator over the same design.
//
// Note there is a circularity problem where some child nodes can back
// to their ancestral nodes via member pointers, which can lead to an
// infinite traversal. To break this, nodes that are subject to such
// referencing and represent code which can reasonably be assumed not to
// be equivalent to any other code, are hashed either by name (e.g.:
// AstNodeModule), or by unique identifier (e.g.: AstNodeUOrStructDType).
//------------------------------------------------------------
// AstNode - Warns to help find missing cases
virtual void visit(AstNode* nodep) override {
#if VL_DEBUG
UINFO(0, "%Warning: Hashing node as AstNode: " << nodep);
#endif
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
//------------------------------------------------------------
// AstNodeDType
virtual void visit(AstNodeArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->virtRefDTypep());
m_hash += nodep->left();
m_hash += nodep->right();
});
}
virtual void visit(AstNodeUOrStructDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
m_hash += nodep->uniqueNum();
});
}
virtual void visit(AstParamTypeDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
virtual void visit(AstMemberDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstDefImplicitDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->uniqueNum();
});
}
virtual void visit(AstAssocArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->virtRefDTypep());
iterateNull(nodep->virtRefDType2p());
});
}
virtual void visit(AstDynArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstUnsizedArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstBasicDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
m_hash += nodep->keyword();
m_hash += nodep->nrange().left();
m_hash += nodep->nrange().right();
});
}
virtual void visit(AstConstDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstClassRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->classp());
});
}
virtual void visit(AstIfaceRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->cellp());
});
}
virtual void visit(AstQueueDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstRefDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->typedefp());
iterateNull(nodep->refDTypep());
});
}
virtual void visit(AstVoidDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstEnumDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, false, [=]() { //
m_hash += nodep->uniqueNum();
});
}
//------------------------------------------------------------
// AstNodeMath
virtual void visit(AstNodeMath* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstConst* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->num().toHash();
});
}
virtual void visit(AstNullCheck* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstCCast* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->size();
});
}
virtual void visit(AstVarRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
if (nodep->varScopep()) {
iterateNull(nodep->varScopep());
} else {
iterateNull(nodep->varp());
m_hash += nodep->hiernameToProt();
}
});
}
virtual void visit(AstVarXRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
iterateNull(nodep->varp());
m_hash += nodep->dotted();
});
}
virtual void visit(AstMemberSel* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstFScanF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
virtual void visit(AstSScanF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
virtual void visit(AstTestPlusArgs* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
//------------------------------------------------------------
// AstNodeStmt
virtual void visit(AstNodeStmt* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstNodeText* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
virtual void visit(AstNodeCCall* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->funcp());
});
}
virtual void visit(AstNodeFTaskRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
iterateNull(nodep->taskp());
iterateNull(nodep->classOrPackagep());
});
}
virtual void visit(AstCMethodHard* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstCoverInc* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->declp());
});
}
virtual void visit(AstDisplay* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->displayType();
});
}
virtual void visit(AstMonitorOff* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->off();
});
}
virtual void visit(AstJumpGo* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->labelp());
});
}
virtual void visit(AstTraceInc* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->declp());
});
}
virtual void visit(AstNodeCoverOrAssert* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
//------------------------------------------------------------
// AstNode direct descendents
virtual void visit(AstNodeRange* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstNodeModule* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
m_hash += nodep->origName();
m_hash += nodep->hierName();
});
}
virtual void visit(AstNodePreSel* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstClassExtends* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstSelLoopVars* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstDefParam* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstArg* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstParseRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->expect();
m_hash += nodep->name();
});
}
virtual void visit(AstClassOrPackageRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->classOrPackageNodep());
});
}
virtual void visit(AstSenItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->edgeType();
});
}
virtual void visit(AstSenTree* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstSFormatF* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->text();
});
}
virtual void visit(AstElabDisplay* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->displayType();
});
}
virtual void visit(AstInitItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstInitArray* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstPragma* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->pragType();
});
}
virtual void visit(AstAttrOf* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->attrType();
});
}
virtual void visit(AstNodeFile* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstCFunc* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstVar* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->varType();
});
}
virtual void visit(AstScope* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() {
m_hash += nodep->name();
iterateNull(nodep->aboveScopep());
});
}
virtual void visit(AstVarScope* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
iterateNull(nodep->varp());
iterateNull(nodep->scopep());
});
}
virtual void visit(AstEnumItem* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstTypedef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstTypedefFwd* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstActive* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateNull(nodep->sensesp());
});
}
virtual void visit(AstCell* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->modp());
});
}
virtual void visit(AstCellInline* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->scopep());
});
}
virtual void visit(AstNodeFTask* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstModport* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstModportVarRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->varp());
});
}
virtual void visit(AstModportFTaskRef* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
iterateNull(nodep->ftaskp());
});
}
virtual void visit(AstNodeProcedure* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
virtual void visit(AstNodeBlock* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
virtual void visit(AstPin* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {
m_hash += nodep->name();
m_hash += nodep->pinNum();
});
}
public:
// CONSTRUCTORS
explicit HasherVisitor(AstNode* nodep)
: m_cacheInUser4{true} {
iterate(nodep);
}
explicit HasherVisitor(const AstNode* nodep)
: m_cacheInUser4{false} {
iterate(const_cast<AstNode*>(nodep));
}
V3Hash finalHash() const { return m_hash; }
virtual ~HasherVisitor() override = default;
};
//######################################################################
// V3Hasher methods
V3Hash V3Hasher::operator()(AstNode* nodep) const {
if (!nodep->user4()) { HasherVisitor visitor(nodep); }
return V3Hash(nodep->user4());
}
V3Hash V3Hasher::uncachedHash(const AstNode* nodep) {
HasherVisitor visitor(nodep);
return visitor.finalHash();
}

52
src/V3Hasher.h Normal file
View File

@ -0,0 +1,52 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// V3Hasher handles computation of AstNode hashes
//
//*************************************************************************
#ifndef VERILATOR_V3HASHER_H_
#define VERILATOR_V3HASHER_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Hash.h"
//============================================================================
class V3Hasher final {
// NODE STATE
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
AstUser4InUse m_inuser4;
public:
// CONSTRUCTORS
V3Hasher() = default;
~V3Hasher() = default;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Compute hash of node. This method caches the hash in the node's user4().
V3Hash operator()(AstNode* nodep) const;
// Compute hash of node, without caching in user4.
static V3Hash uncachedHash(const AstNode* nodep);
};
#endif // Guard

View File

@ -24,6 +24,7 @@
#include <algorithm>
#include <cerrno>
#include <cmath>
#include <functional>
constexpr int MAX_SPRINTF_DOUBLE_SIZE
= 1100; // Maximum characters with a sprintf %e/%f/%g (really 1079)
@ -883,7 +884,11 @@ string V3Number::toString() const {
return str;
}
uint32_t V3Number::toHash() const { return m_value[0]; }
V3Hash V3Number::toHash() const {
V3Hash hash(m_width);
for (int i = 0; i < words(); ++i) { hash += m_value[i]; }
return hash;
}
uint32_t V3Number::edataWord(int eword) const {
UASSERT(!isFourState(), "edataWord with 4-state " << *this);
@ -1526,11 +1531,8 @@ bool V3Number::isCaseEq(const V3Number& rhs) const {
if (isString()) return toString() == rhs.toString();
if (isDouble()) return toDouble() == rhs.toDouble();
if (this->width() != rhs.width()) return false;
for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) {
if (this->bitIs(bit) != rhs.bitIs(bit)) return false;
}
return true;
if (m_value != rhs.m_value) return false;
return m_valueX == rhs.m_valueX;
}
V3Number& V3Number::opCaseEq(const V3Number& lhs, const V3Number& rhs) {
@ -1759,13 +1761,17 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) {
opCleanThis(); // Mult produces extra bits in result
} else {
for (int lword = 0; lword < lhs.words(); lword++) {
const vluint64_t lwordval = static_cast<vluint64_t>(lhs.m_value[lword]);
if (lwordval == 0) continue;
for (int rword = 0; rword < rhs.words(); rword++) {
vluint64_t mul = static_cast<vluint64_t>(lhs.m_value[lword])
* static_cast<vluint64_t>(rhs.m_value[rword]);
const vluint64_t rwordval = static_cast<vluint64_t>(rhs.m_value[rword]);
if (rwordval == 0) continue;
vluint64_t mul = lwordval * rwordval;
for (int qword = lword + rword; qword < this->words(); qword++) {
mul += static_cast<vluint64_t>(m_value[qword]);
m_value[qword] = (mul & 0xffffffffULL);
mul = (mul >> 32ULL) & 0xffffffffULL;
if (mul == 0) break;
}
}
}

View File

@ -21,6 +21,7 @@
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Hash.h"
#include <cmath>
#include <limits>
@ -298,7 +299,7 @@ public:
string toDecimalS() const; // return ASCII signed decimal number
string toDecimalU() const; // return ASCII unsigned decimal number
double toDouble() const;
uint32_t toHash() const;
V3Hash toHash() const;
uint32_t edataWord(int eword) const;
uint8_t dataByte(int byte) const;
uint32_t countBits(const V3Number& ctrl) const;

232
src/V3OptionParser.cpp Normal file
View File

@ -0,0 +1,232 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Command line option parser
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
#include "V3Global.h"
#include "V3Options.h"
#endif
#include "V3Error.h"
#include "V3OptionParser.h"
#include "V3String.h"
//######################################################################
// V3OptionParser::Impl
struct V3OptionParser::Impl {
// TYPES
// Setting for isOnOffAllowed() and isPartialMatchAllowed()
enum class en : uint8_t {
NONE, // "-opt"
ONOFF, // "-opt" and "-no-opt"
VALUE // "-opt val"
};
// Base class of actual action classes
template <en MODE, bool ALLOW_PARTIAL_MATCH = false>
class ActionBase VL_NOT_FINAL : public ActionIfs {
bool m_undocumented = false; // This option is not documented
public:
virtual bool isValueNeeded() const override final { return MODE == en::VALUE; }
virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; }
virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; }
virtual bool isUndocumented() const override { return m_undocumented; }
virtual void undocumented() override { m_undocumented = true; }
};
// Actual action classes
template <typename T> class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string
template <typename BOOL> class ActionOnOff; // "-opt" and "-no-opt" for bool-ish
class ActionCbCall; // Callback without argument for "-opt"
class ActionCbOnOff; // Callback for "-opt" and "-no-opt"
template <class T> class ActionCbVal; // Callback for "-opt val"
class ActionCbPartialMatch; // Callback "-O3" for "-O"
class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-"
// MEMBERS
std::map<const string, std::unique_ptr<ActionIfs>> m_options; // All actions for option
bool m_isFinalized{false}; // Becomes after finalize() is called
VSpellCheck m_spellCheck; // Suggests closest option when not found
};
//######################################################################
// Action classes in V3OptionParser::Impl
#define V3OPTION_PARSER_DEF_ACT_CLASS(className, type, body, enType) \
template <> class V3OptionParser::Impl::className<type> final : public ActionBase<enType> { \
type* m_valp; /* Pointer to a option variable*/ \
\
public: \
explicit className(type* valp) \
: m_valp(valp) {} \
virtual void exec(const char* optp, const char* argp) override { body; } \
}
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE);
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE);
#endif
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE);
V3OPTION_PARSER_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE);
V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF);
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
V3OPTION_PARSER_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)),
en::ONOFF);
#endif
#undef V3OPTION_PARSER_DEF_ACT_CLASS
#define V3OPTION_PARSER_DEF_ACT_CB_CLASS(className, funcType, body, ...) \
class V3OptionParser::Impl::className final : public ActionBase<__VA_ARGS__> { \
std::function<funcType> m_cb; /* Callback function */ \
\
public: \
using CbType = std::function<funcType>; \
explicit className(CbType cb) \
: m_cb(std::move(cb)) {} \
virtual void exec(const char* optp, const char* argp) override { body; } \
}
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE);
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF);
template <>
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal<int>, void(int), m_cb(std::atoi(argp)), en::VALUE);
template <>
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbVal<const char*>, void(const char*), m_cb(argp),
en::VALUE);
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE,
true);
V3OPTION_PARSER_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*),
m_cb(optp, argp), en::VALUE, true);
#undef V3OPTION_PARSER_DEF_ACT_CB_CLASS
//######################################################################
// Member functions of V3OptionParser
V3OptionParser::ActionIfs* V3OptionParser::find(const char* optp) {
auto it = m_pimpl->m_options.find(optp);
if (it != m_pimpl->m_options.end()) return it->second.get();
for (auto&& act : m_pimpl->m_options) {
if (act.second->isOnOffAllowed()) { // Find starts with "-no"
const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3);
if (nop && (act.first == nop || act.first == (string{"-"} + nop))) {
return act.second.get();
}
} else if (act.second->isPartialMatchAllowed()) {
if (!std::strncmp(optp, act.first.c_str(), act.first.length())) {
return act.second.get();
}
}
}
return nullptr;
}
template <class ACT, class ARG>
V3OptionParser::ActionIfs& V3OptionParser::add(const std::string& opt, ARG arg) {
UASSERT(!m_pimpl->m_isFinalized, "Cannot add after finalize() is called");
std::unique_ptr<ACT> act{new ACT{std::move(arg)}};
UASSERT(opt.size() >= 2, opt << " is too short");
UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'");
UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt);
const auto insertedResult = m_pimpl->m_options.emplace(opt, std::move(act));
UASSERT(insertedResult.second, opt << " is already registered");
return *insertedResult.first->second;
}
bool V3OptionParser::hasPrefixNo(const char* strp) {
UASSERT(strp[0] == '-', strp << " does not start with '-'");
if (strp[1] == '-') ++strp;
return std::strncmp(strp, "-no", 3) == 0;
}
int V3OptionParser::parse(int idx, int argc, char* argv[]) {
UASSERT(m_pimpl->m_isFinalized, "finalize() must be called before parse()");
const char* optp = argv[idx];
if (optp[0] == '-' && optp[1] == '-') ++optp;
ActionIfs* actp = find(optp);
if (!actp) return 0;
if (!actp->isValueNeeded()) {
actp->exec(optp, nullptr);
return 1;
} else if (idx + 1 < argc) {
actp->exec(optp, argv[idx + 1]);
return 2;
}
return 0;
}
string V3OptionParser::getSuggestion(const char* str) const {
return m_pimpl->m_spellCheck.bestCandidateMsg(str);
}
void V3OptionParser::addSuggestionCandidate(const string& s) {
m_pimpl->m_spellCheck.pushCandidate(s);
}
void V3OptionParser::finalize() {
UASSERT(!m_pimpl->m_isFinalized, "finalize() must not be called twice");
for (auto&& opt : m_pimpl->m_options) {
if (opt.second->isUndocumented()) continue;
m_pimpl->m_spellCheck.pushCandidate(opt.first);
if (opt.second->isOnOffAllowed()) m_pimpl->m_spellCheck.pushCandidate("-no" + opt.first);
}
m_pimpl->m_isFinalized = true;
}
V3OptionParser::V3OptionParser()
: m_pimpl{new Impl{}} {}
V3OptionParser::~V3OptionParser() = default;
//######################################################################
// Member functions of V3OptionParser::AppendHelper
#define V3OPTION_PARSER_DEF_OP(actKind, argType, actType) \
V3OptionParser::ActionIfs& V3OptionParser::AppendHelper::operator()( \
const char* optp, actKind, argType arg) const { \
return m_parser.add<Impl::actType>(optp, arg); \
}
V3OPTION_PARSER_DEF_OP(Set, bool*, ActionSet<bool>)
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
V3OPTION_PARSER_DEF_OP(Set, VOptionBool*, ActionSet<VOptionBool>)
#endif
V3OPTION_PARSER_DEF_OP(Set, int*, ActionSet<int>)
V3OPTION_PARSER_DEF_OP(Set, string*, ActionSet<string>)
V3OPTION_PARSER_DEF_OP(OnOff, bool*, ActionOnOff<bool>)
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
V3OPTION_PARSER_DEF_OP(OnOff, VOptionBool*, ActionOnOff<VOptionBool>)
#endif
V3OPTION_PARSER_DEF_OP(CbCall, Impl::ActionCbCall::CbType, ActionCbCall)
V3OPTION_PARSER_DEF_OP(CbOnOff, Impl::ActionCbOnOff::CbType, ActionCbOnOff)
V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal<int>::CbType, ActionCbVal<int>)
V3OPTION_PARSER_DEF_OP(CbVal, Impl::ActionCbVal<const char*>::CbType, ActionCbVal<const char*>)
#undef V3OPTION_PARSER_DEF_OP
V3OptionParser::ActionIfs&
V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatch,
Impl::ActionCbPartialMatch::CbType cb) const {
const size_t prefixLen = std::strlen(optp);
auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); };
return m_parser.add<Impl::ActionCbPartialMatch>(optp, std::move(wrap));
}
V3OptionParser::ActionIfs&
V3OptionParser::AppendHelper::operator()(const char* optp, CbPartialMatchVal,
Impl::ActionCbPartialMatchVal::CbType cb) const {
const size_t prefixLen = std::strlen(optp);
auto wrap
= [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); };
return m_parser.add<Impl::ActionCbPartialMatchVal>(optp, std::move(wrap));
}

157
src/V3OptionParser.h Normal file
View File

@ -0,0 +1,157 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Command line option parser
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3OPTION_PARSER_H_
#define VERILATOR_V3OPTION_PARSER_H_
#include "config_build.h"
#include "verilatedos.h"
#include <functional>
#include <memory>
#include <string>
// Not to include V3Option.h here so that VlcMain or other executables can use this file easily.
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
class VOptionBool;
#endif
// Typycal usage would look as below.
// See also V3Options::parseOptsList() in V3Optoins.cpp for more detailed usage.
//
// V3OptionParser parser;
// V3OptionParser::AppendHelper DECL_OPTION{parser};
// V3OPTION_PARSER_DECL_TAGS;
//
// DECL_OPPTION("-help", CbCall, []() { showHelp(); });
// DECL_OPPTION("-some_opt", Set, &m_intMember});
// parser.finalize();
// for (int i = 1; i < argc;) {
// if (int consumed = parser.parse(i, argc, argv)) {
// i += consumed;
// } else { // error
// cerr << parser.getSuggestion(argv[i]) << endl;
// }
// }
//
//######################################################################
// V3 OptionParser
class V3OptionParser final {
public:
// TYPES
class ActionIfs;
// Functor to register options to V3OptionParser
class AppendHelper;
struct Impl;
private:
// MEMBERS
std::unique_ptr<Impl> m_pimpl;
// METHODS
ActionIfs* find(const char* optp);
template <class ACT, class ARG> ActionIfs& add(const string& opt, ARG arg);
static bool hasPrefixNo(const char* strp); // Returns true if strp starts with "-no"
public:
// METHODS
// Returns how many args are consumed. 0 means not match
int parse(int idx, int argc, char* argv[]);
// Find the most similar option
string getSuggestion(const char* str) const;
void addSuggestionCandidate(const string& s);
// Call this function after all options are registered.
void finalize();
// CONSTRUCTORS
V3OptionParser();
~V3OptionParser();
};
class V3OptionParser::ActionIfs VL_NOT_FINAL {
public:
virtual ~ActionIfs() = default;
virtual bool isValueNeeded() const = 0; // Need val of "-opt val"
virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd
virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal"
virtual bool isUndocumented() const = 0; // Will not be suggested in typo
// Set a value or run callback
virtual void exec(const char* optp, const char* valp) = 0;
// Mark this option undocumented. (Exclude this option from suggestion list).
virtual void undocumented() = 0;
};
// A helper class to register options
class V3OptionParser::AppendHelper final {
public:
// TYPES
// Tag to specify which operator() to call
struct Set {}; // For ActionSet
struct OnOff {}; // For ActionOnOff
struct CbCall {}; // For ActionCbCall
struct CbOnOff {}; // For ActionOnOff
struct CbVal {}; // For ActionCbVal
struct CbPartialMatch {}; // For ActionCbPartialMatch
struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal
private:
// MEMBERS
V3OptionParser& m_parser; // The actual option registory
public:
// METHODS
ActionIfs& operator()(const char* optp, Set, bool*) const;
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
ActionIfs& operator()(const char* optp, Set, VOptionBool*) const;
#endif
ActionIfs& operator()(const char* optp, Set, int*) const;
ActionIfs& operator()(const char* optp, Set, string*) const;
ActionIfs& operator()(const char* optp, OnOff, bool*) const;
#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
ActionIfs& operator()(const char* optp, OnOff, VOptionBool*) const;
#endif
ActionIfs& operator()(const char* optp, CbCall, std::function<void(void)>) const;
ActionIfs& operator()(const char* optp, CbOnOff, std::function<void(bool)>) const;
ActionIfs& operator()(const char* optp, CbVal, std::function<void(int)>) const;
ActionIfs& operator()(const char* optp, CbVal, std::function<void(const char*)>) const;
ActionIfs& operator()(const char* optp, CbPartialMatch,
std::function<void(const char*)>) const;
ActionIfs& operator()(const char* optp, CbPartialMatchVal,
std::function<void(const char*, const char*)>) const;
// CONSTRUCTORS
explicit AppendHelper(V3OptionParser& parser)
: m_parser(parser) {}
};
#define V3OPTION_PARSER_DECL_TAGS \
const auto Set VL_ATTR_UNUSED = V3OptionParser::AppendHelper::Set{}; \
const auto OnOff VL_ATTR_UNUSED = V3OptionParser::AppendHelper::OnOff{}; \
const auto CbCall VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbCall{}; \
const auto CbOnOff VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbOnOff{}; \
const auto CbVal VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbVal{}; \
const auto CbPartialMatch VL_ATTR_UNUSED = V3OptionParser::AppendHelper::CbPartialMatch{}; \
const auto CbPartialMatchVal VL_ATTR_UNUSED \
= V3OptionParser::AppendHelper::CbPartialMatchVal {}
//######################################################################
#endif // guard

View File

@ -19,9 +19,9 @@
#include "V3Global.h"
#include "V3Ast.h"
#include "V3String.h"
#include "V3Os.h"
#include "V3Options.h"
#include "V3OptionParser.h"
#include "V3Error.h"
#include "V3File.h"
#include "V3PreShell.h"
@ -37,7 +37,6 @@
#include <cctype>
#include <dirent.h>
#include <fcntl.h>
#include <functional>
#include <list>
#include <map>
#include <memory>
@ -106,226 +105,6 @@ public:
~V3OptionsImp() = default;
};
//######################################################################
// V3 OptionParser
class V3OptionsParser final {
public:
// TYPES
class ActionIfs VL_NOT_FINAL {
public:
virtual ~ActionIfs() = default;
virtual bool isValueNeeded() const = 0; // Need val of "-opt val"
virtual bool isOnOffAllowed() const = 0; // true if "-no-opt" is allowd
virtual bool isPartialMatchAllowed() const = 0; // true if "-Wno-" matches "-Wno-fatal"
virtual bool isUndocumented() const = 0; // Will not be suggested in typo
// Set a value or run callback
virtual void exec(const char* optp, const char* valp) = 0;
virtual void undocumented() = 0;
};
enum class en { // Setting for isOnOffAllowed() and isPartialMatchAllowed()
NONE, // "-opt"
ONOFF, // "-opt" and "-no-opt"
VALUE // "-opt val"
};
// Base class of actual action classes
template <en MODE, bool ALLOW_PARTIAL_MATCH = false>
class ActionBase VL_NOT_FINAL : public ActionIfs {
bool m_undocumented = false; // This option is not documented
public:
virtual bool isValueNeeded() const override final { return MODE == en::VALUE; }
virtual bool isOnOffAllowed() const override final { return MODE == en::ONOFF; }
virtual bool isPartialMatchAllowed() const override final { return ALLOW_PARTIAL_MATCH; }
virtual bool isUndocumented() const override { return m_undocumented; }
virtual void undocumented() override { m_undocumented = true; }
};
// Actual action classes
template <typename T> class ActionSet; // "-opt" for bool-ish, "-opt val" for int and string
template <typename BOOL> class ActionOnOff; // "-opt" and "-no-opt" for bool-ish
class ActionCbCall; // Callback without argument for "-opt"
class ActionCbOnOff; // Callback for "-opt" and "-no-opt"
template <class T> class ActionCbVal; // Callback for "-opt val"
class ActionCbPartialMatch; // Callback "-O3" for "-O"
class ActionCbPartialMatchVal; // Callback "-debugi-V3Options 3" for "-debugi-"
// Functor to register options to V3OptionsParser
struct AppendHelper;
private:
// MEMBERS
std::map<const string, std::unique_ptr<ActionIfs>> m_options; // All actions for option
bool m_isFinalized{false}; // Becomes after finalize() is called
VSpellCheck m_spellCheck; // Suggests closest option when not found
// METHODS
ActionIfs* find(const char* optp) {
auto it = m_options.find(optp);
if (it != m_options.end()) return it->second.get();
for (auto&& act : m_options) {
if (act.second->isOnOffAllowed()) { // Find starts with "-no"
const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3);
if (nop && (act.first == nop || act.first == (string{"-"} + nop))) {
return act.second.get();
}
} else if (act.second->isPartialMatchAllowed()) {
if (!std::strncmp(optp, act.first.c_str(), act.first.length())) {
return act.second.get();
}
}
}
return nullptr;
}
template <class ACT, class ARG> ActionIfs& add(const std::string& opt, ARG arg) {
UASSERT(!m_isFinalized, "Cannot add after finalize() is called");
std::unique_ptr<ACT> act{new ACT{std::move(arg)}};
UASSERT(opt.size() >= 2, opt << " is too short");
UASSERT(opt[0] == '-' || opt[0] == '+', opt << " does not start with either '-' or '+'");
UASSERT(!(opt[0] == '-' && opt[1] == '-'), "Option must have single '-', but " << opt);
const auto insertedResult = m_options.emplace(opt, std::move(act));
UASSERT(insertedResult.second, opt << " is already registered");
return *insertedResult.first->second;
}
static bool hasPrefixNo(const char* strp) { // Returns true if strp starts with "-no"
UASSERT(strp[0] == '-', strp << " does not start with '-'");
if (strp[1] == '-') ++strp;
return std::strncmp(strp, "-no", 3) == 0;
}
public:
// METHODS
// Returns how many args are consumed. 0 means not match
int parse(int idx, int argc, char* argv[]) {
UASSERT(m_isFinalized, "finalize() must be called before parse()");
const char* optp = argv[idx];
if (optp[0] == '-' && optp[1] == '-') ++optp;
ActionIfs* actp = find(optp);
if (!actp) return 0;
if (!actp->isValueNeeded()) {
actp->exec(optp, nullptr);
return 1;
} else if (idx + 1 < argc) {
actp->exec(optp, argv[idx + 1]);
return 2;
}
return 0;
}
// Find the most similar option
string getSuggestion(const char* str) const { return m_spellCheck.bestCandidateMsg(str); }
void addSuggestionCandidate(const string& s) { m_spellCheck.pushCandidate(s); }
void finalize() {
UASSERT(!m_isFinalized, "finalize() must not be called twice");
for (auto&& opt : m_options) {
if (opt.second->isUndocumented()) continue;
m_spellCheck.pushCandidate(opt.first);
if (opt.second->isOnOffAllowed()) m_spellCheck.pushCandidate("-no" + opt.first);
}
m_isFinalized = true;
}
};
#define V3OPTIONS_DEF_ACT_CLASS(className, type, body, enType) \
template <> class V3OptionsParser::className<type> final : public ActionBase<enType> { \
type* m_valp; /* Pointer to a option variable*/ \
\
public: \
explicit className(type* valp) \
: m_valp(valp) {} \
virtual void exec(const char* optp, const char* argp) override { body; } \
}
V3OPTIONS_DEF_ACT_CLASS(ActionSet, bool, *m_valp = true, en::NONE);
V3OPTIONS_DEF_ACT_CLASS(ActionSet, VOptionBool, m_valp->setTrueOrFalse(true), en::NONE);
V3OPTIONS_DEF_ACT_CLASS(ActionSet, int, *m_valp = std::atoi(argp), en::VALUE);
V3OPTIONS_DEF_ACT_CLASS(ActionSet, string, *m_valp = argp, en::VALUE);
V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, bool, *m_valp = !hasPrefixNo(optp), en::ONOFF);
V3OPTIONS_DEF_ACT_CLASS(ActionOnOff, VOptionBool, m_valp->setTrueOrFalse(!hasPrefixNo(optp)),
en::ONOFF);
#undef V3OPTIONS_DEF_ACT_CLASS
#define V3OPTIONS_DEF_ACT_CB_CLASS(className, funcType, body, ...) \
class V3OptionsParser::className final : public ActionBase<__VA_ARGS__> { \
std::function<funcType> m_cb; /* Callback function */ \
\
public: \
using CbType = std::function<funcType>; \
explicit className(CbType cb) \
: m_cb(std::move(cb)) {} \
virtual void exec(const char* optp, const char* argp) override { body; } \
}
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbCall, void(void), m_cb(), en::NONE);
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbOnOff, void(bool), m_cb(!hasPrefixNo(optp)), en::ONOFF);
template <>
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal<int>, void(int), m_cb(std::atoi(argp)), en::VALUE);
template <>
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbVal<const char*>, void(const char*), m_cb(argp), en::VALUE);
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatch, void(const char*), m_cb(optp), en::NONE, true);
V3OPTIONS_DEF_ACT_CB_CLASS(ActionCbPartialMatchVal, void(const char*, const char*),
m_cb(optp, argp), en::VALUE, true);
#undef V3OPTIONS_DEF_ACT_CB_CLASS
// A helper class to register options
struct V3OptionsParser::AppendHelper final {
V3OptionsParser& m_parser; // The actual option registory
// TYPES
// Tag to specify which operator() to call
struct Set {}; // For ActionSet
struct OnOff {}; // For ActionOnOff
struct CbCall {}; // For ActionCbCall
struct CbOnOff {}; // For ActionOnOff
struct CbVal {}; // For ActionCbVal
struct CbPartialMatch {}; // For ActionCbPartialMatch
struct CbPartialMatchVal {}; // For ActionCbPartialMatchVal
// METHODS
#define V3OPTIONS_DEF_OP(actKind, argType, actType) \
ActionIfs& operator()(const char* optp, actKind, argType arg) const { \
return m_parser.add<actType>(optp, arg); \
}
V3OPTIONS_DEF_OP(Set, bool*, ActionSet<bool>)
V3OPTIONS_DEF_OP(Set, VOptionBool*, ActionSet<VOptionBool>)
V3OPTIONS_DEF_OP(Set, int*, ActionSet<int>)
V3OPTIONS_DEF_OP(Set, string*, ActionSet<string>)
V3OPTIONS_DEF_OP(OnOff, bool*, ActionOnOff<bool>)
V3OPTIONS_DEF_OP(OnOff, VOptionBool*, ActionOnOff<VOptionBool>)
V3OPTIONS_DEF_OP(CbCall, ActionCbCall::CbType, ActionCbCall)
V3OPTIONS_DEF_OP(CbOnOff, ActionCbOnOff::CbType, ActionCbOnOff)
V3OPTIONS_DEF_OP(CbVal, ActionCbVal<int>::CbType, ActionCbVal<int>)
V3OPTIONS_DEF_OP(CbVal, ActionCbVal<const char*>::CbType, ActionCbVal<const char*>)
#undef V3OPTIONS_DEF_OP
// Syntax sugar to register a member function of V3Options directry
ActionIfs& operator()(const char* optp, CbVal,
std::pair<V3Options*, void (V3Options::*)(int)> arg) const {
auto cb = [arg](int v) { (arg.first->*arg.second)(v); };
return m_parser.add<ActionCbVal<int>>(optp, cb);
}
ActionIfs& operator()(const char* optp, CbVal,
std::pair<V3Options*, void (V3Options::*)(const string&)> arg) const {
auto cb = [arg](const string& v) { (arg.first->*arg.second)(v); };
return m_parser.add<ActionCbVal<const char*>>(optp, cb);
}
// Callback of partial match expects prefix to be removed, so wrap the given callback
ActionIfs& operator()(const char* optp, CbPartialMatch,
ActionCbPartialMatch::CbType cb) const {
const size_t prefixLen = std::strlen(optp);
auto wrap = [prefixLen, cb](const char* optp) { cb(optp + prefixLen); };
return m_parser.add<ActionCbPartialMatch>(optp, std::move(wrap));
}
ActionIfs& operator()(const char* optp, CbPartialMatchVal,
ActionCbPartialMatchVal::CbType cb) const {
const size_t prefixLen = std::strlen(optp);
auto wrap
= [prefixLen, cb](const char* optp, const char* argp) { cb(optp + prefixLen, argp); };
return m_parser.add<ActionCbPartialMatchVal>(optp, std::move(wrap));
}
};
//######################################################################
// V3LangCode class functions
@ -1008,6 +787,10 @@ void V3Options::notify() {
cmdfl->v3warn(E_UNSUPPORTED,
"--main not usable with SystemC. Suggest see examples for sc_main().");
}
if (coverage() && savable()) {
cmdfl->v3error("--coverage and --savable not supported together");
}
}
//######################################################################
@ -1112,20 +895,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
addArg(argv[i]); // -f's really should be inserted in the middle, but this is for debug
}
V3OptionsParser parser;
const auto Set = V3OptionsParser::AppendHelper::Set{};
const auto OnOff = V3OptionsParser::AppendHelper::OnOff{};
const auto CbCall = V3OptionsParser::AppendHelper::CbCall{};
const auto CbOnOff = V3OptionsParser::AppendHelper::CbOnOff{};
const auto CbVal = V3OptionsParser::AppendHelper::CbVal{};
const auto CbPartialMatch = V3OptionsParser::AppendHelper::CbPartialMatch{};
const auto CbPartialMatchVal = V3OptionsParser::AppendHelper::CbPartialMatchVal{};
V3OptionsParser::AppendHelper DECL_OPTION{parser};
V3OptionParser parser;
V3OptionParser::AppendHelper DECL_OPTION{parser};
V3OPTION_PARSER_DECL_TAGS;
auto callStrSetter = [this](void (V3Options::*cbStr)(const string&)) {
return [this, cbStr](const string& v) { (this->*cbStr)(v); };
};
// Usage
// DECL_OPTION("-option", action, pointer_or_lambda);
// action: one of Set, OnOff, CbCall, CbOnOff, CbVal, CbPartialMatch, and CbPartialMatchVal
// Set : Set value to a variable, pointer_or_lambda must be a pointer to the
// variable.
// variable.
// true is set to bool-ish variable when '-opt' is passed to verilator.
// val is set to int and string variable when '-opt val' is passed.
// OnOff : Set value to a bool-ish variable, pointer_or_lambda must be a pointer
@ -1138,7 +919,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
// CbVal : Call lambda or function that takes int or const char*.
// "-opt val" is passed to verilator, val is passed to the lambda.
// If a function to be called is a member of V3Options that only takes
// bool/const string&, {this, &V3Options::memberFunc} can be passed
// const string&, callStrSetter(&V3Options::memberFunc) can be passed
// instead of lambda as a syntax sugar.
// CbPartialMatch : Call lambda or function that takes remaining string.
// e.g. DECL_OPTION("-opt-", CbPartialMatch, [](const char*optp) { cout <<
@ -1201,14 +982,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-bin", Set, &m_bin);
DECL_OPTION("-build", Set, &m_build);
DECL_OPTION("-CFLAGS", CbVal, {this, &V3Options::addCFlags});
DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
DECL_OPTION("-cc", CbCall, [this]() {
m_outFormatOk = true;
m_systemC = false;
});
DECL_OPTION("-cdc", OnOff, &m_cdc);
DECL_OPTION("-clk", CbVal, {this, &V3Options::addClocker});
DECL_OPTION("-no-clk", CbVal, {this, &V3Options::addNoClocker});
DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
DECL_OPTION("-comp-limit-members", Set,
&m_compLimitMembers)
@ -1244,7 +1025,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-D", CbPartialMatch, [this](const char* valp) { addDefine(valp, false); });
DECL_OPTION("-debug", CbCall, [this]() { setDebugMode(3); });
DECL_OPTION("-debugi", CbVal, {this, &V3Options::setDebugMode});
DECL_OPTION("-debugi", CbVal, [this](int v) { setDebugMode(v); });
DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
setDebugSrcLevel(optp, std::atoi(valp));
});
@ -1279,6 +1060,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-E", Set, &m_preprocOnly);
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
DECL_OPTION("-exe", OnOff, &m_exe);
DECL_OPTION("-expand-limit", CbVal,
[this](const char* valp) { m_expandLimit = std::atoi(valp); });
DECL_OPTION("-F", CbVal, [this, fl, &optdir](const char* valp) {
parseOptsFile(fl, parseFileArg(optdir, valp), true);
@ -1320,7 +1103,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
});
DECL_OPTION("-inline-mult", Set, &m_inlineMult);
DECL_OPTION("-LDFLAGS", CbVal, {this, &V3Options::addLdLibs});
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
auto setLang = [this, fl](const char* valp) {
V3LangCode optval = V3LangCode(valp);
if (optval.legal()) {
@ -1340,7 +1123,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
DECL_OPTION("-MAKEFLAGS", CbVal, {this, &V3Options::addMakeFlags});
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
DECL_OPTION("-MMD", OnOff, &m_makeDepend);
DECL_OPTION("-MP", OnOff, &m_makePhony);
DECL_OPTION("-Mdir", CbVal, [this](const char* valp) {
@ -1465,6 +1248,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
fl->v3warn(DEPRECATED, "Deprecated --no-relative-cfuncs, unnecessary with C++11.");
});
DECL_OPTION("-relative-includes", OnOff, &m_relativeIncludes);
DECL_OPTION("-reloop-limit", CbVal, [this, fl](const char* valp) {
m_reloopLimit = std::atoi(valp);
if (m_reloopLimit < 2) { fl->v3error("--reloop-limit must be >= 2: " << valp); }
});
DECL_OPTION("-report-unoptflat", OnOff, &m_reportUnoptflat);
DECL_OPTION("-rr", CbCall, []() {}); // Processed only in bin/verilator shell

View File

@ -285,6 +285,7 @@ private:
int m_convergeLimit = 100; // main switch: --converge-limit
int m_coverageMaxWidth = 256; // main switch: --coverage-max-width
int m_dumpTree = 0; // main switch: --dump-tree
int m_expandLimit = 64; // main switch: --expand-limit
int m_gateStmts = 100; // main switch: --gate-stmts
int m_ifDepth = 0; // main switch: --if-depth
int m_inlineMult = 2000; // main switch: --inline-mult
@ -295,6 +296,7 @@ private:
int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs
int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace
int m_pinsBv = 65; // main switch: --pins-bv
int m_reloopLimit = 40; // main switch: --reloop-limit
VOptionBool m_skipIdentical; // main switch: --skip-identical
int m_threads = 0; // main switch: --threads (0 == --no-threads)
int m_threadsMaxMTasks = 0; // main switch: --threads-max-mtasks
@ -481,6 +483,7 @@ public:
int coverageMaxWidth() const { return m_coverageMaxWidth; }
int dumpTree() const { return m_dumpTree; }
bool dumpTreeAddrids() const { return m_dumpTreeAddrids; }
int expandLimit() const { return m_expandLimit; }
int gateStmts() const { return m_gateStmts; }
int ifDepth() const { return m_ifDepth; }
int inlineMult() const { return m_inlineMult; }
@ -491,6 +494,7 @@ public:
int outputSplitCFuncs() const { return m_outputSplitCFuncs; }
int outputSplitCTrace() const { return m_outputSplitCTrace; }
int pinsBv() const { return m_pinsBv; }
int reloopLimit() const { return m_reloopLimit; }
VOptionBool skipIdentical() const { return m_skipIdentical; }
int threads() const { return m_threads; }
int threadsMaxMTasks() const { return m_threadsMaxMTasks; }

View File

@ -1723,50 +1723,73 @@ void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* d
AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp,
AstCFunc*& newFuncpr, int& newStmtsr) {
AstActive* activep = nullptr;
AstScope* scopep = lvertexp->scopep();
AstSenTree* domainp = lvertexp->domainp();
AstScope* const scopep = lvertexp->scopep();
AstSenTree* const domainp = lvertexp->domainp();
AstNode* nodep = lvertexp->nodep();
AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
AstNodeModule* const modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func
UASSERT(modp, "nullptr");
if (VN_IS(nodep, SenTree)) {
// Just ignore sensitivities, we'll deal with them when we move statements that need them
} else { // Normal logic
// Make or borrow a CFunc to contain the new statements
if (v3Global.opt.profCFuncs()
|| (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
newFuncpr->symProlog(true);
newStmtsr = 0;
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
scopep->addActivep(newFuncpr);
// Where will we be adding the call?
activep = new AstActive(nodep->fileline(), name, domainp);
// Add a top call to it
AstCCall* callp = new AstCCall(nodep->fileline(), newFuncpr);
callp->argTypes("vlSymsp");
activep->addStmtsp(callp);
UINFO(6, " New " << newFuncpr << endl);
// Move the logic into a CFunc
nodep->unlinkFrBack();
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
// procedures. Everything else is handled in one go
AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure);
if (procp && !v3Global.opt.profCFuncs()) {
nodep = procp->bodysp();
pushDeletep(procp);
}
// Move the logic to the function we're creating
nodep->unlinkFrBack();
if (domainp == m_deleteDomainp) {
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
if (v3Global.opt.outputSplitCFuncs()) {
// Add in the number of nodes we're adding
EmitCBaseCounterVisitor visitor(nodep);
newStmtsr += visitor.count();
while (nodep) {
// Make or borrow a CFunc to contain the new statements
if (v3Global.opt.profCFuncs()
|| (v3Global.opt.outputSplitCFuncs()
&& v3Global.opt.outputSplitCFuncs() < newStmtsr)) {
// Put every statement into a unique function to ease profiling or reduce function
// size
newFuncpr = nullptr;
}
if (!newFuncpr && domainp != m_deleteDomainp) {
const string name = cfuncName(modp, domainp, scopep, nodep);
newFuncpr = new AstCFunc(nodep->fileline(), name, scopep);
newFuncpr->argTypes(EmitCBaseVisitor::symClassVar());
newFuncpr->symProlog(true);
newStmtsr = 0;
if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true);
scopep->addActivep(newFuncpr);
// Create top call to it
AstCCall* const callp = new AstCCall(nodep->fileline(), newFuncpr);
callp->argTypes("vlSymsp");
// Where will we be adding the call?
AstActive* const newActivep = new AstActive(nodep->fileline(), name, domainp);
newActivep->addStmtsp(callp);
if (!activep) {
activep = newActivep;
} else {
activep->addNext(newActivep);
}
UINFO(6, " New " << newFuncpr << endl);
}
AstNode* const nextp = nodep->nextp();
// When processing statements in a procedure, unlink the current statement
if (nodep->backp()) nodep->unlinkFrBack();
if (domainp == m_deleteDomainp) {
UINFO(4, " Ordering deleting pre-settled " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
newFuncpr->addStmtsp(nodep);
if (v3Global.opt.outputSplitCFuncs()) {
// Add in the number of nodes we're adding
EmitCBaseCounterVisitor visitor(nodep);
newStmtsr += visitor.count();
}
}
nodep = nextp;
}
}
return activep;

View File

@ -56,7 +56,7 @@
#include "V3Parse.h"
#include "V3Width.h"
#include "V3Unroll.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include <deque>
#include <map>
@ -314,7 +314,7 @@ class ParamProcessor final {
key += "[" + cvtToStr(bdtp->left()) + ":" + cvtToStr(bdtp->right()) + "]";
}
}
V3Hash hash = V3Hashed::uncachedHash(nodep);
V3Hash hash = V3Hasher::uncachedHash(nodep);
// Force hash collisions -- for testing only
if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash();
int num;

View File

@ -146,6 +146,10 @@ private:
void lexStreamDepthAdd(int delta);
};
//======================================================================
// Enum Class for `pragma protect encoding types
enum class Enctype : uint8_t { UUENCODE, BASE64, QUOTE_PRINTABLE, RAW, ERR };
//======================================================================
// Class entry for each per-lexer state
@ -170,6 +174,9 @@ public: // Used only by V3PreLex.cpp and V3PreProc.cpp
bool m_defQuote = false; // Definition value inside quote
string m_defValue; // Definition value being built.
int m_enterExit = 0; // For VL_LINE, the enter/exit level
int m_protLength = 0; // unencoded length for BASE64
int m_protBytes = 0; // decoded length for BASE64
Enctype m_encType; // encoding type for `pragma protect
// CONSTRUCTORS
V3PreLex(V3PreProcImp* preimpp, FileLine* filelinep)

View File

@ -77,6 +77,11 @@ static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l)
%x ARGMODE
%x INCMODE
%x PRTMODE
%x PRAGMA
%x PRAGMAERR
%x PRAGMAPRT
%x PRAGMAPRTERR
%x ENCBASE64
/* drop: Drop Ctrl-Z - can't pass thru or may EOF the output too soon */
@ -112,12 +117,119 @@ bom [\357\273\277]
<INITIAL>"`undef" { FL_FWDC; return VP_UNDEF; }
<INITIAL>"`undefineall" { FL_FWDC; return VP_UNDEFINEALL; }
<INITIAL>"`error" { FL_FWDC; if (!pedantic()) return VP_ERROR; else return VP_DEFREF; }
<INITIAL>"`pragma"{wsn}*[^\n\r]* { FL_FWDC;
const char* pointp = yytext + strlen("`pragma");
while (isspace(*pointp)) ++pointp;
if (!*pointp && v3Global.opt.pedantic()) {
yyerrorf("`pragma is missing a pragma_expression.");
/* We wanted this to be next to `protect But it must be before `pramga */
/* we win only because we both match to the end of the line so the length */
/* is equal and we are first*/
<PRAGMAPRT>"encoding"{wsn}*[^\n\r]* { FL_FWDC;
int res;
char enctype[16]; // long enough to hold "quote-printable"
if (LEXP->m_protBytes > 0) {
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "multiple `pragma protected encoding sections");
}
res = sscanf(yytext + strlen("encoding"), " = (enctype = \"%15[A-Za-z0-9]\", line_length = %d, bytes = %d)", &enctype[0], &LEXP->m_protLength, &LEXP->m_protBytes);
if (res == 0)
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma protected encoding must have an \"enctype\" field");
LEXP->m_encType = !VL_STRCASECMP(enctype, "uuencode") ? Enctype::UUENCODE :
!VL_STRCASECMP(enctype, "base64") ? Enctype::BASE64 :
!VL_STRCASECMP(enctype, "quoted-printable") ? Enctype::QUOTE_PRINTABLE :
!VL_STRCASECMP(enctype, "raw") ? Enctype::RAW : Enctype::ERR;
if (LEXP->m_encType == Enctype::ERR)
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "Illegal encoding type for `pragma protected encoding");
if (LEXP->m_encType != Enctype::BASE64)
LEXP->curFilelinep()->v3warn(E_UNSUPPORTED, "Unsupported: only BASE64 is recognized for `pragma protected encoding");
if (res == 3) {
if ((LEXP->m_encType == Enctype::BASE64) && (LEXP->m_protLength & 3))
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "line_length must be multiple of 4 for BASE64");
} else {
// default values
LEXP->m_protBytes = 0;
LEXP->m_protLength = 76; // ?? default value not mentioned in IEEE spec
}
BEGIN(INITIAL);
return VP_TEXT;
}
<PRAGMAPRT>"key_block"{wsn}*[\n\r] {
FL_FWDC;
linenoInc();
BEGIN(ENCBASE64);
return VP_TEXT; }
<PRAGMAPRT>"data_block"{wsn}*[\n\r] {
FL_FWDC;
linenoInc();
LEXP->curFilelinep()->v3warn(PROTECTED, "A '`pragma protected data_block' encrypted section was detected and will be skipped.");
BEGIN(ENCBASE64);
return VP_TEXT; }
<PRAGMAPRT>("begin_protected"|"end_protected")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; }
<PRAGMAPRT>"version="[^\n\r]*[\n\r] {
FL_FWDC;
linenoInc();
BEGIN(INITIAL);
return VP_TEXT; }
<PRAGMAPRT>("encrypt_agent"|"encrypt_agent_info"|"key_keyowner"|"key_keyname"){wsn}*={wsn}*[\"][^\n\r]*[\"][\n\r] {
FL_FWDC;
linenoInc();
BEGIN(INITIAL);
return VP_TEXT; }
<PRAGMAPRT>("data_method"|"key_method"){wsn}*={wsn}*[\"][^\n\r]*[\"][\n\r] {
FL_FWDC;
linenoInc();
BEGIN(INITIAL);
return VP_TEXT; }
/* catch-all for unknown '`pragma protect' rules */
<PRAGMAPRT>. { yyless(0);
BEGIN(PRAGMAPRTERR);
return VP_TEXT; }
<ENCBASE64>[A-Za-z0-9+/]+[=]{0,2}[\n\r] { FL_FWDC; linenoInc(); FL_BRK;
if ((yyourleng()-1) <= size_t(LEXP->m_protLength) && ((yyleng & 3) == 1)) {
LEXP->m_protBytes -= (yyleng-1)/4*3;
} else {
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 line too long in `pragma protect key_bloock/data_block");
}
if (yytext[yyleng-3] == '=')
LEXP->m_protBytes++;
if (yytext[yyleng-2] == '=')
LEXP->m_protBytes++;
if (LEXP->m_protBytes == 0) {
BEGIN(INITIAL);
} else if (LEXP->m_protBytes < 0)
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 encoding (too short) in `pragma protect key_bloock/data_block");
/*return VP_TEXT;*/ }
<ENCBASE64>{wsn}*[\n\r] { FL_FWDC;
if (LEXP->m_protBytes != 0)
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "BASE64 encoding length mismatch in `pragma protect key_bloock/data_block");
linenoInc(); BEGIN(INITIAL);
return VP_TEXT; }
/* Catch only empty `pragma lines */
<INITIAL>"`pragma"{wsn}*[\n\r] {
yyless(yyleng-1); FL_FWDC;
if (v3Global.opt.pedantic()) {
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma is missing a pragma_expression.");
}
return VP_TEXT; }
/* catch all other nonempty `pragma */
<INITIAL>"`pragma"{wsn}*[^\n\r] {
yyless(yyleng-1); FL_FWDC;
if (!v3Global.opt.preprocOnly())
BEGIN(PRAGMA);
return VP_TEXT; }
<PRAGMA>"protect"{wsn}* { FL_FWDC; BEGIN(PRAGMAPRT); return VP_TEXT;}
/* catch-all for unknown `pragma rules */
<PRAGMA>. { yyless(0);
BEGIN(PRAGMAERR);
return VP_TEXT; }
/* the catch-all rule only got 1 char, lets get all line */
<PRAGMAERR>[^\n\r]* { FL_FWDC;
/* Add a warning here for unknown pragmas if desired, at the moment , we don't */
/* LEXP->curFilelinep()->v3warn(BADPRAGMA, "Unknown `pragma"); */
BEGIN(INITIAL);
return VP_TEXT; }
<PRAGMAPRTERR>[^\n\r]* { FL_FWDC;
LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "Unknown '`pragma protect' error");
BEGIN(INITIAL);
return VP_TEXT; }
<INITIAL,STRIFY>"`__FILE__" { FL_FWDC;
static string rtnfile;

View File

@ -30,17 +30,21 @@
#include "V3Global.h"
#include "V3Premit.h"
#include "V3Ast.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <algorithm>
constexpr int STATIC_CONST_MIN_WIDTH = 256; // Minimum size to extract to static constant
//######################################################################
// Structure for global state
class PremitAssignVisitor final : public AstNVisitor {
private:
// NODE STATE
// AstVar::user4() // bool; occurs on LHS of current assignment
AstUser4InUse m_inuser4;
// AstVar::user3() // bool; occurs on LHS of current assignment
AstUser3InUse m_inuser3;
// STATE
bool m_noopt = false; // Disable optimization of variables in this block
@ -50,7 +54,7 @@ private:
// VISITORS
virtual void visit(AstNodeAssign* nodep) override {
// AstNode::user4ClearTree(); // Implied by AstUser4InUse
// AstNode::user3ClearTree(); // Implied by AstUser3InUse
// LHS first as fewer varrefs
iterateAndNextNull(nodep->lhsp());
// Now find vars marked as lhs
@ -59,9 +63,9 @@ private:
virtual void visit(AstVarRef* nodep) override {
// it's LHS var is used so need a deep temporary
if (nodep->access().isWriteOrRW()) {
nodep->varp()->user4(true);
nodep->varp()->user3(true);
} else {
if (nodep->varp()->user4()) {
if (nodep->varp()->user3()) {
if (!m_noopt) UINFO(4, "Block has LHS+RHS var: " << nodep << endl);
m_noopt = true;
}
@ -88,9 +92,11 @@ private:
// AstNodeMath::user() -> bool. True if iterated already
// AstShiftL::user2() -> bool. True if converted to conditional
// AstShiftR::user2() -> bool. True if converted to conditional
// AstConst::user2p() -> Replacement static variable pointer
// *::user4() -> See PremitAssignVisitor
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
// AstUser4InUse part of V3Hasher via V3DupFinder
// STATE
AstNodeModule* m_modp = nullptr; // Current module
@ -100,6 +106,11 @@ private:
AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions
bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts
V3DupFinder m_dupFinder; // Duplicate finder for static constants that can be reused
VDouble0 m_staticConstantsExtracted; // Statistic tracking
VDouble0 m_staticConstantsReused; // Statistic tracking
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@ -139,14 +150,6 @@ private:
}
}
AstVar* getBlockTemp(AstNode* nodep) {
string newvarname = (string("__Vtemp") + cvtToStr(m_modp->varNumGetInc()));
AstVar* varp
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
m_cfuncp->addInitsp(varp);
return varp;
}
void insertBeforeStmt(AstNode* newp) {
// Insert newp before m_stmtp
if (m_inWhilep) {
@ -173,28 +176,65 @@ private:
AstNRelinker linker;
nodep->unlinkFrBack(&linker);
AstVar* varp = getBlockTemp(nodep);
AstVar* varp = nullptr;
AstConst* const constp = VN_CAST(nodep, Const);
const bool useStatic = constp && (constp->width() >= STATIC_CONST_MIN_WIDTH)
&& !constp->num().isFourState();
if (useStatic) {
// Extract as static constant
const auto& it = m_dupFinder.findDuplicate(constp);
if (it == m_dupFinder.end()) {
const string newvarname = string("__Vconst") + cvtToStr(m_modp->varNumGetInc());
varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname,
nodep->dtypep());
varp->isConst(true);
varp->isStatic(true);
varp->valuep(constp);
m_modp->addStmtp(varp);
m_dupFinder.insert(constp);
nodep->user2p(varp);
++m_staticConstantsExtracted;
} else {
varp = VN_CAST(it->second->user2p(), Var);
++m_staticConstantsReused;
}
} else {
// Keep as local temporary
const string newvarname = string("__Vtemp") + cvtToStr(m_modp->varNumGetInc());
varp
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
m_cfuncp->addInitsp(varp);
}
if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const
// Replace node tree with reference to var
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
linker.relink(newp);
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign(
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
insertBeforeStmt(assp);
if (debug() > 8) assp->dumpTree(cout, "deepou:");
if (!useStatic) {
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign(
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
insertBeforeStmt(assp);
if (debug() > 8) assp->dumpTree(cout, "deepou:");
}
nodep->user1(true); // Don't add another assignment
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
UINFO(4, " MOD " << nodep << endl);
VL_RESTORER(m_modp);
{
m_modp = nodep;
m_cfuncp = nullptr;
iterateChildren(nodep);
}
UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?");
UASSERT_OBJ(m_dupFinder.empty(), nodep, "Statements outside module ?");
m_modp = nodep;
m_cfuncp = nullptr;
iterateChildren(nodep);
m_modp = nullptr;
m_dupFinder.clear();
}
virtual void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
@ -401,7 +441,11 @@ private:
public:
// CONSTRUCTORS
explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~PremitVisitor() override = default;
virtual ~PremitVisitor() {
V3Stats::addStat("Optimizations, Prelim static constants extracted",
m_staticConstantsExtracted);
V3Stats::addStat("Optimizations, Prelim static constants reused", m_staticConstantsReused);
}
};
//----------------------------------------------------------------------

View File

@ -20,7 +20,7 @@
#include "V3Global.h"
#include "V3String.h"
#include "V3ProtectLib.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include "V3Task.h"
#include <list>
@ -87,9 +87,9 @@ private:
iterateChildren(nodep);
V3Hash hash = V3Hashed::uncachedHash(m_cfilep);
m_hashValuep->addText(fl, cvtToStr(hash.fullValue()) + ";\n");
m_cHashValuep->addText(fl, cvtToStr(hash.fullValue()) + "U;\n");
V3Hash hash = V3Hasher::uncachedHash(m_cfilep);
m_hashValuep->addText(fl, cvtToStr(hash.value()) + ";\n");
m_cHashValuep->addText(fl, cvtToStr(hash.value()) + "U;\n");
m_foundTop = true;
}

View File

@ -18,12 +18,12 @@
// Each CFunc:
// Look for a series of assignments that would look better in a loop:
//
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #))
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1))
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #+C))
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1+C))
// ->
// Create __Vilp local variable
// FOR(__Vilp = low; __Vilp <= high; ++__Vlip)
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp))
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp + C))
//
// Likewise vector assign to the same constant converted to a loop.
//
@ -39,8 +39,6 @@
#include <algorithm>
constexpr unsigned RELOOP_MIN_ITERS = 40; // Need at least this many loops to do this optimization
//######################################################################
class ReloopVisitor final : public AstNVisitor {
@ -61,6 +59,7 @@ private:
AstNodeSel* m_mgSelRp = nullptr; // Parent select, nullptr = constant
AstNodeVarRef* m_mgVarrefLp = nullptr; // Parent varref
AstNodeVarRef* m_mgVarrefRp = nullptr; // Parent varref, nullptr = constant
int64_t m_mgOffset = 0; // Index offset
AstConst* m_mgConstRp = nullptr; // Parent RHS constant, nullptr = sel
uint32_t m_mgIndexLo = 0; // Merge range
uint32_t m_mgIndexHi = 0; // Merge range
@ -83,38 +82,50 @@ private:
if (!m_mgAssignps.empty()) {
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
UINFO(9, "End merge iter=" << items << " " << m_mgIndexHi << ":" << m_mgIndexLo << " "
<< m_mgAssignps[0] << endl);
if (items >= RELOOP_MIN_ITERS) {
<< m_mgOffset << " " << m_mgAssignps[0] << endl);
if (items >= static_cast<uint32_t>(v3Global.opt.reloopLimit())) {
UINFO(6, "Reloop merging items=" << items << " " << m_mgIndexHi << ":"
<< m_mgIndexLo << " " << m_mgAssignps[0] << endl);
<< m_mgIndexLo << " " << m_mgOffset << " "
<< m_mgAssignps[0] << endl);
++m_statReloops;
m_statReItems += items;
// Transform first assign into for loop body
AstNodeAssign* bodyp = m_mgAssignps.front();
AstNodeAssign* const bodyp = m_mgAssignps.front();
UASSERT_OBJ(bodyp->lhsp() == m_mgSelLp, bodyp, "Corrupt queue/state");
FileLine* fl = bodyp->fileline();
AstVar* itp = findCreateVarTemp(fl, m_mgCfuncp);
FileLine* const fl = bodyp->fileline();
AstVar* const itp = findCreateVarTemp(fl, m_mgCfuncp);
AstNode* initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
new AstConst(fl, m_mgIndexLo));
AstNode* condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
new AstConst(fl, m_mgIndexHi));
AstNode* incp = new AstAssign(
if (m_mgOffset > 0) {
UASSERT_OBJ(m_mgIndexLo >= m_mgOffset, bodyp,
"Reloop iteration starts at negative index");
m_mgIndexLo -= m_mgOffset;
m_mgIndexHi -= m_mgOffset;
}
AstNode* const initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
new AstConst(fl, m_mgIndexLo));
AstNode* const condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
new AstConst(fl, m_mgIndexHi));
AstNode* const incp = new AstAssign(
fl, new AstVarRef(fl, itp, VAccess::WRITE),
new AstAdd(fl, new AstConst(fl, 1), new AstVarRef(fl, itp, VAccess::READ)));
AstWhile* whilep = new AstWhile(fl, condp, nullptr, incp);
AstWhile* const whilep = new AstWhile(fl, condp, nullptr, incp);
initp->addNext(whilep);
bodyp->replaceWith(initp);
whilep->addBodysp(bodyp);
// Replace constant index with new loop index
AstNode* lbitp = m_mgSelLp->bitp();
lbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
AstNode* const offsetp
= m_mgOffset == 0 ? nullptr : new AstConst(fl, std::abs(m_mgOffset));
AstNode* const lbitp = m_mgSelLp->bitp();
AstNode* const lvrefp = new AstVarRef(fl, itp, VAccess::READ);
lbitp->replaceWith(m_mgOffset > 0 ? new AstAdd(fl, lvrefp, offsetp) : lvrefp);
VL_DO_DANGLING(lbitp->deleteTree(), lbitp);
if (m_mgSelRp) { // else constant and no replace
AstNode* rbitp = m_mgSelRp->bitp();
rbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
AstNode* const rbitp = m_mgSelRp->bitp();
AstNode* const rvrefp = new AstVarRef(fl, itp, VAccess::READ);
rbitp->replaceWith(m_mgOffset < 0 ? new AstAdd(fl, rvrefp, offsetp) : rvrefp);
VL_DO_DANGLING(rbitp->deleteTree(), lbitp);
}
if (debug() >= 9) initp->dumpTree(cout, "-new: ");
@ -133,6 +144,7 @@ private:
m_mgSelRp = nullptr;
m_mgVarrefLp = nullptr;
m_mgVarrefRp = nullptr;
m_mgOffset = 0;
m_mgConstRp = nullptr;
}
}
@ -143,6 +155,7 @@ private:
{
m_cfuncp = nodep;
iterateChildren(nodep);
mergeEnd(); // Finish last pending merge, if any
}
}
virtual void visit(AstNodeAssign* nodep) override {
@ -155,7 +168,7 @@ private:
return;
}
// Of a constant index
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
AstConst* const lbitp = VN_CAST(lselp->bitp(), Const);
if (!lbitp) {
mergeEnd();
return;
@ -164,53 +177,58 @@ private:
mergeEnd();
return;
}
uint32_t index = lbitp->toUInt();
const uint32_t lindex = lbitp->toUInt();
// Of variable
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
AstNodeVarRef* const lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
if (!lvarrefp) {
mergeEnd();
return;
}
// RHS is a constant or a select
AstConst* rconstp = VN_CAST(nodep->rhsp(), Const);
AstNodeSel* rselp = VN_CAST(nodep->rhsp(), NodeSel);
AstConst* const rconstp = VN_CAST(nodep->rhsp(), Const);
AstNodeSel* const rselp = VN_CAST(nodep->rhsp(), NodeSel);
AstNodeVarRef* rvarrefp = nullptr;
uint32_t rindex = lindex;
if (rconstp) { // Ok
} else {
if (!rselp) {
mergeEnd();
return;
}
AstConst* rbitp = VN_CAST(rselp->bitp(), Const);
} else if (rselp) {
AstConst* const rbitp = VN_CAST(rselp->bitp(), Const);
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
if (!rbitp || rbitp->toUInt() != index || !rvarrefp
|| lvarrefp->varp() == rvarrefp->varp()) {
if (!rbitp || !rvarrefp || lvarrefp->varp() == rvarrefp->varp()) {
mergeEnd();
return;
}
rindex = rbitp->toUInt();
} else {
mergeEnd();
return;
}
if (m_mgSelLp) { // Old merge
if (m_mgCfuncp == m_cfuncp && m_mgNextp == nodep && m_mgSelLp->same(lselp)
&& m_mgVarrefLp->same(lvarrefp)
&& (m_mgConstRp
? (rconstp && m_mgConstRp->same(rconstp))
: (rselp && m_mgSelRp->same(rselp) && m_mgVarrefRp->same(rvarrefp)))
&& (index == m_mgIndexLo - 1 || index == m_mgIndexHi + 1)) {
if (m_mgCfuncp == m_cfuncp // In same function
&& m_mgNextp == nodep // Consecutive node
&& m_mgVarrefLp->same(lvarrefp) // Same array on left hand side
&& (m_mgConstRp // On the right hand side either ...
? (rconstp && m_mgConstRp->same(rconstp)) // ... same constant
: (rselp && m_mgVarrefRp->same(rvarrefp))) // ... or same array
&& (lindex == m_mgIndexLo - 1 || lindex == m_mgIndexHi + 1) // Left index +/- 1
&& (m_mgConstRp || lindex == rindex + m_mgOffset) // Same right index offset
) {
// Sequentially next to last assign; continue merge
if (index == m_mgIndexLo - 1) {
m_mgIndexLo = index;
} else if (index == m_mgIndexHi + 1) {
m_mgIndexHi = index;
if (lindex == m_mgIndexLo - 1) {
m_mgIndexLo = lindex;
} else if (lindex == m_mgIndexHi + 1) {
m_mgIndexHi = lindex;
}
UINFO(9, "Continue merge i=" << index << " " << m_mgIndexHi << ":" << m_mgIndexLo
UINFO(9, "Continue merge i=" << lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo
<< " " << nodep << endl);
m_mgAssignps.push_back(nodep);
m_mgNextp = nodep->nextp();
return;
} else {
// This assign doesn't merge with previous assign,
UINFO(9, "End merge i="
<< lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo << " " << nodep
<< endl); // This assign doesn't merge with previous assign,
// but should start a new merge
mergeEnd();
}
@ -224,10 +242,11 @@ private:
m_mgSelRp = rselp;
m_mgVarrefLp = lvarrefp;
m_mgVarrefRp = rvarrefp;
m_mgOffset = static_cast<int64_t>(lindex) - static_cast<int64_t>(rindex);
m_mgConstRp = rconstp;
m_mgIndexLo = index;
m_mgIndexHi = index;
UINFO(9, "Start merge i=" << index << " " << nodep << endl);
m_mgIndexLo = lindex;
m_mgIndexHi = lindex;
UINFO(9, "Start merge i=" << lindex << " o=" << m_mgOffset << nodep << endl);
}
//--------------------
virtual void visit(AstVar*) override {} // Accelerate

View File

@ -23,7 +23,7 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Hashed.h"
#include "V3Hasher.h"
#include <unordered_set>
@ -37,7 +37,7 @@ private:
// TYPES
struct HashSenTree {
size_t operator()(const AstSenTree* kp) const {
return V3Hashed::uncachedHash(kp).fullValue();
return V3Hasher::uncachedHash(kp).value();
}
};

View File

@ -979,6 +979,17 @@ private:
}
SimStackNode stackNode(nodep, &tconnects);
m_callStack.push_front(&stackNode);
// Clear output variable
if (auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) {
AstConst cnst(funcp->fvarp()->fileline(), AstConst::WidthedValue(), basicp->widthMin(),
0);
if (basicp->isZeroInit()) {
cnst.num().setAllBits0();
} else {
cnst.num().setAllBitsX();
}
newValue(funcp->fvarp(), &cnst);
}
// Evaluate the function
iterate(funcp);
m_callStack.pop_front();

View File

@ -342,7 +342,7 @@ private:
AstVarScope* findDuplicateTable(AstVarScope* vsc1p) {
// See if another table we've created is identical, if so use it for both.
// (A more 'modern' way would be to instead use V3Hashed::findDuplicate)
// (A more 'modern' way would be to instead use V3DupFinder::findDuplicate)
AstVar* var1p = vsc1p->varp();
for (AstVarScope* vsc2p : m_modTableVscs) {
AstVar* var2p = vsc2p->varp();

View File

@ -1157,7 +1157,8 @@ private:
cfuncp->argTypes(EmitCBaseVisitor::symClassVar());
if (cfuncp->name() == "new") {
cfuncp->addInitsp(
new AstCStmt(nodep->fileline(), "_ctor_var_reset(vlSymsp);\n"));
new AstCStmt(nodep->fileline(),
VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);\n"));
}
}
}

View File

@ -42,7 +42,7 @@
#include "V3Trace.h"
#include "V3EmitCBase.h"
#include "V3Graph.h"
#include "V3Hashed.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include <map>
@ -154,8 +154,8 @@ public:
class TraceVisitor final : public EmitCBaseVisitor {
private:
// NODE STATE
// V3Hashed
// Ast*::user4() // V3Hashed calculation
// V3Hasher in V3DupFinder
// Ast*::user4() // V3Hasher calculation
// Cleared entire netlist
// AstCFunc::user1() // V3GraphVertex* for this node
// AstTraceDecl::user1() // V3GraphVertex* for this node
@ -165,7 +165,7 @@ private:
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser3InUse m_inuser3;
// AstUser4InUse In V3Hashed
// AstUser4InUse In V3Hasher via V3DupFinder
// STATE
AstNodeModule* m_topModp = nullptr; // Module to add variables to
@ -194,7 +194,7 @@ private:
void detectDuplicates() {
UINFO(9, "Finding duplicates\n");
// Note uses user4
V3Hashed hashed; // Duplicate code detection
V3DupFinder dupFinder; // Duplicate code detection
// Hash all of the values the traceIncs need
for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp;
itp = itp->verticesNextp()) {
@ -205,13 +205,9 @@ private:
UASSERT_OBJ(nodep->valuep()->backp() == nodep, nodep,
"Trace duplicate back needs consistency,"
" so we can map duplicates back to TRACEINCs");
hashed.hash(nodep->valuep());
UINFO(8, " Hashed " << std::hex << hashed.nodeHash(nodep->valuep()) << " "
<< nodep << endl);
// Just keep one node in the map and point all duplicates to this node
if (hashed.findDuplicate(nodep->valuep()) == hashed.end()) {
hashed.hashAndInsert(nodep->valuep());
if (dupFinder.findDuplicate(nodep->valuep()) == dupFinder.end()) {
dupFinder.insert(nodep->valuep());
}
}
}
@ -221,10 +217,10 @@ private:
if (TraceTraceVertex* const vvertexp = dynamic_cast<TraceTraceVertex*>(itp)) {
AstTraceDecl* const nodep = vvertexp->nodep();
if (nodep->valuep() && !vvertexp->duplicatep()) {
const auto dupit = hashed.findDuplicate(nodep->valuep());
if (dupit != hashed.end()) {
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
if (dupit != dupFinder.end()) {
const AstTraceDecl* const dupDeclp
= VN_CAST_CONST(hashed.iteratorNodep(dupit)->backp(), TraceDecl);
= VN_CAST_CONST(dupit->second->backp(), TraceDecl);
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
TraceTraceVertex* const dupvertexp
= dynamic_cast<TraceTraceVertex*>(dupDeclp->user1u().toGraphVertex());
@ -237,7 +233,6 @@ private:
}
}
}
hashed.clear();
}
void graphSimplify(bool initial) {

View File

@ -127,6 +127,7 @@ private:
new AstAssign(fl, prep, new AstVarRef(fl, varp, VAccess::READ)))),
nullptr);
newp->branchPred(VBranchPred::BP_LIKELY);
newp->isBoundsCheck(true);
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
abovep->addNextStmt(newp, abovep);
prep->user2p(newp); // Save so we may LogAnd it next time

View File

@ -492,7 +492,7 @@ private:
// signed: Unsigned (11.8.1)
// width: LHS + RHS
AstNodeDType* vdtypep = m_vup->dtypeNullSkipRefp();
userIterateAndNext(vdtypep, WidthVP(SELF, BOTH).p());
userIterate(vdtypep, WidthVP(SELF, BOTH).p());
if (VN_IS(vdtypep, QueueDType)) {
// Queue "element 0" is lhsp, so we need to swap arguments
auto* newp = new AstConsQueue(nodep->fileline(), nodep->rhsp()->unlinkFrBack(),
@ -2457,7 +2457,7 @@ private:
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : nullptr;
UINFO(9, " from dt " << fromDtp << endl);
userIterateAndNext(fromDtp, WidthVP(SELF, BOTH).p());
userIterate(fromDtp, WidthVP(SELF, BOTH).p());
if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) {
methodCallEnum(nodep, adtypep);
} else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
@ -3310,7 +3310,9 @@ private:
while (const AstConstDType* vdtypep = VN_CAST(dtypep, ConstDType)) {
dtypep = vdtypep->subDTypep()->skipRefp();
}
userIterateAndNext(dtypep, WidthVP(SELF, BOTH).p());
userIterate(dtypep, WidthVP(SELF, BOTH).p());
if (auto* vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
@ -3864,27 +3866,6 @@ private:
if (nodep->timeunit().isNone()) {
nodep->v3fatalSrc("display %t has no time units");
}
double scale = nodep->timeunit().multiplier()
/ v3Global.rootp()->timeprecision().multiplier();
if (scale != 1.0) {
AstNode* newp;
AstNRelinker relinkHandle;
argp->unlinkFrBack(&relinkHandle);
if (argp->isDouble()) { // Convert it
ch = '^';
newp = new AstMulD(
argp->fileline(),
new AstConst(argp->fileline(), AstConst::RealDouble(), scale),
argp);
} else {
newp = new AstMul(argp->fileline(),
new AstConst(argp->fileline(),
AstConst::Unsized64(),
std::llround(scale)),
argp);
}
relinkHandle.relink(newp);
}
argp = nextp;
}
break;
@ -6125,6 +6106,7 @@ private:
}
void userIterateAndNext(AstNode* nodep, WidthVP* vup) {
if (!nodep) return;
if (nodep->didWidth()) return; // Avoid iterating list we have already iterated
{
VL_RESTORER(m_vup);
m_vup = vup;

View File

@ -27,6 +27,8 @@
#define V3ERROR_NO_GLOBAL_
#include "V3Error.cpp"
#include "V3String.cpp"
#define V3OPTION_PARSER_NO_VOPTION_BOOL
#include "V3OptionParser.cpp"
#include "V3Os.cpp"
#include "VlcTop.cpp"
@ -47,74 +49,43 @@ string VlcOptions::version() {
return ver;
}
bool VlcOptions::onoff(const char* sw, const char* arg, bool& flag) {
// if sw==arg, then return true (found it), and flag=true
// if sw=="-no-arg", then return true (found it), and flag=false
// if sw=="-noarg", then return true (found it), and flag=false
// else return false
if (arg[0] != '-') v3fatalSrc("OnOff switches must have leading dash.");
if (0 == strcmp(sw, arg)) {
flag = true;
return true;
} else if (0 == strncmp(sw, "-no", 3) && (0 == strcmp(sw + 3, arg + 1))) {
flag = false;
return true;
} else if (0 == strncmp(sw, "-no-", 4) && (0 == strcmp(sw + 4, arg + 1))) {
flag = false;
return true;
}
return false;
}
void VlcOptions::parseOptsList(int argc, char** argv) {
V3OptionParser parser;
V3OptionParser::AppendHelper DECL_OPTION{parser};
V3OPTION_PARSER_DECL_TAGS;
DECL_OPTION("-annotate-all", OnOff, &m_annotateAll);
DECL_OPTION("-rank", OnOff, &m_rank);
DECL_OPTION("-unlink", OnOff, &m_unlink);
DECL_OPTION("-annotate-min", Set, &m_annotateMin);
DECL_OPTION("-annotate", Set, &m_annotateOut);
DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); });
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
DECL_OPTION("-V", CbCall, []() {
showVersion(true);
std::exit(0);
});
DECL_OPTION("-version", CbCall, []() {
showVersion(false);
std::exit(0);
});
DECL_OPTION("-write", Set, &m_writeFile);
DECL_OPTION("-write-info", Set, &m_writeInfoFile);
parser.finalize();
// Parse parameters
// Note argc and argv DO NOT INCLUDE the filename in [0]!!!
// May be called recursively when there are -f files.
for (int i = 0; i < argc;) {
UINFO(9, " Option: " << argv[i] << endl);
if (argv[i][0] == '-') {
const char* sw = argv[i];
bool flag = true;
// Allow gnu -- switches
if (sw[0] == '-' && sw[1] == '-') ++sw;
// Single switches
if (onoff(sw, "-annotate-all", flag /*ref*/)) {
m_annotateAll = flag;
} else if (onoff(sw, "-rank", flag /*ref*/)) {
m_rank = flag;
} else if (onoff(sw, "-unlink", flag /*ref*/)) {
m_unlink = flag;
}
// Parameterized switches
else if (!strcmp(sw, "-annotate-min") && (i + 1) < argc) {
++i;
m_annotateMin = atoi(argv[i]);
} else if (!strcmp(sw, "-annotate") && (i + 1) < argc) {
++i;
m_annotateOut = argv[i];
} else if (!strcmp(sw, "-debug")) {
V3Error::debugDefault(3);
} else if (!strcmp(sw, "-debugi") && (i + 1) < argc) {
++i;
V3Error::debugDefault(atoi(argv[i]));
} else if (!strcmp(sw, "-V")) {
showVersion(true);
std::exit(0);
} else if (!strcmp(sw, "-version")) {
showVersion(false);
std::exit(0);
} else if (!strcmp(sw, "-write") && (i + 1) < argc) {
++i;
m_writeFile = argv[i];
} else if (!strcmp(sw, "-write-info") && (i + 1) < argc) {
++i;
m_writeInfoFile = argv[i];
if (int consumed = parser.parse(i, argc, argv)) {
i += consumed;
} else {
v3fatal("Invalid option: " << argv[i]);
v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
++i;
}
++i;
} // - options
else {
} else {
addReadFile(argv[i]);
++i;
}

View File

@ -47,7 +47,6 @@ class VlcOptions final {
private:
// METHODS
static void showVersion(bool verbose);
static bool onoff(const char* sw, const char* arg, bool& flag);
public:
// CONSTRUCTORS

View File

@ -621,7 +621,7 @@ def write_types(filename):
fh.write(" }\n")
def write_header(filename):
def write_macros(filename):
with open_file(filename) as fh:
typen = "None"
base = "None"
@ -629,10 +629,7 @@ def write_header(filename):
in_filename = "V3AstNodes.h"
ifile = Args.I + "/" + in_filename
with open(ifile) as ifh:
fh.write("#line 1 \"../" + in_filename + "\"\n")
for line in ifh:
for (lineno, line) in enumerate(ifh, 1):
# Drop expanded macro definitions - but keep empty line so compiler
# message locations are accurate
line = re.sub(r'^\s*#(define|undef)\s+ASTGEN_.*$', '', line)
@ -644,13 +641,20 @@ def write_header(filename):
if match:
typen = match.group(1)
base = match.group(4)
if not typen.startswith("Node"):
macro = "#define ASTGEN_SUPER_{t}(...) {b}(AstType::at{t}, __VA_ARGS__)\n" \
.format(b=base, t=typen)
fh.write(macro)
# Substitute macros
line = re.sub(r'\bASTGEN_SUPER\s*\(',
base + "(AstType::at" + typen + ", ", line)
# Emit the line
fh.write(line)
match = re.search(r"ASTGEN_SUPER_(\w+)", line)
if match:
if typen != match.group(1):
print((
"V3AstNodes.h:{l} ERROR: class Ast{t} calls wrong superclass "
+
"constructor macro (should call ASTGEN_SUPER_{t})"
).format(l=lineno, t=typen))
sys.exit(1)
######################################################################
@ -708,7 +712,7 @@ if Args.classes:
write_visitor("V3Ast__gen_visitor.h")
write_impl("V3Ast__gen_impl.h")
write_types("V3Ast__gen_types.h")
write_header("V3AstNodes__gen.h")
write_macros("V3AstNodes__gen_macros.h")
for cpt in Args.infiles:
if not re.search(r'.cpp$', cpt):

View File

@ -52,9 +52,10 @@ string(REGEX REPLACE "(^|;)--" "\\1-"
getarg(TEST_VERILATOR_ARGS_NORM "-prefix" TEST_PREFIX)
getarg(TEST_VERILATOR_ARGS_NORM "-threads" TEST_THREADS)
getarg(TEST_VERILATOR_ARGS_NORM "-trace-threads" TEST_TRACE_THREADS)
# Strip unwanted args with 1 parameter
string(REGEX REPLACE "(^|;)--?(Mdir|make|prefix|threads);[^;]*" ""
string(REGEX REPLACE "(^|;)--?(Mdir|make|prefix|threads|trace-threads);[^;]*" ""
TEST_VERILATOR_ARGS
"${TEST_VERILATOR_ARGS}")
# Strip unwanted args
@ -81,6 +82,9 @@ endif()
if(TEST_THREADS)
list(APPEND verilate_ARGS THREADS ${TEST_THREADS})
endif()
if(TEST_TRACE_THREADS)
list(APPEND verilate_ARGS TRACE_THREADS ${TEST_TRACE_THREADS})
endif()
if(TEST_SYSTEMC)
list(APPEND verilate_ARGS SYSTEMC)
endif()

View File

@ -908,7 +908,7 @@ sub compile_vlt_flags {
unshift @verilator_flags, "--trace-threads 2" if $param{vltmt} && $checkflags =~ /-trace-fst /;
unshift @verilator_flags, "--debug-partition" if $param{vltmt};
unshift @verilator_flags, "-CFLAGS -ggdb -LDFLAGS -ggdb" if $opt_gdbsim;
unshift @verilator_flags, "-CFLAGS -fsanitize=address -LDFLAGS -fsanitize=address" if $param{sanitize};
unshift @verilator_flags, "-CFLAGS -fsanitize=address,undefined -LDFLAGS -fsanitize=address,undefined" if $param{sanitize};
unshift @verilator_flags, "--make gmake" if $param{verilator_make_gmake};
unshift @verilator_flags, "--make cmake" if $param{verilator_make_cmake};
unshift @verilator_flags, "--exe" if
@ -2304,6 +2304,10 @@ sub cfg_with_threaded {
return 1; # C++11 now always required
}
sub cfg_with_ccache {
return `grep "OBJCACHE \?= ccache" "$ENV{VERILATOR_ROOT}/include/verilated.mk"` ne "";
}
sub tries {
# Number of retries when reading logfiles, generally only need many
# retries when system is busy running a lot of tests
@ -2449,10 +2453,12 @@ sub _lineno_match {
my $lineno = shift;
my $lines = shift;
return 1 if !defined $lines;
if ($lines =~ /^(\d+)$/) {
return $1 == $lineno;
} elsif ($lines =~ /^(\d+)-(\d+)$/) {
return $1 <= $lineno && $2 >= $lineno;
foreach my $lc (split /,/, $lines) {
if ($lc =~ /^(\d+)$/) {
return 1 if $1 == $lineno;
} elsif ($lc =~ /^(\d+)-(\d+)$/) {
return 1 if $1 <= $lineno && $2 >= $lineno;
}
}
return 0;
}

View File

@ -54,6 +54,15 @@ static const bool verbose = false;
} \
} while (0)
#define TEST_CHECK_Z(got) \
do { \
if ((got)) { \
std::cout << std::dec << "%Error: " << __FILE__ << ":" << __LINE__ << std::hex \
<< ": GOT!= NULL EXP=NULL" << std::endl; \
++errors; \
} \
} while (0)
#define TEST_CHECK_NZ(got) \
do { \
if (!(got)) { \

21
test_regress/t/t_assoc2.pl Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

36
test_regress/t/t_assoc2.v Normal file
View File

@ -0,0 +1,36 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
// associative array of an associative array
logic [31:0] a [logic [31:0]][logic [63:0]];
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) begin
a[5][8] = 8;
a[5][9] = 9;
end
else if (cyc == 2) begin
`checkh(a[5][8], 8);
`checkh(a[5][9], 9);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,48 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt => 1);
if (!$Self->cfg_with_ccache) {
skip("Requires configuring with ccache");
}
top_filename("t_a1_first_cc.v");
# This test requires rebuilding the object files to check the ccache log
foreach my $filename (glob ("$Self->{obj_dir}/*.o")) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}
compile(
verilator_flags2 => ['--trace'],
make_flags => "ccache-report"
);
my $report = "$Self->{obj_dir}/$Self->{VM_PREFIX}__ccache_report.txt";
# We do not actually want to make this test depend on whether the file was
# cached or not, so trim the report to ignore actual caching behaviour
run(cmd => ["sed", "-i", "-e", "'s/ : .*/ : IGNORED/; /|/s/.*/IGNORED/;'", $report]);
files_identical($report, "t/$Self->{name}__ccache_report_initial.out");
# Now rebuild again (should be all up to date)
run(
logfile => "$Self->{obj_dir}/rebuild.log",
cmd => ["make", "-C", $Self->{obj_dir},
"-f", "$Self->{VM_PREFIX}.mk",
$Self->{VM_PREFIX}, "ccache-report"]
);
files_identical($report, "t/$Self->{name}__ccache_report_rebuild.out");
ok(1);
1;

View File

@ -0,0 +1,12 @@
################################################################################
ccache report (from verilator_ccache_report) :
Compiled object files:
Vt_ccache_report__ALL.o : IGNORED
Summary:
IGNORED
Longest:
IGNORED
################################################################################

View File

@ -0,0 +1,5 @@
################################################################################
ccache report (from verilator_ccache_report) :
All object files up to date
################################################################################

View File

@ -0,0 +1,34 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}
top_filename("t/t_class_extends.v");
compile(
make_flags => 'VM_PARALLEL_BUILDS=1', # bug2775
verilator_flags2 => ["--protect-ids",
"--protect-key SECRET_KEY"]
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,33 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}
top_filename("t/t_class_local.v");
compile(
verilator_flags2 => ["--protect-ids",
"--protect-key SECRET_KEY"]
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,33 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}
top_filename("t/t_class_static_method.v");
compile(
verilator_flags2 => ["--protect-ids",
"--protect-key SECRET_KEY"]
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,33 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt_all => 1);
# This test makes randomly named .cpp/.h files, which tend to collect, so remove them first
foreach my $filename (glob ("$Self->{obj_dir}/*_PS*.cpp"
." $Self->{obj_dir}/*_PS*.h"
." $Self->{obj_dir}/*.d" )) {
print "rm $filename\n" if $Self->{verbose};
unlink $filename;
}
top_filename("t/t_class_virtual.v");
compile(
verilator_flags2 => ["--protect-ids",
"--protect-key SECRET_KEY"]
);
execute(
check_finished => 1,
);
ok(1);
1;

Some files were not shown because too many files have changed in this diff Show More