MAJOR: Merge develop-v4 into master

This commit is contained in:
Wilson Snyder 2018-08-25 08:01:15 -04:00
commit ec538c02d8
196 changed files with 15561 additions and 4920 deletions

21
Changes
View File

@ -2,7 +2,26 @@ Revision history for Verilator
The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 3.927 devel
* Verilator 4.000 devel
** This is a major release. Any patches may require major rework to apply.
[Thanks everyone]
** Add multithreaded model generation.
** Add runtime arguments.
*** Better optimize large always block splitting, bug1244. [John Coiner]
*** Add new reloop optimization for repetitive assignment compression.
**** Fix internals to be C++ null-pointer-check clean.
**** Fix internals to avoid 'using namespace std'.
**** Fix Verilation performance issues, bug1316. [John Coiner]
**** Fix clocker attributes to not propagate on concats. [John Coiner]
* Verilator 3.926 2018-08-22

View File

@ -120,6 +120,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
bin/verilator \
bin/verilator_coverage \
bin/verilator_difftree \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
doxygen-mainpage doxygen.config veripool-logo.png \
@ -154,6 +155,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_coverage \
bin/verilator_gantt \
bin/verilator_includer \
bin/verilator_profcfunc \
include/verilated.mk \
@ -183,8 +185,8 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
.PHONY:verilator_coverage_bin_dbg
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
@echo ------------------------------------------------------------
@echo "making verilator in src" ; \
(cd src && $(MAKE) $(OBJCACHE_JOBS) )
@echo "making verilator in src"
$(MAKE) -C src $(OBJCACHE_JOBS)
.PHONY:msg_test
msg_test: all_nomsg
@ -210,7 +212,7 @@ smoke-test: all_nomsg
test_regress/t/t_a_first_sc.pl
test_regress: all_nomsg
@(cd test_regress && $(MAKE))
$(MAKE) -C test_regress
examples: all_nomsg
for p in examples/* ; do \
@ -272,12 +274,12 @@ internals.pdf: internals.pod Makefile
# 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_includer verilator_profcfunc
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.
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_profcfunc.1
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
VL_INST_INC_BLDDIR_FILES = \
include/verilated_config.h \
@ -295,10 +297,11 @@ installbin:
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
( 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 ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
( 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 )
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/bin
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
@ -477,7 +480,7 @@ maintainer-clean::
clean mostlyclean distclean maintainer-clean maintainer-copy::
for dir in $(SUBDIRS); do \
echo making $@ in $$dir ; \
(cd $$dir && $(MAKE) $@) ; \
$(MAKE) -C $$dir $@ ; \
done
clean mostlyclean distclean maintainer-clean::

132
TODO
View File

@ -5,119 +5,115 @@
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
Language support:
* Fix ordering of each bit separately in a signal (mips)
* Language support:
** Fix ordering of each bit separately in a signal (mips)
assign b[3:0] = b[7:4]; assign b[7:4] = in;
* Support UDP gate primitives/ cell libraries
** Support UDP gate primitives/ cell libraries
(have code for combos - problem is sequential udps)
* Function to eval combo logic after /*verilator public*/ functions [gwaters]
* Support generated clocks (correctness)
* Real numbers
* Recursive functions
* Verilog configuration files
* Structs/unions (have starting point)
* DPI to define C/C++ calls from Verilog
* Expression coverage (see notes)
* Better tristate support
** Function to eval combo logic after /*verilator public*/ functions [gwaters]
** Support generated clocks (correctness)
** Recursive functions
** Verilog configuration files
** Expression coverage (see notes)
** Better tristate support
** UVM
Long-term Features
* Assertions
* Tristate support
* Multithreaded execution
* Long-term Features
** Assertions
** Tristate support
Configure/Make/Install
* Distribute with flex/bison already expanded?
* Configure/Make/Install
** Distribute with flex/bison already expanded?
Flex library not needed. Probably too difficult to be worth it.
Testing:
* Capture all inputs into global "rerun it" file
* Code to make wrapper that sets signals, so can do comparison checks
* New random program generator
* Better graph viewer with search and zoom
* Port and test against opencores.org code
* // verilator debug in code so can see only tree affecting those nodes
* Testing:
** Capture all inputs into global "rerun it" file
** Code to make wrapper that sets signals, so can do comparison checks
** New random program generator
** Better graph viewer with search and zoom
** Port and test against opencores.org code
** // verilator debug in code so can see only tree affecting those nodes
Usability:
* Detect and pre-remove most UNOPTFLATs (4.000)
* Better reporting of unopt problems, including what lines of code
* Report more errors (all of them?) before exiting [Eugene Weber]
* Auto-create scons config files
* Print version/etc message at runtime. (4.000?)
* Usability:
** Detect and pre-remove most UNOPTFLATs
** Better reporting of unopt problems, including what lines of code
** Report more errors (all of them?) before exiting [Eugene Weber]
** Auto-create scons config files
** Print version/etc message at runtime. (4.000?)
Include number of lines of code, percent comments, code complexity measurement
<-80chars------------------------------------------------------------------->
Verilator 3.600 - The fast free open-sourced simulator. Copyright 2001-2013.
Verilated #### modules, #### instances, ##### sigs,
#### non-comment lines, ##### ops, ### KB model size
Lint:
* CDCRSTLOGIC should allow filtering with paths
* Lint:
** CDCRSTLOGIC should allow filtering with paths
"waive CDCRSTLOGIC --from a.b.sig --to a.c.sig --via OR"
Internal Code:
* A Visitor class that understands how to traverse data types
* V3Graph should be templated container type, taking in Vertex + Edge types
* Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
things, to reduce bugs. Also add _20 trailing space to \ encoded names. (4.000)
* Internal Code:
** A Visitor class that understands how to traverse data types
** V3Graph should be templated container type, taking in Vertex + Edge types
** Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
things, to reduce bugs. Also add _20 trailing space to \ encoded names.
Runtime:
* New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
* Remove all private internal functions from top level wrapper header, move
to new level (4.000?)
* Completely standalone simulation (4.000)
* Runtime:
** New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
** Remove all private internal functions from top level wrapper header, move
to new level
** Completely standalone simulation
main() records arguments for $test$plusvars
instantiates top,
does tracing (support $dump?)
calls top->simulateForever()
exits
Performance:
* Latch optimizations
* Constant propagation
* Performance:
** Latch optimizations
** Constant propagation
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
* Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
** Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
Could propagate the AND into pos/negedges and let domaining optimize.
* Negedge reset
** Negedge reset
Switch to remove negedges that don't matter
Can't remove async resets from control flops (like in syncronizers)
* If all references to array have a constant index, blow up into separate signals-per-index
* Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
* Move _last sets and all other combo logic inside master
** If all references to array have a constant index, blow up into separate signals-per-index
** Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
** Move _last sets and all other combo logic inside master
if() that triggers on all possible sense items
* Rewrite and combine V3Life, V3Subst
** Rewrite and combine V3Life, V3Subst
If block temp only ever set in one place to constant, propagate it
Used in t_mem for array delayed assignments
Replace variables if set later in same cfunc branch
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
* Same assignment on both if branches
** Same assignment on both if branches
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
Careful though, as b could appear in the statement or multiple times in statement
(Could just require exatly two 'b's in statement)
* Simplify XOR/XNOR/AND/OR bit selection trees
** Simplify XOR/XNOR/AND/OR bit selection trees
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
* Combine variables into wider elements
** Combine variables into wider elements
Parallel statements on different bits should become single signal
Variables that are always consumed in "parallel" can be joined
* Duplicate assignments in gate optimization
** Duplicate assignments in gate optimization
Common to have many separate posedge blocks, each with identical
reset_r <= rst_in
* If signal is used only once (not counting trace), always gate substitute
** If signal is used only once (not counting trace), always gate substitute
Don't merge if any combining would form circ logic (out goes back to in)
* Multiple assignments each bit can become single assign with concat
** Multiple assignments each bit can become single assign with concat
Make sure a SEL of a CONCAT can get the single bit back.
* Usually blocks/values
** Usually blocks/values
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
* Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
* I-cache packing improvements (what/how?)
* Data cache organization (order of vars in class)
** Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
** I-cache packing improvements (what/how?)
** Data cache organization (order of vars in class)
First have clocks,
then bools instead of uint32_t's
then based on what sense list they come from, all outputs, then all inputs
finally have any signals part of a "usually" block, or constant.
* Rather then tracking widths, have a MSB...LSB of this expression
** Rather then tracking widths, have a MSB...LSB of this expression
(or better, a bitmask of bits relevant in this expression)
* Track recirculation and convert into clock-enables
* Clock enables should become new clocking domains for speed
* If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
* Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
** Track recirculation and convert into clock-enables
** Clock enables should become new clocking domains for speed
** If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
** Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)

View File

@ -338,6 +338,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--pipe-filter <command> Filter all input through a script
--prefix <topname> Name of top level class
--prof-cfuncs Name functions for profiling
--prof-threads Enable generating gantt chart data for threads
--private Debugging; see docs
--public Debugging; see docs
-pvalue+<name>=<value> Overwrite toplevel parameter
@ -350,6 +351,9 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--stats-vars Provide statistics on variables
-sv Enable SystemVerilog parsing
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
--threads <threads> Enable multithreading
--threads-dpi <mode> Enable multithreaded DPI
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
--top-module <topname> Name of top level input module
--trace Enable waveform creation
--trace-depth <levels> Depth of tracing
@ -380,6 +384,19 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
--x-initial-edge Enable initial X->0 and X->1 edge triggers
-y <dir> Directory to search for modules
This is a short summary of the arguments to run-time Verilated arguments.
detailed descriptions in L</"RUNTIME ARGUMENTS"> for more information.
+verilator+debug Enable debugging
+verilator+debugi+<value> Enable debugging at a level
+verilator+help Display help
+verilator+prof+threads+file+I<filename> Set profile filename
+verilator+prof+threads+start+I<value> Set profile starting point
+verilator+prof+threads+window+I<value> Set profile duration
+verilator+rand+reset+<value> Set random reset technique
+verilator+V Verbose version and config
+verilator+version Show version and exit
=head1 VERILATION ARGUMENTS
@ -1070,6 +1087,18 @@ Verilog module and line number the statement came from. This allows gprof
or oprofile reports to be correlated with the original Verilog source
statements. See also L<verilator_profcfunc>.
=item --prof-threads
Enable gantt chart data collection for threaded builds.
Verilator will record the start and end time of each macro-task across a
number of calls to eval. (What is a macro-task? See the Verilator internals
document.)
When profiling is enabled, the runtime will emit a blurb of profiling data
in non-human-friendly form. The C<verilator_gantt> script will transform
this into a nicer visual format and produce some related statistics.
=item --private
Opposite of --public. Is the default; this option exists for backwards
@ -1124,7 +1153,10 @@ Enable including save and restore functions in the generated model.
The user code must create a VerilatedSerialize or VerilatedDeserialze
object then calling the << or >> operators on the generated model and any
other data the process needs saved/restored. For example:
other data the process needs saved/restored. These functions are not
thread safe, and are typically called only by a main thread.
For example:
void save_model(const char* filenamep) {
VerilatedSave os;
@ -1163,6 +1195,42 @@ compatibility with other simulators.
A synonym for C<+1800-2017ext+>I<ext>.
=item --threads I<threads>
=item --no-threads
With --threads 0 or --no-threads, the default, the generated model is not
thread safe. With --threads 1, the generated model is single threaded but
may run in a multithreaded environment. With --threads N, where N >= 2, the
model is generated to run multithreaded on up to N threads. See
L</"MULTITHREADING">.
=item --threads-dpi all
=item --threads-dpi none
=item --threads-dpi pure
When using --dpi with --threads, control what DPI tasks are thread safe.
With --threads-dpi all, enable Verilator to assume all DPI imports are
threadsafe, and to use thread-local storage for communication with DPI,
potentially improving performance. Any DPI libraries need appropriate
mutexes to avoid undefined behavior.
With --threads-dpi none, Verilator assume DPI imports are not thread safe,
and Verilator will serialize calls to DPI imports by default, potentially
harming performance.
With --threads-dpi pure, the default, Verilator assumes DPI pure imports
are threadsafe, but non-pure DPI imports are not.
=item --threads-max-mtasks I<value>
Rarely needed. When using --threads, specify the number of mtasks the
model is to be partitioned into. If unspecified, Verilator approximates a
good value.
=item --top-module I<topname>
When the input Verilog contains more than one top level module, specifies
@ -1432,6 +1500,67 @@ are desired for error messages instead of relative filenames.
=back
=head1 RUNTIME ARGUMENTS
The following are the arguments that may be passed to a Verilated
executable, provided that executable calls Verilated::commandArgs().
All runtime arguments begin with +verilator, so that the user's executable
may skip over all +verilator arguments when parsing its command line.
=over 4
=item +verilator+debug
Enable debugging. Equivalent to +verilator+debugi+4.
=item +verilator+debugi+I<value>
Enable debugging at the provided level.
=item +verilator+help
Display help and exit.
=item +verilator+prof+threads+file+I<filename>
When using --prof-threads, the filename to dump to. Defaults to
"profile_threads.dat".
=item +verilator+prof+threads+start+I<value>
When using --prof-threads, Verilator will wait until $time is at this
value, then start the profiling warmup, then capturing. Generally this
should be set to some time that is well within the normal operation of the
simulation, i.e. outside of reset. If 0, the dump is disabled. Defaults to
1.
=item +verilator+prof+threads+window+I<value>
When using --prof-threads, after $time reaches
+verilator+prof+threads+start, Verilator will warm up the profiling for
this number of eval() calls, then will capture the profiling of this number
of eval() calls. Defaults to 2, which makes sense for a
single-clock-domain module where it's typical to want to capture one
posedge eval() and one negedge eval().
=item +verilator+rand+reset+I<value>
When a model was Verilated using "-x-inital unique", sets the
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
Randomize. See L</"Unknown states">.
=item +verilator+V
Shows the verbose version, including configuration information.
=item +verilator+version
Displays program version and exits.
=back
=head1 EXAMPLE C++ EXECUTION
We'll compile this example into C++.
@ -1586,6 +1715,9 @@ compile times, and --x-assign=fast --x-initial=fast may increase the risk
of reset bugs in trade for performance; see the above documentation for
these flags.
If using Verilated multithreaded, use C<numactl> to ensure you are using
non-conflicting hardware resources. See L</"MULTITHREADING">.
Minor Verilog code changes can also give big wins. You should not have any
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
huge improvements; one user fixed their one UNOPTFLAT warning by making a
@ -2127,6 +2259,91 @@ the names of the .cpp files to compile in from the make variables generated
in obj_dir/Vour_classes.mk.
=head1 MULTITHREADING
Verilator experimentally supports multithreading.
With --no-threads, the default, the model is not thread safe, and any use
of more than one thread calling into one or even different Verilated models
may result in unpredictable behavior. This gives the highest single thread
performance.
With --threads 1, the generated model is single threaded, however the
support libraries are multithread safe. This allows different
instantiations of model(s) to potentially each be run under a different
thread. All threading is the responsibility of the user's C++ testbench.
With --threads N, where N is at least 2, the generated model will be
designed to run in parallel on N threads. The thread calling eval()
provides one of those threads, and the generated model will create and
manage the other N-1 threads. It's the client's responsibility not to
oversubscribe the available CPU cores. Under CPU oversubscription, the
Verilated model should not livelock nor deadlock, however, you can expect
performance to be far worse than it would be with proper stoichiometry of
threads and CPU cores.
The remainder of this section describe behavior with --threads 1 or
--threads N (not --no-threads).
VL_THREADED is defined when compiling a threaded Verilated module, causing
the Verilated support classes become threadsafe.
The thread used for constructing a model must the the same thread that
calls eval() into the model, this is called the "eval thread". The thread
used to perform certain global operations such as saving and tracing must
be done by a "main thread". In most cases the eval thread and main thread
are the same thread (i.e. the user's top C++ testbench runs on a single
thread), but this is not required.
When running a multithreaded model, the default Linux task scheduler often
works against the model, by assuming threads are short lived, and thus
often schedules threads using multiple hyperthreads within the same
physical core. For best performance use the C<numactl> program to (when the
threading count fits) select unique physical cores on the same socket. For
example, if a model was Verilated with "--threads 4", we consult
egrep 'processor|physical id|core id' /proc/cpuinfo
To select cores 0, 1, 2, and 3 that are all located on the same socket (0)
but different physical cores. (Also useful is "numactl --hardware", or
C<lscpu> but those doesn't show Hyperthreading cores.) Then we execute
numactl -m 0 -C 0,1,2,3 -- verilated_executable_name
This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
(presumably on socket 0) optimizing performance. Of course this must be
adjusted if you want another simulator using e.g. socket 1, or if you
Verilated with a different number of threads. To see what CPUs are
actually used, use --prof-threads.
=head2 Multithreaded Verilog and Library Support
$display/$stop/$finish are delayed until the end of an eval() call in order
to maintain ordering between threads. This may result in additional tasks
completing after the $stop or $finish.
=over 4
If using --coverage, the coverage routines are fully thread safe.
If using --dpi, Verilator assumes pure DPI imports are thread safe,
balancing performance versus saftey. See --threads-dpi.
If using --savable, the save/restore classes are not multithreaded and are
must be called only by the eval thread.
If using --sc, the SystemC kernel is not thread safe, therefore the eval
thread and main thread must be the same.
If using --trace, the tracing classes must be constructed and called from
the main thread.
If using --vpi, since SystemVerilog VPI was not architected by IEEE to be
multithreaded, Verilator requires all VPI calls are only made from the main
thread.
=back
=head1 CONFIGURATION FILES
In addition to the command line, warnings and other features may be
@ -3591,6 +3808,21 @@ section for more details.
Ignoring this warning will only slow simulations, it will simulate
correctly.
=item UNOPTTHREADS
Warns that the thread scheduler was unable to partition the design to fill
the requested number of threads.
One workaround is to request fewer threads with C<--threads>.
Another possible workaround is to allow more MTasks in the runtime, by
increasing the value of --threads-max-mtasks. More MTasks will result in
more communication and synchronization overhead at runtime; the scheduler
attempts to minimize the number of MTasks for this reason.
Ignoring this warning will only slow simulations, it will simulate
correctly.
=item UNPACKED
Warns that unpacked structs and unions are not supported.
@ -4140,6 +4372,8 @@ performance gain.
In 2009, major SystemVerilog and DPI language support was added.
In 2018, Verilator 4.000 was released with multithreaded support.
Currently, various language features and performance enhancements are added
as the need arises. Verilator is now about 3x faster than in 2002, and is
faster than many popular commercial simulators.
@ -4240,7 +4474,7 @@ License Version 2.0.
=head1 SEE ALSO
L<verilator_coverage>, L<verilator_profcfunc>, L<make>,
L<verilator_coverage>, L<verilator_gantt>, L<verilator_profcfunc>, L<make>,
L<verilator --help> which is the source for this document,

560
bin/verilator_gantt Executable file
View File

@ -0,0 +1,560 @@
: # -*-Mode: perl;-*- use perl, wherever it is
eval 'exec perl -wS $0 ${1+"$@"}'
if 0;
# See copyright, etc in below POD section.
######################################################################
use strict;
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use vars qw ($Debug);
$Debug = 0;
my $Opt_File;
my $Opt_Time_Per_Char = 0; # rdtsc ticks per char in gantt chart, 0=auto
my $opt_vcd = "profile_threads.vcd";
our %Threads;
our %Mtasks;
our %Global;
autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
"help" => \&usage,
"scale=i" => \$Opt_Time_Per_Char,
"debug" => sub { $Debug = 1; },
"vcd=s" => \$opt_vcd,
"no-vcd!" => sub { $opt_vcd = undef; },
"<>" => \&parameter,
)) {
die "%Error: Bad usage, try 'verilator_gantt --help'\n";
}
$Opt_File = "profile_threads.dat" if !defined $Opt_File;
process($Opt_File);
write_vcd($opt_vcd) if defined $opt_vcd;
exit(0);
#######################################################################
sub usage {
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
exit (1);
}
sub parameter {
my $param = shift;
if (!defined $Opt_File) {
$Opt_File = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub process {
my $filename = shift;
read_data($filename);
report();
}
#######################################################################
sub read_data {
my $filename = shift;
%Global = (rdtsc_cycle_time => 0);
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
while (my $line = $fh->getline) {
if ($line =~ m/VLPROF mtask\s(\d+)\sstart\s(\d+)\send\s(\d+)\selapsed\s(\d+)\spredict_time\s(\d+)\scpu\s(\d+)\son thread (\d+)/) {
my $mtask = $1;
my $start = $2;
my $end = $3;
my $elapsed_time = $4;
my $predict_time = $5;
my $cpu = $6;
my $thread = $7;
$Threads{$thread}{$start}{mtask} = $mtask;
$Threads{$thread}{$start}{end} = $end;
$Threads{$thread}{$start}{cpu} = $cpu;
if (!exists $Mtasks{$mtask}{elapsed}) {
$Mtasks{$mtask}{elapsed} = 0;
}
$Mtasks{$mtask}{elapsed} += $elapsed_time;
$Mtasks{$mtask}{predict} = $predict_time;
$Mtasks{$mtask}{end} = max($Mtasks{$mtask}{end}, $end);
}
elsif ($line =~ /^VLPROFTHREAD/) {}
elsif ($line =~ m/VLPROF arg\s+(\S+)\+([0-9.])\s*$/
|| $line =~ m/VLPROF arg\s+(\S+)\s+([0-9.])\s*$/) {
$Global{args}{$1} = $2;
}
elsif ($line =~ m/VLPROF stat\s+(\S+)\s+([0-9.]+)/) {
$Global{stats}{$1} = $2;
}
elsif ($line =~ /^#/) {}
elsif ($Debug) {
chomp $line;
print "Unk: $line\n";
}
# TODO -- this is parsing text printed by a client.
# Really, verilator proper should generate this
# if it's useful...
if ($line =~ m/rdtsc time = (\d+) ticks/) {
$Global{rdtsc_cycle_time} = $1;
}
}
}
sub report {
print "Verilator Gantt report\n";
print "\nArgument settings:\n";
foreach my $arg (sort keys %{$Global{args}}) {
my $plus = ($arg =~ /^\+/) ? "+" : " ";
printf " %s%s%d\n", $arg, $plus, $Global{args}{$arg};
}
my $nthreads = scalar keys %Threads;
$Global{cpus}{cpu_time} = {};
foreach my $thread (keys %Threads) {
# Make potentially multiple characters per column
foreach my $start (keys %{$Threads{$thread}}) {
my $cpu = $Threads{$thread}{$start}{cpu};
my $elapsed = $Threads{$thread}{$start}{end} - $start;
$Global{cpus}{cpu_time}{$cpu} += $elapsed;
}
}
my $mt_mtask_time = 0;
my $long_mtask_time = 0;
my $last_end = 0;
foreach my $mtask (keys %Mtasks) {
$mt_mtask_time += $Mtasks{$mtask}{elapsed};
$last_end = max($last_end, $Mtasks{$mtask}{end});
$long_mtask_time = max($long_mtask_time, $Mtasks{$mtask}{elapsed});
}
$Global{last_end} = $last_end;
report_graph();
# If we know cycle time in the same (rdtsc) units,
# this will give us an actual utilization number,
# (how effectively we keep the cores busy.)
#
# It also gives us a number we can compare against
# serial mode, to estimate the overhead of data sharing,
# which will show up in the total elapsed time. (Overhead
# of synchronization and scheduling should not.)
print "\nAnalysis:\n";
printf " Total threads = %d\n", $nthreads;
printf " Total mtasks = %d\n", scalar(keys %Mtasks);
printf " Total cpus used = %d\n", scalar(keys %{$Global{cpus}});
printf " Total yields = %d\n", $Global{stats}{yields};
printf " Total eval time = %d rdtsc ticks\n", $Global{last_end};
printf " Longest mtask time = %d rdtsc ticks\n", $long_mtask_time;
printf " All-thread mtask time = %d rdtsc ticks\n", $mt_mtask_time;
my $long_efficiency = $long_mtask_time/($Global{last_end});
printf " Longest-thread efficiency = %0.1f%%\n", $long_efficiency*100;
my $mt_efficiency = $mt_mtask_time/($Global{last_end}*$nthreads);
printf " All-thread efficiency = %0.1f%%\n", $mt_efficiency*100;
printf " All-thread speedup = %0.1f\n", $mt_efficiency*$nthreads;
if ($Global{rdtsc_cycle_time} > 0) {
my $ut = $mt_mtask_time / $Global{rdtsc_cycle_time};
print "tot_mtask_cpu=$mt_mtask_time cyc=$Global{rdtsc_cycle_time} ut=$ut\n";
}
my @p2e_ratios;
my $min_p2e = 1000000;
my $min_mtask;
my $max_p2e = -1000000;
my $max_mtask;
foreach my $mtask (sort keys %Mtasks) {
if ($Mtasks{$mtask}{elapsed} > 0) {
if ($Mtasks{$mtask}{predict} == 0) {
$Mtasks{$mtask}{predict} = 1; # don't log(0) below
}
my $p2e_ratio = log( $Mtasks{$mtask}{predict} / $Mtasks{$mtask}{elapsed} );
#print "log(p2e $mtask) = $p2e_ratio (predict $Mtasks{$mtask}{predict}, elapsed $Mtasks{$mtask}{elapsed})\n";
push @p2e_ratios, $p2e_ratio;
if ($p2e_ratio > $max_p2e) {
$max_p2e = $p2e_ratio;
$max_mtask = $mtask;
}
if ($p2e_ratio < $min_p2e) {
$min_p2e = $p2e_ratio;
$min_mtask = $mtask;
}
}
}
print "\nStatistics:\n";
print " min log(p2e) = $min_p2e from mtask $min_mtask (predict $Mtasks{$min_mtask}{predict}, elapsed $Mtasks{$min_mtask}{elapsed})\n";
print " max log(p2e) = $max_p2e from mtask $max_mtask (predict $Mtasks{$max_mtask}{predict}, elapsed $Mtasks{$max_mtask}{elapsed})\n";
my $stddev = stddev(\@p2e_ratios);
my $mean = mean(\@p2e_ratios);
print " mean = " . ($mean) . "\n";
print " stddev = " . ($stddev) . "\n";
print " e ^ stddev = " . exp($stddev). "\n";
print "\n";
}
sub report_graph {
my $time_per = $Opt_Time_Per_Char;
if ($time_per == 0) {
$time_per = ($Global{last_end} / 40); # Start with 40 columns
while ($time_per > 10) {
my ($graph, $conflicts) = _make_graph($time_per);
last if !$conflicts;
$time_per = int($time_per/2);
}
# One more step so we can fit more labels
$time_per = int($time_per/2);
}
my ($graph, $conflicts) = _make_graph($time_per);
print "\nThread gantt graph:\n";
print " Legend: One character width = $time_per rdtsc ticks\n";
print " Legend: '&' = multiple mtasks in this period (character width)\n";
my $scale = " <-".$Global{last_end}." rdtsc total";
for (my $col = length($scale); # -2 for '->' below
$col < ($Global{last_end}/$time_per); ++$col) {
$scale .= "-";
}
print " $scale->\n";
foreach my $thread (sort keys %{$graph}) {
print " t: ";
_print_graph_line($graph->{$thread}, '');
}
}
sub _make_graph {
my $time_per = shift;
my $graph = {}; # {thread}{column}{char=>'x' or chars=>#}
my $conflicts = 0;
foreach my $thread (keys %Threads) {
# Make potentially multiple characters per column
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
my $end = $Threads{$thread}{$start}{end};
my $mtask = $Threads{$thread}{$start}{mtask};
my $cpu = $Threads{$thread}{$start}{cpu};
my $startcol = _time_col($time_per, $start);
my $endcol = _time_col($time_per, $end);
my $label = "[";
$label .= "$cpu"; # Maybe make optional in future
my $width = $endcol - $startcol + 1;
while (length($label) < ($width-1)) { # -1 for ']'
$label .= "-";
}
$label .= "]";
$graph->{$thread}[$startcol]{char} .= $label;
}
if ($Debug) {
print "# Multicol: "; _print_graph_line($graph->{$thread}, '|');
}
# Expand line to one char per column
for (my $col = 0; $col <= $#{$graph->{$thread}}; ++$col) {
if (my $chars = $graph->{$thread}[$col]{char}) {
my $ok = 1;
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
if ($graph->{$thread}[$col + $coladd]{char}) {
$ok = 0; last;
}
}
if (!$ok) {
if ($chars =~ /\[.*\[/) { # Two begins or more
$conflicts++;
$graph->{$thread}[$col]{char} = "&";
} else {
$graph->{$thread}[$col]{char} = "[";
}
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
if ($graph->{$thread}[$col + $coladd]{char}) {
last;
} else {
$graph->{$thread}[$col + $coladd]{char} = 'x';
}
}
} else {
my $coladd = 0;
foreach my $char (split //, $chars) {
$graph->{$thread}[$col+$coladd]{char} = $char;
++$coladd;
}
}
}
}
if ($Debug) {
print "# Singlcol: "; _print_graph_line($graph->{$thread}, '|');
}
}
print "# Conflicts $conflicts\n" if $Debug;
return ($graph, $conflicts);
}
sub _print_graph_line {
my $graph_thread = shift;
my $sep = shift;
for (my $col = 0; $col <= $#{$graph_thread}; ++$col) {
my $c = $graph_thread->[$col]{char}; $c=' ' if !defined $c;
print $c, $sep;
}
print "\n";
}
sub _time_col {
my $time_per = shift;
my $time = shift;
return int($time/$time_per);
}
#######################################################################
sub write_vcd {
my $filename = shift;
print "Writing $filename\n";
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
my $vcd = {values => {}, # {<time>}{<code>} = value
sigs => {}, # {<module>}{<sig}} = code
code => 0,
};
my %parallelism;
foreach my $thread (keys %Threads) {
my $mcode = ($vcd->{sigs}{threads}{"thread${thread}_mtask"} ||= $vcd->{code}++);
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
my $end = $Threads{$thread}{$start}{end};
my $mtask = $Threads{$thread}{$start}{mtask};
my $cpu = $Threads{$thread}{$start}{cpu};
$vcd->{values}{$start}{$mcode} = $mtask;
$vcd->{values}{$end}{$mcode} = undef;
$parallelism{$start}++;
$parallelism{$end}--;
my $ccode = $vcd->{sigs}{cpus}{"cpu${cpu}_thread"} ||= $vcd->{code}++;
$vcd->{values}{$start}{$ccode} = $thread;
$vcd->{values}{$end}{$ccode} = undef;
my $mcode = $vcd->{sigs}{mtasks}{"mtask${mtask}_cpu"} ||= $vcd->{code}++;
$vcd->{values}{$start}{$mcode} = $cpu;
$vcd->{values}{$end}{$mcode} = undef;
}
}
{
my $pcode = ($vcd->{sigs}{Stats}{"parallelism"} ||= $vcd->{code}++);
my $value = 0;
foreach my $time (sort {$a<=>$b} keys %parallelism) {
$value += $parallelism{$time};
$vcd->{values}{$time}{$pcode} = $value;
}
}
$fh->print('$version Generated by verilator_gantt $end'."\n");
$fh->print('$timescale 1ns $end'."\n");
$fh->print("\n");
my %all_codes;
$fh->print(' $scope module gantt $end'."\n");
foreach my $module (sort keys %{$vcd->{sigs}}) {
$fh->printf(' $scope module %s $end'."\n", $module);
foreach my $sig (sort keys %{$vcd->{sigs}{$module}}) {
my $code = $vcd->{sigs}{$module}{$sig};
$fh->printf(' $var wire 32 v%x %s [31:0] $end'."\n",
$code, $sig);
$all_codes{$code} = 1;
}
$fh->print(' $upscope $end'."\n");
}
$fh->print(' $upscope $end'."\n");
$fh->print('$enddefinitions $end'."\n");
$fh->print("\n");
my $first = 1;
foreach my $time (sort {$a <=> $b} keys %{$vcd->{values}}) {
if ($first) {
$first = 0;
# Start with Z for any signals without time zero data
foreach my $code (keys %all_codes) {
if (!defined $vcd->{values}{$time}{$code}) {
$vcd->{values}{$time}{$code} = undef;
}
}
}
$fh->printf("#%d\n", $time);
foreach my $code (sort keys %{$vcd->{values}{$time}}) {
my $value = $vcd->{values}{$time}{$code};
if (defined $value) {
$fh->printf("b%b v%x\n", $value, $code);
} else {
$fh->printf("bz v%x\n", $code);
}
}
}
}
#######################################################################
# Similar to Statistics::Basic functions, but avoid a package dependency
sub max {
my $n = $_[0]; shift;
while (defined $_[0]) {
$n = $_[0] if !defined $n || $_[0] > $n;
shift;
}
return $n;
}
sub mean {
my $arrayref = shift;
my $n = 0;
my $sum = 0;
foreach my $v (@$arrayref) {
$sum += $v;
$n++;
}
return undef if !$n;
return $sum/$n;
}
sub stddev {
my $arrayref = shift;
my $n = 0;
my $sum = 0;
my $sumsq = 0;
foreach my $v (@$arrayref) {
$sum += $v;
$sumsq += $v**2;
$n++;
}
return undef if !$n;
return sqrt(($sumsq/$n) - ($sum/$n)**2);
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_gantt - Create Gantt chart of multi-threaded execution
=head1 SYNOPSIS
Creates a visual representation to help analyze Verilator multithreaded
simulation performance, by showing when each macro-task starts and ends,
and showing when each thread is busy or idle.
The generated Gantt chart has time on the X-axis. Times shown are to the
scale printed, i.e. a certain about of time for each character width. The
Y-axis shows threads, each thread's execution is shown on one line. That
line shows "[" at the position in time when it executes.
Following the "[" is the cpu number the task executed on, followed by zero
or more "-" to make the width of the characters match the scaled execution
time, followed by a "]". If the scale is too small, the cpu number and
mtask number will not be printed. If the scale is very small, a "&"
indicates multiple mtasks started at that time position.
Also creates a value change dump (VCD) format dump file which may be viewed
in a waveform viewer (e.g. C<GTKWave>). See below.
=head1 USAGE
Build with --prof-threads.
Run a sim with +verilator+prof+threads+window 2.
This will create profile_threads.dat.
Then run:
verilator_gantt profile_threads.dat
The report will be printed on standard output, this also generates
profile_threads.vcd
View profile_threads.vcd in a waveform viewer.
=head1 VCD SIGNALS
In waveforms there are the following signals. Most signals the "decimal"
format will remove the leading zeros and make the traces easier to read.
parallelism: The number of mtasks active at this time, for best performance
this will match the thread count. You may want to use an "analog step"
format to view this signal.
cpu#_thread: For the given CPU number, the thread number executing.
mtask#_cpu; For the given mtask id, the CPU it is executing on.
thread#_mtask: For the given thread number, the mtask id executing.
=head1 ARGUMENTS
=over 4
=item I<filename>
The filename to read data from, defaults to "profile_threads.dat".
=item --help
Displays this message and program version and exits.
=item --scale I<n>
On the X-axis of the generated Gantt chart, each character represents this
many time units. (On x86, time units are rdtsc ticks.) Defaults to 0,
which will automatically compute a reasonable scale where no two mtasks
need to fit into same character width's worth of scaled time.
=item --no-vcd
=item --vcd I<filename>
Set output filename for vcd dump, or disable. Default is
verilator_gantt.vcd.
=back
=head1 DISTRIBUTION
The latest version is available from L<http://www.veripool.org/>.
Copyright 2018-2018 by Wilson Snyder. Verilator 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.
=head1 AUTHORS
Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
C<verilator>
=cut
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_gantt $V4/test_regress/obj_vltmt/t_gantt/vlt_sim.log"
### End:

View File

@ -6,7 +6,7 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[3.927 devel],[https://www.veripool.org/verilator],
AC_INIT([Verilator],[4.000 devel],[https://www.veripool.org/verilator],
[verilator],[https://www.veripool.org/verilator])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
@ -189,6 +189,34 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
fi
])
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
[# _MY_LDLIBS_CHECK_FLAG(flag) -- Check if linker supports specific options
# Set $_my_result appropriately
ACO_SAVE_LIBS="$LIBS"
LIBS="$LIBS $1"
AC_MSG_CHECKING([whether $CXX linker accepts $1])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[]])],
[_my_result=yes
if test -s conftest.err; then
if grep -e "$1" conftest.err >/dev/null; then
_my_result=no
fi
fi],
[_my_result=no])
AC_MSG_RESULT($_my_result)
LIBS="$ACO_SAVE_LIBS"
])
AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
[# _MY_LDLIBS_CHECK_OPT(flag) -- Check if linker supports specific options
# If it does, append flag to variable
_MY_LDLIBS_CHECK_FLAG($2)
if test "$_my_result" = "yes" ; then
$1="$$1 $2"
fi
])
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# gnu++14 is the newest that Verilator supports
@ -225,7 +253,6 @@ AC_SUBST(CFG_CXXFLAGS_WEXTRA)
# Flags for compiling Verilator internals including parser always
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-delete-null-pointer-checks)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unused-parameter)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-undefined-bool-conversion)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-shadow)
@ -257,6 +284,13 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-unused-variable)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-shadow)
AC_SUBST(CFG_CXXFLAGS_NO_UNUSED)
# Find multithread linker flags
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-mt)
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-pthread)
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-lpthread)
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-latomic)
AC_SUBST(CFG_LDLIBS_THREADS)
# Set CFG_WITH_THREADED if can support threading
AC_MSG_CHECKING(whether $CXX supports Verilated threads)
ACO_SAVE_CXXFLAGS="$CXXFLAGS"

View File

@ -38,6 +38,7 @@ VerilatedVoidCb Verilated::s_flushCb = NULL;
// Keep below together in one cache line
Verilated::Serialized Verilated::s_s;
Verilated::NonSerialized Verilated::s_ns;
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
Verilated::CommandArgValues Verilated::s_args;
@ -196,6 +197,17 @@ Verilated::Serialized::Serialized() {
s_fatalOnVpiError = true; // retains old default behaviour
}
Verilated::NonSerialized::NonSerialized() {
s_profThreadsStart = 1;
s_profThreadsWindow = 2;
s_profThreadsFilenamep = strdup("profile_threads.dat");
}
Verilated::NonSerialized::~NonSerialized() {
if (s_profThreadsFilenamep) {
free(const_cast<char*>(s_profThreadsFilenamep)); s_profThreadsFilenamep=NULL;
}
}
//===========================================================================
// Random reset -- Only called at init time, so don't inline.
@ -1648,6 +1660,20 @@ void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
s_s.s_fatalOnVpiError = flag;
}
void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
s_ns.s_profThreadsStart = flag;
}
void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
s_ns.s_profThreadsWindow = flag;
}
void Verilated::profThreadsFilenamep(const char* flagp) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
if (s_ns.s_profThreadsFilenamep) free(const_cast<char*>(s_ns.s_profThreadsFilenamep));
s_ns.s_profThreadsFilenamep = strdup(flagp);
}
const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
// Returns new'ed data
@ -1684,7 +1710,7 @@ void Verilated::flushCall() VL_MT_SAFE {
}
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
VerilatedLockGuard lock(m_mutex);
VerilatedLockGuard lock(s_args.m_argMutex);
s_args.argc = argc;
s_args.argv = argv;
VerilatedImp::commandArgs(argc,argv);
@ -1780,9 +1806,61 @@ void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
for (int i=0; i<argc; ++i) {
s_s.m_argVec.push_back(argv[i]);
commandArgVl(argv[i]);
}
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
void VerilatedImp::commandArgVl(const std::string& arg) {
if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) {
std::string value;
if (0) {
}
else if (arg == "+verilator+debug") {
Verilated::debug(4);
}
else if (commandArgVlValue(arg, "+verilator+debugi+", value/*ref*/)) {
Verilated::debug(atoi(value.c_str()));
}
else if (arg == "+verilator+help") {
versionDump();
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
}
else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value/*ref*/)) {
Verilated::profThreadsStart(atoll(value.c_str()));
}
else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value/*ref*/)) {
Verilated::profThreadsWindow(atol(value.c_str()));
}
else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value/*ref*/)) {
Verilated::profThreadsFilenamep(value.c_str());
}
else if (commandArgVlValue(arg, "+verilator+rand+reset+", value/*ref*/)) {
Verilated::randReset(atoi(value.c_str()));
}
else if (arg == "+verilator+V") {
versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
}
else if (arg == "+verilator+version") {
versionDump();
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
}
else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
}
}
}
bool VerilatedImp::commandArgVlValue(const std::string& arg,
const std::string& prefix, std::string& valuer) {
size_t len = prefix.length();
if (0==strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
return true;
} else {
return false;
}
}
//======================================================================
// VerilatedSyms:: Methods

View File

@ -344,9 +344,21 @@ class Verilated {
~Serialized() {}
} s_s;
static struct NonSerialized { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
vluint64_t s_profThreadsStart; ///< +prof+threads starting time
vluint32_t s_profThreadsWindow; ///< +prof+threads window size
// Slow path
const char* s_profThreadsFilenamep; ///< +prof+threads filename
NonSerialized();
~NonSerialized();
} s_ns;
// no need to be save-restored (serialized) the
// assumption is that the restore is allowed to pass different arguments
static struct CommandArgValues {
VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED
int argc;
const char** argv;
CommandArgValues() : argc(0), argv(NULL) {}
@ -408,6 +420,14 @@ public:
/// Enable/disable vpi fatal
static void fatalOnVpiError(bool flag) VL_MT_SAFE;
static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; }
/// --prof-threads related settings
static void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
static vluint64_t profThreadsStart() VL_MT_SAFE { return s_ns.s_profThreadsStart; }
static void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
static vluint32_t profThreadsWindow() VL_MT_SAFE { return s_ns.s_profThreadsWindow; }
static void profThreadsFilenamep(const char* flagp) VL_MT_SAFE;
static const char* profThreadsFilenamep() VL_MT_SAFE { return s_ns.s_profThreadsFilenamep; }
/// Flush callback for VCD waves
static void flushCb(VerilatedVoidCb cb) VL_MT_SAFE;
static void flushCall() VL_MT_SAFE;

View File

@ -24,6 +24,8 @@ CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
# Compiler flags that turn on extra warnings
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
# Linker libraries for multithreading
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
######################################################################
# Programs
@ -116,6 +118,7 @@ ifneq ($(VM_THREADS),0)
ifneq ($(VM_THREADS),)
# Need C++11 at least, so always default to newest
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
LDLIBS += $(CFG_LDLIBS_THREADS)
endif
endif

View File

@ -239,6 +239,9 @@ public:
}
private:
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex);
static void commandArgVl(const std::string& arg);
static bool commandArgVlValue(const std::string& arg,
const std::string& prefix, std::string& valuer);
public:
// METHODS - user scope tracking

View File

@ -0,0 +1,229 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// THIS MODULE IS PUBLICLY LICENSED
//
// Copyright 2012-2018 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.
//
// This is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
//=============================================================================
///
/// \file
/// \brief Thread pool for verilated modules
///
//=============================================================================
#include "verilatedos.h"
#include "verilated_threads.h"
#include <cstdio>
std::atomic<vluint64_t> VlNotification::s_yields;
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = NULL;
//=============================================================================
// VlMTaskVertex
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
: m_upstreamDepsDone(0),
m_upstreamDepCount(upstreamDepCount) {
assert(atomic_is_lock_free(&m_upstreamDepsDone));
}
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling)
: m_poolp(poolp)
, m_profiling(profiling)
, m_exiting(false)
// Must init this last -- after setting up fields that it might read:
, m_cthread(startWorker, this) {}
VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release);
{
VerilatedLockGuard lk(m_mutex);
if (sleeping()) {
wakeUp();
}
}
// The thread should exit; join it.
m_cthread.join();
}
void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_profiling)) {
m_poolp->setupProfilingClientThread();
}
VlNotification alarm;
ExecRec work;
work.m_fnp = NULL;
while (1) {
bool sleep = false;
if (VL_UNLIKELY(!work.m_fnp)) {
// Look for work
VerilatedLockGuard lk(m_mutex);
if (VL_LIKELY(!m_ready.empty())) {
dequeWork(&work);
} else {
// No work available, prepare to sleep. Pass alarm/work
// into m_sleepAlarm so wakeUp will tall this function.
//
// Must modify m_sleepAlarm in the same critical section as
// the check for ready work, otherwise we could race with
// another thread enqueueing work and never be awoken.
m_sleepAlarm.first = &alarm;
m_sleepAlarm.second = &work;
sleep = true;
}
}
// Do this here, not above, to avoid a race with the destructor.
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire)))
break;
if (VL_UNLIKELY(sleep)) {
alarm.waitForNotification(); // ZZZzzzzz
alarm.reset();
}
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_evenCycle, work.m_sym);
work.m_fnp = NULL;
}
}
if (VL_UNLIKELY(m_profiling)) {
m_poolp->tearDownProfilingClientThread();
}
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(int nThreads, bool profiling)
: m_profiling(profiling) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads+1) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n", cpus, nThreads+1);
}
// Create'em
for (int i=0; i<nThreads; ++i) {
m_workers.push_back(new VlWorkerThread(this, profiling));
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
// donated to run mtasks during the eval.
if (VL_UNLIKELY(m_profiling)) {
setupProfilingClientThread();
}
}
VlThreadPool::~VlThreadPool() {
for (int i = 0; i < m_workers.size(); ++i) {
// Each ~WorkerThread will wait for its thread to exit.
delete m_workers[i];
}
if (VL_UNLIKELY(m_profiling)) {
tearDownProfilingClientThread();
}
}
void VlThreadPool::tearDownProfilingClientThread() {
assert(t_profilep);
delete t_profilep;
t_profilep = NULL;
}
void VlThreadPool::setupProfilingClientThread() {
assert(!t_profilep);
t_profilep = new ProfileTrace;
// Reserve some space in the thread-local profiling buffer;
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
VerilatedLockGuard lk(m_mutex);
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) {
VerilatedLockGuard lk(m_mutex);
for (ProfileSet::iterator it = m_allProfiles.begin();
it != m_allProfiles.end(); ++it) {
// Every thread's profile trace gets a copy of rec.
(*it)->emplace_back(rec);
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
VerilatedLockGuard lk(m_mutex);
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* fp = fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
return;
}
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n",
vluint64_t(m_workers.size()+1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
Verilated::profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
Verilated::profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n",
VlNotification::yields());
vluint32_t thread_id = 0;
for (ProfileSet::iterator pit = m_allProfiles.begin();
pit != m_allProfiles.end(); ++pit) {
++thread_id;
bool printing = false; // False while in warmup phase
for (ProfileTrace::iterator eit = (*pit)->begin();
eit != (*pit)->end(); ++eit) {
switch (eit->m_type) {
case VlProfileRec::TYPE_BARRIER:
printing = true;
break;
case VlProfileRec::TYPE_MTASK_RUN:
if (!printing) break;
fprintf(fp, "VLPROF mtask %d"
" start %" VL_PRI64"u end %" VL_PRI64"u elapsed %" VL_PRI64 "u"
" predict_time %u cpu %u on thread %u\n",
eit->m_mtaskId,
eit->m_startTime,
eit->m_endTime,
(eit->m_endTime - eit->m_startTime),
eit->m_predictTime,
eit->m_cpu,
thread_id);
break;
default: assert(false);
break;
}
}
}
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n",
ticksElapsed);
fclose(fp);
}

313
include/verilated_threads.h Normal file
View File

@ -0,0 +1,313 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// THIS MODULE IS PUBLICLY LICENSED
//
// Copyright 2012-2018 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.
//
// This is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
//=============================================================================
///
/// \file
/// \brief Thread pool and profiling for Verilated modules
///
//=============================================================================
#ifndef _VERILATED_THREADS_H_
#define _VERILATED_THREADS_H_
#include "verilatedos.h"
#include <atomic>
#include <thread>
#include <vector>
#include <set>
#include <sched.h> // For sched_getcpu()
#include "verilated.h" // for VerilatedMutex and clang annotations
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
typedef void* VlThrSymTab;
class VlNotification {
// MEMBERS
std::atomic<bool> m_notified; // Notification pending
static std::atomic<vluint64_t> s_yields; // Statistics
public:
// CONSTRUCTORS
VlNotification()
: m_notified(false) {
assert(atomic_is_lock_free(&m_notified));
}
~VlNotification() {}
// METHODS
static vluint64_t yields() { return s_yields; }
// Block until notify() has occurred, then return.
// If notify() has already occurred, return immediately.
//
// This is logically const: the object will remain in notified state
// after WaitForNotification() returns, so you could notify more than
// one thread of the same event.
inline void waitForNotification() {
unsigned ct = 0;
while (VL_UNLIKELY(!notified())) {
VL_CPU_RELAX();
ct++;
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
ct = 0;
++s_yields; // Statistics
std::this_thread::yield();
}
}
}
// The 'inline' keyword here means nothing to the compiler, it's
// implicit on methods defined within the class body anyway.
//
// 'inline' is attached the this method, and others in this file,
// to remind humans that some routines in this file are called many
// times per cycle in threaded mode. Such routines should be
// inlinable; that's why they're declared in the .h and not the .cpp.
inline bool notified() {
return m_notified.load(std::memory_order_acquire);
}
// Set notified state. If state is already notified,
// it remains so.
inline void notify() {
m_notified.store(true, std::memory_order_release);
}
// Reset the state to un-notified state, which is also the
// state of a new Notification object.
inline void reset() {
m_notified.store(false, std::memory_order_relaxed);
}
};
typedef void (*VlExecFnp)(bool, VlThrSymTab);
/// Track dependencies for a single MTask.
class VlMTaskVertex {
// MEMBERS
// On even cycles, _upstreamDepsDone increases as upstream
// dependencies complete. When it reaches _upstreamDepCount,
// this MTaskVertex is ready.
//
// On odd cycles, _upstreamDepsDone decreases as upstream
// dependencies complete, and when it reaches zero this MTaskVertex
// is ready.
//
// An atomic is smaller than a mutex, and lock-free.
//
// (Why does the size of this class matter? If an mtask has many
// downstream mtasks to notify, we hope these will pack into a
// small number of cache lines to reduce the cost of pointer chasing
// during done-notification. Nobody's quantified that cost though.
// If we were really serious about shrinking this class, we could
// use 16-bit types here...)
std::atomic<vluint32_t> m_upstreamDepsDone;
const vluint32_t m_upstreamDepCount;
public:
// CONSTRUCTORS
// 'upstreamDepCount' is the number of upstream MTaskVertex's
// that must notify this MTaskVertex before it will become ready
// to run.
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
~VlMTaskVertex() {}
// Upstream mtasks must call this when they complete.
// Returns true when the current MTaskVertex becomes ready to execute,
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
vluint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
vluint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
VL_CPU_RELAX();
}
}
};
// Profiling support
class VlProfileRec {
protected:
friend class VlThreadPool;
enum VlProfileE {
TYPE_MTASK_RUN,
TYPE_BARRIER
};
VlProfileE m_type; // Record type
vluint32_t m_mtaskId; // Mtask we're logging
vluint32_t m_predictTime; // How long scheduler predicted would take
vluint64_t m_startTime; // Tick at start of execution
vluint64_t m_endTime; // Tick at end of execution
unsigned m_cpu; // Execution CPU number (at start anyways)
public:
class Barrier {};
VlProfileRec() {}
explicit VlProfileRec(Barrier) {
m_type = TYPE_BARRIER;
m_mtaskId = 0;
m_predictTime = 0;
m_startTime = 0;
m_cpu = sched_getcpu();
}
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
m_type = VlProfileRec::TYPE_MTASK_RUN;
m_mtaskId = mtask;
m_predictTime = predict;
m_startTime = time;
m_cpu = sched_getcpu();
}
void endRecord(vluint64_t time) {
m_endTime = time;
}
};
class VlThreadPool;
class VlWorkerThread {
private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlThrSymTab m_sym; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec() : m_fnp(NULL), m_sym(NULL), m_evenCycle(false) {}
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
: m_fnp(fnp), m_sym(sym), m_evenCycle(evenCycle) {}
};
// MEMBERS
VerilatedMutex m_mutex;
// Why a vector? We expect the pending list to be very short, typically
// 0 or 1 or 2, so popping from the front shouldn't be
// expensive. Revisit if we ever have longer queues...
std::vector<ExecRec> m_ready VL_GUARDED_BY(m_mutex);
VlThreadPool* m_poolp; // Our associated thread pool
// If values stored are non-NULL, the thread is asleep pending new
// work. If the thread is not asleep, both parts of m_sleepAlarm must
// be NULL.
std::pair<VlNotification*, ExecRec*> m_sleepAlarm VL_GUARDED_BY(m_mutex);
bool m_profiling; // Is profiling enabled?
std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record
VL_UNCOPYABLE(VlWorkerThread);
public:
// CONSTRUCTORS
explicit VlWorkerThread(VlThreadPool* poolp, bool profiling);
~VlWorkerThread();
// METHODS
inline void dequeWork(ExecRec* workp) VL_REQUIRES(m_mutex) {
// As noted above this is inefficient if our ready list is ever
// long (but it shouldn't be)
*workp = m_ready.front();
m_ready.erase(m_ready.begin());
}
inline void wakeUp() VL_REQUIRES(m_mutex) {
VlNotification* notifyp = m_sleepAlarm.first;
m_sleepAlarm.first = NULL; // NULL+NULL means wake
m_sleepAlarm.second = NULL;
notifyp->notify();
}
inline bool sleeping() VL_REQUIRES(m_mutex) {
return (m_sleepAlarm.first != NULL);
}
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) {
VerilatedLockGuard lk(m_mutex);
m_ready.emplace_back(fnp, evenCycle, sym);
if (VL_LIKELY(sleeping())) { // Generally queue is waiting for work
// Awaken thread
dequeWork(m_sleepAlarm.second);
wakeUp();
}
}
void workerLoop();
static void startWorker(VlWorkerThread* workerp);
};
class VlThreadPool {
// TYPES
typedef std::vector<VlProfileRec> ProfileTrace;
typedef std::set<ProfileTrace*> ProfileSet;
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
bool m_profiling; // is profiling enabled?
// Support profiling -- we can append records of profiling events
// to this vector with very low overhead, and then dump them out
// later. This prevents the overhead of printf/malloc/IO from
// corrupting the profiling data. It's super cheap to append
// a VlProfileRec struct on the end of a pre-allocated vector;
// this is the only cost we pay in real-time during a profiling cycle.
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
ProfileSet m_allProfiles VL_GUARDED_BY(m_mutex);
VerilatedMutex m_mutex;
public:
// CONSTRUCTORS
// Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...)
VlThreadPool(int nThreads, bool profiling);
~VlThreadPool();
// METHODS
inline int numThreads() const {
return m_workers.size();
}
inline VlWorkerThread* workerp(int index) {
assert(index >= 0);
assert(index < m_workers.size());
return m_workers[index];
}
inline VlProfileRec* profileAppend() {
t_profilep->emplace_back();
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec);
void profileDump(const char* filenamep, vluint64_t ticksElapsed);
// In profiling mode, each executing thread must call
// this once to setup profiling state:
void setupProfilingClientThread();
void tearDownProfilingClientThread();
private:
VL_UNCOPYABLE(VlThreadPool);
};
#endif

View File

@ -0,0 +1,548 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: pre-C++11 replacements for std::unordered_set
// and std::unordered_map.
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
//*************************************************************************
// This file has clones of the std::unordered_set and std::unordered_map
// hash table types. They are here so that Verilator can use hash tables
// in pre-C++11 compilers, and the same client code can link against the
// std:: types when they are available.
//
// The implementations in this file do not implement the complete APIs
// of the std:: types. Nor are they correct in every detail,
// notably, the const_iterators do not enforce constness. We can extend
// these implementations to cover more of the std API as needed.
//
// TODO: In the future, when Verilator requires C++11 to compile,
// remove this entire file and switch to the std:: types.
//
//*************************************************************************
#ifndef _V3_UNORDERED_SET_MAP_H_
#define _V3_UNORDERED_SET_MAP_H_
#include "verilated_config.h"
#include "verilatedos.h"
#include <list>
#include <stdexcept>
#include <string>
// Abstract 'vl_hash' and 'vl_equal_to' templates.
template <typename T> struct vl_hash {
size_t operator()(const T& k) const;
};
template <typename T> struct vl_equal_to {
bool operator()(const T& a, const T& b) const;
};
// Specializations of 'vl_hash' and 'vl_equal_to'.
inline size_t vl_hash_bytes(const void* vbufp, size_t nbytes) {
const vluint8_t* bufp = static_cast<const vluint8_t*>(vbufp);
size_t hash = 0;
for (size_t i = 0; i < nbytes; i++) {
hash = bufp[i] + 31u * hash; // the K&R classic!
}
return hash;
}
template <> inline size_t
vl_hash<unsigned int>::operator()(const unsigned int& k) const {
return k;
}
template <> inline bool
vl_equal_to<unsigned int>::operator()(const unsigned int& a,
const unsigned int& b) const {
return a == b;
}
template <> inline size_t
vl_hash<std::string>::operator()(const std::string& k) const {
return vl_hash_bytes(k.data(), k.size());
}
template <> inline bool
vl_equal_to<std::string>::operator()(const std::string& a,
const std::string& b) const {
// Don't scan the strings if the sizes are different.
if (a.size() != b.size()) {
return false;
}
return (0 == a.compare(b)); // Must scan.
}
template <typename T> struct vl_hash<T*> {
size_t operator()(T* kp) const {
return ((sizeof(size_t) == sizeof(kp))
? reinterpret_cast<size_t>(kp)
: vl_hash_bytes(&kp, sizeof(kp)));
}
};
template <typename T> struct vl_equal_to<T*> {
bool operator()(T* ap, T* bp) const {
return ap == bp;
}
};
//===================================================================
//
/// Functional clone of the std::unordered_set hash table.
template <class T_Key,
class T_Hash = vl_hash<T_Key>,
class T_Equal = vl_equal_to<T_Key> >
class vl_unordered_set {
public:
// TYPES
typedef std::list<T_Key> Bucket;
enum RehashType {GROW, SHRINK};
template <class KK, class VV,
class HH, class EQ> friend class vl_unordered_map;
class iterator {
protected:
// MEMBERS
size_t m_bucketIdx; // Bucket this iterator points into.
typename Bucket::iterator m_bit; // Bucket-local iterator.
const vl_unordered_set* m_setp; // The containing set.
public:
// CONSTRUCTORS
iterator(size_t bucketIdx, typename Bucket::iterator bit,
const vl_unordered_set* setp)
: m_bucketIdx(bucketIdx), m_bit(bit), m_setp(setp) {}
// METHODS
const T_Key& operator*() const {
return *m_bit;
}
// This should really be 'const T_Key*' type for unordered_set,
// however this iterator is shared with unordered_map whose
// operator-> returns a non-const ValueType*, so keep this
// non-const to avoid having to define a whole separate iterator
// for unordered_map.
T_Key* operator->() const {
return &(*m_bit);
}
bool operator==(const iterator& other) const {
return ((m_bucketIdx == other.m_bucketIdx)
&& (m_bit == other.m_bit));
}
bool operator!=(const iterator& other) const {
return (!this->operator==(other));
}
void advanceUntilValid() {
while (1) {
if (m_bit != m_setp->m_bucketsp[m_bucketIdx].end()) {
// Valid iterator in this bucket; we're done.
return;
}
// Try the next bucket?
m_bucketIdx++;
if (m_bucketIdx == m_setp->numBuckets()) {
// Ran past the end of buckets, set to end().
*this = m_setp->end();
return;
}
m_bit = m_setp->m_bucketsp[m_bucketIdx].begin();
}
}
void operator++() {
++m_bit;
advanceUntilValid();
}
typename Bucket::iterator bit() const { return m_bit; }
};
// TODO: there's no real const enforcement on the 'const_iterator'.
typedef iterator const_iterator;
private:
// MEMBERS
size_t m_numElements; // Number of entries present.
size_t m_log2Buckets; // Log-base-2 of the number of buckets.
mutable Bucket* m_bucketsp; // Hash table buckets. May be NULL;
// // we'll allocate it on the fly when
// // the first entries are created.
Bucket m_emptyBucket; // A fake bucket, used to construct end().
T_Hash m_hash; // Hash function provider.
T_Equal m_equal; // Equal-to function provider.
public:
// CONSTRUCTORS
vl_unordered_set()
: m_numElements(0)
, m_log2Buckets(initLog2Buckets())
, m_bucketsp(NULL)
, m_hash()
, m_equal() { }
vl_unordered_set(const vl_unordered_set& other)
: m_numElements(other.m_numElements)
, m_log2Buckets(other.m_log2Buckets)
, m_bucketsp(NULL)
, m_hash()
, m_equal() {
if (other.m_bucketsp) {
m_bucketsp = new Bucket[numBuckets()];
for (size_t i = 0; i < numBuckets(); i++) {
m_bucketsp[i] = other.m_bucketsp[i];
}
}
}
~vl_unordered_set() {
delete [] m_bucketsp; VL_DANGLING(m_bucketsp);
}
vl_unordered_set& operator=(const vl_unordered_set& other) {
if (this != &other) {
clear();
delete [] m_bucketsp;
m_numElements = other.m_numElements;
m_log2Buckets = other.m_log2Buckets;
if (other.m_bucketsp) {
m_bucketsp = new Bucket[numBuckets()];
for (size_t i = 0; i < numBuckets(); i++) {
m_bucketsp[i] = other.m_bucketsp[i];
}
} else {
m_bucketsp = NULL;
}
}
return *this;
}
// METHODS
static size_t initLog2Buckets() { return 4; }
iterator begin() {
if (m_numElements) {
initBuckets();
iterator result = iterator(0, m_bucketsp[0].begin(), this);
result.advanceUntilValid();
return result;
}
return end();
}
const_iterator begin() const {
if (m_numElements) {
initBuckets();
const_iterator result = iterator(0, m_bucketsp[0].begin(), this);
result.advanceUntilValid();
return result;
}
return end();
}
const_iterator end() const {
return iterator(VL_ULL(0xFFFFFFFFFFFFFFFF),
const_cast<Bucket&>(m_emptyBucket).begin(), this);
}
bool empty() const { return m_numElements == 0; }
size_t size() const { return m_numElements; }
size_t count(const T_Key& key) const {
return (find(key) == end()) ? 0 : 1;
}
size_t hashToBucket(size_t hashVal) const {
return hashToBucket(hashVal, m_log2Buckets);
}
static size_t hashToBucket(size_t hashVal, unsigned log2Buckets) {
// Fibonacci hashing
// See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
//
// * The magic numbers below are UINT_MAX/phi where phi is the
// golden ratio number (1.618...) for either 64- or 32-bit
// values of UINT_MAX.
//
// * Fibonacci hashing mixes the result of the client's hash
// function further. This permits the use of very fast client
// hash funcs (like just returning the int or pointer value as
// is!) and tolerates crappy client hash functions pretty well.
size_t mult = hashVal * ((sizeof(size_t) == 8)
? VL_ULL(11400714819323198485)
: 2654435769lu);
size_t result = (mult >> (((sizeof(size_t) == 8)
? 64 : 32) - log2Buckets));
return result;
}
iterator find_internal(const T_Key& key, size_t& bucketIdxOut) {
size_t hash = m_hash.operator()(key);
bucketIdxOut = hashToBucket(hash);
initBuckets();
Bucket* bucketp = &m_bucketsp[bucketIdxOut];
for (typename Bucket::iterator it = bucketp->begin();
it != bucketp->end(); ++it) {
if (m_equal.operator()(*it, key)) {
return iterator(bucketIdxOut, it, this);
}
}
return end();
}
const_iterator find(const T_Key& key) const {
size_t bucketIdx;
return const_cast<vl_unordered_set*>(this)->find_internal(key,
bucketIdx);
}
iterator find(const T_Key& key) {
size_t bucketIdx;
return find_internal(key, bucketIdx);
}
std::pair<iterator, bool> insert(const T_Key &val) {
size_t bucketIdx;
iterator existIt = find_internal(val, bucketIdx);
if (existIt != end()) {
// Collision with existing element.
//
// An element may be inserted only if it is not
// equal to an existing element. So fail.
return std::pair<iterator, bool>(end(), false);
}
// No collision, so insert it.
m_numElements++;
m_bucketsp[bucketIdx].push_front(val);
// Compute result iterator. This pointer will be valid
// if we don't rehash:
iterator result_it(bucketIdx, m_bucketsp[bucketIdx].begin(), this);
if (needToRehash(GROW)) {
rehash(GROW);
// ... since we rehashed, do a lookup to get the result iterator.
result_it = find(val);
}
return std::pair<iterator, bool>(result_it, true);
}
iterator erase(iterator it) {
iterator next_it = it;
++next_it;
erase(*it);
return next_it;
}
size_t erase(const T_Key &key) {
size_t bucketIdx;
iterator it = find_internal(key, bucketIdx);
if (it != end()) {
m_bucketsp[bucketIdx].erase(it.bit());
m_numElements--;
// Rehashing to handle a shrinking data set is important
// for the Scoreboard in V3Partition, which begins tracking
// a huge number of vertices and then tracks a successively
// smaller number over time.
if (needToRehash(SHRINK)) {
rehash(SHRINK);
}
return 1;
}
return 0;
}
void clear() {
if (m_bucketsp) {
delete [] m_bucketsp;
m_bucketsp = NULL;
}
m_numElements = 0;
m_log2Buckets = initLog2Buckets();
}
private:
size_t numBuckets() const { return (VL_ULL(1) << m_log2Buckets); }
Bucket* getBucket(size_t idx) {
initBuckets();
return &m_bucketsp[idx];
}
void initBuckets() const {
if (!m_bucketsp) m_bucketsp = new Bucket[numBuckets()];
}
bool needToRehash(RehashType rt) const {
if (rt == GROW) {
return ((4 * numBuckets()) < m_numElements);
} else {
return (numBuckets() > (4 * m_numElements));
}
}
void rehash(RehashType rt) {
size_t new_log2Buckets;
if (rt == GROW) {
new_log2Buckets = m_log2Buckets + 2;
} else {
if (m_log2Buckets <= 4) {
// On shrink, saturate m_log2Buckets at its
// initial size of 2^4 == 16 buckets. Don't risk
// underflowing!
return;
}
new_log2Buckets = m_log2Buckets - 2;
}
size_t new_num_buckets = VL_ULL(1) << new_log2Buckets;
Bucket* new_bucketsp = new Bucket[new_num_buckets];
for (size_t i=0; i<numBuckets(); i++) {
while (!m_bucketsp[i].empty()) {
typename Bucket::iterator bit = m_bucketsp[i].begin();
size_t hash = m_hash.operator()(*bit);
size_t new_idx = hashToBucket(hash, new_log2Buckets);
// Avoid mallocing one list elem and freeing another;
// splice just moves it over.
new_bucketsp[new_idx].splice(new_bucketsp[new_idx].begin(),
m_bucketsp[i], bit);
}
}
delete[] m_bucketsp;
m_bucketsp = new_bucketsp;
m_log2Buckets = new_log2Buckets;
}
};
//===================================================================
//
/// Functional clone of the std::unordered_map hash table.
template <class T_Key,
class T_Value,
class T_Hash = vl_hash<T_Key>,
class T_Equal = vl_equal_to<T_Key> >
class vl_unordered_map {
private:
// TYPES
typedef std::pair<T_Key, T_Value> KeyValPair;
class KeyHash {
private:
T_Hash key_hash;
public:
KeyHash() {}
size_t operator()(const KeyValPair& kv_pair) const {
return key_hash.operator()(kv_pair.first);
}
};
class KeyEqual {
private:
T_Equal key_eq;
public:
KeyEqual() {}
bool operator()(const KeyValPair& kv_a, const KeyValPair& kv_b) const {
return key_eq.operator()(kv_a.first, kv_b.first);
}
};
// MEMBERS
typedef vl_unordered_set<KeyValPair, KeyHash, KeyEqual> MapSet;
MapSet m_set; // Wrap this vl_unordered_set which holds all state.
public:
// CONSTRUCTORS
vl_unordered_map() {}
~vl_unordered_map() {}
typedef typename MapSet::iterator iterator;
typedef typename MapSet::const_iterator const_iterator;
// METHODS
iterator begin() { return m_set.begin(); }
const_iterator begin() const { return m_set.begin(); }
const_iterator end() const { return m_set.end(); }
bool empty() const { return m_set.empty(); }
iterator find(const T_Key& k) {
// We can't assume that T_Value() is defined.
// ie, this does not work:
// return m_set.find(std::make_pair(k, T_Value()));
// So, do this instead:
T_Hash mapHash;
T_Equal mapEq;
size_t hash = mapHash.operator()(k);
size_t bucketIdxOut = m_set.hashToBucket(hash);
typename MapSet::Bucket* bucketp = m_set.getBucket(bucketIdxOut);
for (typename MapSet::Bucket::iterator it = bucketp->begin();
it != bucketp->end(); ++it) {
if (mapEq.operator()(it->first, k)) {
return iterator(bucketIdxOut, it, &m_set);
}
}
return end();
}
const_iterator find(const T_Key& k) const {
return const_cast<vl_unordered_map*>(this)->find(k);
}
std::pair<iterator, bool> insert(const KeyValPair& val) {
return m_set.insert(val);
}
iterator erase(iterator it) { return m_set.erase(it); }
size_t erase(const T_Key& k) {
iterator it = find(k);
if (it == end()) { return 0; }
m_set.erase(it);
return 1;
}
T_Value& operator[](const T_Key& k) {
// Here we can assume T_Value() is defined, as
// std::unordered_map::operator[] relies on it too.
KeyValPair dummy = std::make_pair(k, T_Value());
iterator it = m_set.find(dummy);
if (it == m_set.end()) {
it = m_set.insert(dummy).first;
}
// For the 'set', it's generally not safe to modify
// the value after deref. For the 'map' though, we know
// it's safe to modify the value field and we can allow it:
return it->second;
}
T_Value& at(const T_Key& k) {
iterator it = find(k);
if (it == end()) { throw std::out_of_range("sorry"); }
return it->second;
}
const T_Value& at(const T_Key& k) const {
iterator it = find(k);
if (it == end()) { throw std::out_of_range("sorry"); }
return it->second;
}
void clear() { m_set.clear(); }
size_t size() const { return m_set.size(); }
};
#endif

View File

@ -97,7 +97,7 @@ protected:
void* m_userthis; ///< Fake "this" for caller
vluint32_t m_code; ///< Starting code number
// CONSTRUCTORS
VerilatedVcdCallInfo (VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
VerilatedVcdCallback_t changecb,
void* ut, vluint32_t code)
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
@ -146,7 +146,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
m_wroteBytes = 0;
}
void VerilatedVcd::open (const char* filename) {
void VerilatedVcd::open(const char* filename) {
m_assertOne.check();
if (isOpen()) return;
@ -159,7 +159,7 @@ void VerilatedVcd::open (const char* filename) {
Verilated::flushCb(&flush_all);
// SPDIFF_ON
openNext (m_rolloverMB!=0);
openNext(m_rolloverMB!=0);
if (!isOpen()) return;
dumpHeader();
@ -175,7 +175,7 @@ void VerilatedVcd::open (const char* filename) {
}
}
void VerilatedVcd::openNext (bool incFilename) {
void VerilatedVcd::openNext(bool incFilename) {
// Open next filename in concat sequence, mangle filename if
// incFilename is true.
m_assertOne.check();
@ -272,7 +272,7 @@ VerilatedVcd::~VerilatedVcd() {
VerilatedVcdSingleton::removeVcd(this);
}
void VerilatedVcd::closePrev () {
void VerilatedVcd::closePrev() {
// This function is on the flush() call path
if (!isOpen()) return;
@ -281,7 +281,7 @@ void VerilatedVcd::closePrev () {
m_filep->close();
}
void VerilatedVcd::closeErr () {
void VerilatedVcd::closeErr() {
// This function is on the flush() call path
// Close due to an error. We might abort before even getting here,
// depending on the definition of vl_fatal.
@ -304,7 +304,7 @@ void VerilatedVcd::close() {
closePrev();
}
void VerilatedVcd::printStr (const char* str) {
void VerilatedVcd::printStr(const char* str) {
// Not fast...
while (*str) {
*m_writep++ = *str++;
@ -312,13 +312,13 @@ void VerilatedVcd::printStr (const char* str) {
}
}
void VerilatedVcd::printQuad (vluint64_t n) {
void VerilatedVcd::printQuad(vluint64_t n) {
char buf [100];
sprintf(buf,"%" VL_PRI64 "u", n);
printStr(buf);
}
void VerilatedVcd::printTime (vluint64_t timeui) {
void VerilatedVcd::printTime(vluint64_t timeui) {
// VCD file format specification does not allow non-integers for timestamps
// Dinotrace doesn't mind, but Cadence vvision seems to choke
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
@ -347,7 +347,7 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
}
}
void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// This function is on the flush() call path
// We add output data to m_writep.
// When it gets nearly full we dump it using this routine which calls write()
@ -381,19 +381,19 @@ void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
//=============================================================================
// Simple methods
void VerilatedVcd::set_time_unit (const char* unitp) {
//cout<<" set_time_unit ("<<unitp<<") == "<<timescaleToDouble(unitp)
void VerilatedVcd::set_time_unit(const char* unitp) {
//cout<<" set_time_unit("<<unitp<<") == "<<timescaleToDouble(unitp)
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
m_timeUnit = timescaleToDouble(unitp);
}
void VerilatedVcd::set_time_resolution (const char* unitp) {
//cout<<"set_time_resolution ("<<unitp<<") == "<<timescaleToDouble(unitp)
void VerilatedVcd::set_time_resolution(const char* unitp) {
//cout<<"set_time_resolution("<<unitp<<") == "<<timescaleToDouble(unitp)
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
m_timeRes = timescaleToDouble(unitp);
}
double VerilatedVcd::timescaleToDouble (const char* unitp) {
double VerilatedVcd::timescaleToDouble(const char* unitp) {
char* endp;
double value = strtod(unitp, &endp);
if (value==0.0 && endp==unitp) value=1; // On error so we allow just "ns" to return 1e-9.
@ -411,7 +411,7 @@ double VerilatedVcd::timescaleToDouble (const char* unitp) {
return value;
}
std::string VerilatedVcd::doubleToTimescale (double value) {
std::string VerilatedVcd::doubleToTimescale(double value) {
const char* suffixp = "s";
if (value>=1e0) { suffixp="s"; value *= 1e0; }
else if (value>=1e-3 ) { suffixp="ms"; value *= 1e3; }
@ -427,14 +427,14 @@ std::string VerilatedVcd::doubleToTimescale (double value) {
//=============================================================================
// Definitions
void VerilatedVcd::printIndent (int level_change) {
void VerilatedVcd::printIndent(int level_change) {
if (level_change<0) m_modDepth += level_change;
assert(m_modDepth>=0);
for (int i=0; i<m_modDepth; i++) printStr(" ");
if (level_change>0) m_modDepth += level_change;
}
void VerilatedVcd::dumpHeader () {
void VerilatedVcd::dumpHeader() {
printStr("$version Generated by VerilatedVcd $end\n");
time_t time_str = time(NULL);
printStr("$date "); printStr(ctime(&time_str)); printStr(" $end\n");
@ -447,7 +447,7 @@ void VerilatedVcd::dumpHeader () {
makeNameMap();
// Signal header
assert (m_modDepth==0);
assert(m_modDepth==0);
printIndent(1);
printStr("\n");
@ -508,18 +508,18 @@ void VerilatedVcd::dumpHeader () {
printIndent(-1);
printStr("$enddefinitions $end\n\n\n");
assert (m_modDepth==0);
assert(m_modDepth==0);
// Reclaim storage
deleteNameMap();
}
void VerilatedVcd::module (const std::string& name) {
void VerilatedVcd::module(const std::string& name) {
m_assertOne.check();
m_modName = name;
}
void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep,
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
int arraynum, bool tri, bool bussed, int msb, int lsb) {
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
@ -590,29 +590,29 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
}
void VerilatedVcd::declBit (vluint32_t code, const char* name, int arraynum)
{ declare (code, name, "wire", arraynum, false, false, 0, 0); }
{ declare(code, name, "wire", arraynum, false, false, 0, 0); }
void VerilatedVcd::declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
void VerilatedVcd::declQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
void VerilatedVcd::declArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
void VerilatedVcd::declTriBit (vluint32_t code, const char* name, int arraynum)
{ declare (code, name, "wire", arraynum, true, false, 0, 0); }
{ declare(code, name, "wire", arraynum, true, false, 0, 0); }
void VerilatedVcd::declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
void VerilatedVcd::declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
void VerilatedVcd::declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
void VerilatedVcd::declFloat (vluint32_t code, const char* name, int arraynum)
{ declare (code, name, "real", arraynum, false, false, 31, 0); }
{ declare(code, name, "real", arraynum, false, false, 31, 0); }
void VerilatedVcd::declDouble (vluint32_t code, const char* name, int arraynum)
{ declare (code, name, "real", arraynum, false, false, 63, 0); }
{ declare(code, name, "real", arraynum, false, false, 63, 0); }
//=============================================================================
void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
(*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) = newval;
// Buffer can't overflow before sprintf; we sized during declaration
@ -621,7 +621,7 @@ void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
void VerilatedVcd::fullFloat(vluint32_t code, const float newval) {
// cppcheck-suppress invalidPointerCast
(*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) = newval;
// Buffer can't overflow before sprintf; we sized during declaration
@ -634,9 +634,9 @@ void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
//=============================================================================
// Callbacks
void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
void VerilatedVcd::addCallback(
VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb, VerilatedVcdCallback_t changecb,
void* userthis)
void* userthis) VL_MT_UNSAFE_ONE
{
m_assertOne.check();
if (VL_UNLIKELY(isOpen())) {
@ -650,9 +650,9 @@ void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
//=============================================================================
// Dumping
void VerilatedVcd::dumpFull (vluint64_t timeui) {
void VerilatedVcd::dumpFull(vluint64_t timeui) {
m_assertOne.check();
dumpPrep (timeui);
dumpPrep(timeui);
Verilated::quiesce();
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
VerilatedVcdCallInfo *cip = m_callbacks[ent];
@ -660,7 +660,7 @@ void VerilatedVcd::dumpFull (vluint64_t timeui) {
}
}
void VerilatedVcd::dump (vluint64_t timeui) {
void VerilatedVcd::dump(vluint64_t timeui) {
m_assertOne.check();
if (!isOpen()) return;
if (VL_UNLIKELY(m_fullDump)) {
@ -672,7 +672,7 @@ void VerilatedVcd::dump (vluint64_t timeui) {
openNext(true);
if (!isOpen()) return;
}
dumpPrep (timeui);
dumpPrep(timeui);
Verilated::quiesce();
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
VerilatedVcdCallInfo *cip = m_callbacks[ent];
@ -680,7 +680,7 @@ void VerilatedVcd::dump (vluint64_t timeui) {
}
}
void VerilatedVcd::dumpPrep (vluint64_t timeui) {
void VerilatedVcd::dumpPrep(vluint64_t timeui) {
printStr("#");
printTime(timeui);
printStr("\n");
@ -705,28 +705,28 @@ vluint8_t ch;
vluint64_t timestamp = 1;
double doub = 0;
void vcdInit (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
void vcdInit(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
vcdp->scopeEscape('.');
vcdp->module ("top");
vcdp->declBus (0x2, "v1",-1,5,1);
vcdp->declBus (0x3, "v2",-1,6,0);
vcdp->module ("top.sub1");
vcdp->declBit (0x4, "s1",-1);
vcdp->declBit (0x5, "ch",-1);
vcdp->module ("top.sub2");
vcdp->declArray (0x6, "s2",-1, 40,3);
vcdp->module("top");
vcdp->declBus(0x2, "v1",-1,5,1);
vcdp->declBus(0x3, "v2",-1,6,0);
vcdp->module("top.sub1");
vcdp->declBit(0x4, "s1",-1);
vcdp->declBit(0x5, "ch",-1);
vcdp->module("top.sub2");
vcdp->declArray(0x6, "s2",-1, 40,3);
// Note need to add 3 for next code.
vcdp->module ("top2");
vcdp->declBus (0x2, "t2v1",-1,4,1);
vcdp->module("top2");
vcdp->declBus(0x2, "t2v1",-1,4,1);
vcdp->declTriBit (0x10, "io1", -1);
vcdp->declTriBus (0x12, "io5", -1,4,0);
vcdp->declTriArray (0x16, "io96",-1,95,0);
vcdp->declTriArray(0x16, "io96",-1,95,0);
// Note need to add 6 for next code.
vcdp->declDouble (0x1c, "doub",-1);
// Note need to add 2 for next code.
}
void vcdFull (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
void vcdFull(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
vcdp->fullBus (0x2, v1,5);
vcdp->fullBus (0x3, v2,7);
vcdp->fullBit (0x4, s1);
@ -734,11 +734,11 @@ void vcdFull (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
vcdp->fullArray(0x6, &s2[0], 38);
vcdp->fullTriBit (0x10, tri96[0]&1, tri96__tri[0]&1);
vcdp->fullTriBus (0x12, tri96[0]&0x1f, tri96__tri[0]&0x1f, 5);
vcdp->fullTriArray (0x16, tri96, tri96__tri, 96);
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
vcdp->fullDouble(0x1c, doub);
}
void vcdChange (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
void vcdChange(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
vcdp->chgBus (0x2, v1,5);
vcdp->chgBus (0x3, v2,7);
vcdp->chgBit (0x4, s1);
@ -761,8 +761,8 @@ main() {
doub = 0;
{
VerilatedVcdC* vcdp = new VerilatedVcdC;
vcdp->spTrace()->addCallback (&vcdInit, &vcdFull, &vcdChange, 0);
vcdp->open ("test.vcd");
vcdp->spTrace()->addCallback(&vcdInit, &vcdFull, &vcdChange, 0);
vcdp->open("test.vcd");
// Dumping
vcdp->dump(timestamp++);
v1 = 0xfff;

View File

@ -59,7 +59,7 @@ protected:
friend class VerilatedVcd;
vluint32_t m_code; ///< VCD file code number
int m_bits; ///< Size of value in bits
VerilatedVcdSig (vluint32_t code, int bits)
VerilatedVcdSig(vluint32_t code, int bits)
: m_code(code), m_bits(bits) {}
public:
~VerilatedVcdSig() {}
@ -121,25 +121,25 @@ private:
void openNext();
void makeNameMap();
void deleteNameMap();
void printIndent (int levelchange);
void printStr (const char* str);
void printQuad (vluint64_t n);
void printTime (vluint64_t timeui);
void declare (vluint32_t code, const char* name, const char* wirep,
void printIndent(int levelchange);
void printStr(const char* str);
void printQuad(vluint64_t n);
void printTime(vluint64_t timeui);
void declare(vluint32_t code, const char* name, const char* wirep,
int arraynum, bool tri, bool bussed, int msb, int lsb);
void dumpHeader();
void dumpPrep (vluint64_t timeui);
void dumpFull (vluint64_t timeui);
void dumpPrep(vluint64_t timeui);
void dumpFull(vluint64_t timeui);
// cppcheck-suppress functionConst
void dumpDone ();
inline void printCode (vluint32_t code) {
void dumpDone();
inline void printCode(vluint32_t code) {
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
*m_writep++ = static_cast<char>((code)%94+33);
}
static std::string stringCode (vluint32_t code) VL_PURE {
static std::string stringCode(vluint32_t code) VL_PURE {
std::string out;
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
@ -165,34 +165,34 @@ public:
// METHODS
void open(const char* filename) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
void openNext (bool incFilename); ///< Open next data-only file
void openNext(bool incFilename); ///< Open next data-only file
void close() VL_MT_UNSAFE_ONE; ///< Close the file
/// Flush any remaining data to this file
void flush() VL_MT_UNSAFE_ONE { bufferFlush(); }
/// Flush any remaining data from all files
static void flush_all() VL_MT_UNSAFE_ONE;
void set_time_unit (const char* unit); ///< Set time units (s/ms, defaults to ns)
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
void set_time_unit(const char* unit); ///< Set time units (s/ms, defaults to ns)
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
void set_time_resolution (const char* unit); ///< Set time resolution (s/ms, defaults to ns)
void set_time_resolution (const std::string& unit) { set_time_resolution(unit.c_str()); }
void set_time_resolution(const char* unit); ///< Set time resolution (s/ms, defaults to ns)
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
double timescaleToDouble (const char* unitp);
std::string doubleToTimescale (double value);
double timescaleToDouble(const char* unitp);
std::string doubleToTimescale(double value);
/// Inside dumping routines, called each cycle to make the dump
void dump (vluint64_t timeui);
void dump(vluint64_t timeui);
/// Call dump with a absolute unscaled time in seconds
void dumpSeconds (double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
void dumpSeconds(double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
/// Inside dumping routines, declare callbacks for tracings
void addCallback (VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
void addCallback(VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
VerilatedVcdCallback_t change,
void* userthis) VL_MT_UNSAFE_ONE;
/// Inside dumping routines, declare a module
void module (const std::string& name);
void module(const std::string& name);
/// Inside dumping routines, declare a signal
void declBit (vluint32_t code, const char* name, int arraynum);
void declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
@ -207,13 +207,13 @@ public:
// ... other module_start for submodules (based on cell name)
/// Inside dumping routines, dump one signal
void fullBit (vluint32_t code, const vluint32_t newval) {
void fullBit(vluint32_t code, const vluint32_t newval) {
// Note the &1, so we don't require clean input -- makes more common no change case faster
m_sigs_oldvalp[code] = newval;
*m_writep++=('0'+static_cast<char>(newval&1)); printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullBus (vluint32_t code, const vluint32_t newval, int bits) {
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
m_sigs_oldvalp[code] = newval;
*m_writep++='b';
for (int bit=bits-1; bit>=0; --bit) {
@ -222,7 +222,7 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullQuad (vluint32_t code, const vluint64_t newval, int bits) {
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
*m_writep++='b';
for (int bit=bits-1; bit>=0; --bit) {
@ -231,7 +231,7 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullArray (vluint32_t code, const vluint32_t* newval, int bits) {
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
m_sigs_oldvalp[code+word] = newval[word];
}
@ -242,7 +242,7 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriBit (vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
m_sigs_oldvalp[code] = newval;
m_sigs_oldvalp[code+1] = newtri;
*m_writep++ = "01zz"[m_sigs_oldvalp[code]
@ -250,7 +250,7 @@ public:
printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriBus (vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
m_sigs_oldvalp[code] = newval;
m_sigs_oldvalp[code+1] = newtri;
*m_writep++='b';
@ -261,7 +261,7 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriQuad (vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
*m_writep++='b';
@ -272,7 +272,7 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullTriArray (vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
m_sigs_oldvalp[code+word*2] = newvalp[word];
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
@ -286,18 +286,18 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
void fullDouble (vluint32_t code, const double newval);
void fullFloat (vluint32_t code, const float newval);
void fullDouble(vluint32_t code, const double newval);
void fullFloat(vluint32_t code, const float newval);
/// Inside dumping routines, dump one signal as unknowns
/// Presently this code doesn't change the oldval vector.
/// Thus this is for special standalone applications that after calling
/// fullBitX, must when then value goes non-X call fullBit.
inline void fullBitX (vluint32_t code) {
inline void fullBitX(vluint32_t code) {
*m_writep++='x'; printCode(code); *m_writep++='\n';
bufferCheck();
}
inline void fullBusX (vluint32_t code, int bits) {
inline void fullBusX(vluint32_t code, int bits) {
*m_writep++='b';
for (int bit=bits-1; bit>=0; --bit) {
*m_writep++='x';
@ -305,28 +305,28 @@ public:
*m_writep++=' '; printCode(code); *m_writep++='\n';
bufferCheck();
}
inline void fullQuadX (vluint32_t code, int bits) { fullBusX (code, bits); }
inline void fullArrayX (vluint32_t code, int bits) { fullBusX (code, bits); }
inline void fullQuadX(vluint32_t code, int bits) { fullBusX(code, bits); }
inline void fullArrayX(vluint32_t code, int bits) { fullBusX(code, bits); }
/// Inside dumping routines, dump one signal if it has changed
inline void chgBit (vluint32_t code, const vluint32_t newval) {
inline void chgBit(vluint32_t code, const vluint32_t newval) {
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
fullBit (code, newval);
fullBit(code, newval);
}
}
}
inline void chgBus (vluint32_t code, const vluint32_t newval, int bits) {
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
fullBus (code, newval, bits);
fullBus(code, newval, bits);
}
}
}
inline void chgQuad (vluint32_t code, const vluint64_t newval, int bits) {
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
@ -334,34 +334,34 @@ public:
}
}
}
inline void chgArray (vluint32_t code, const vluint32_t* newval, int bits) {
inline void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
if (VL_UNLIKELY(m_sigs_oldvalp[code+word] ^ newval[word])) {
fullArray (code,newval,bits);
fullArray(code,newval,bits);
return;
}
}
}
inline void chgTriBit (vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
| (m_sigs_oldvalp[code+1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
fullTriBit (code, newval, newtri);
fullTriBit(code, newval, newtri);
}
}
}
inline void chgTriBus (vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
| (m_sigs_oldvalp[code+1] ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
fullTriBus (code, newval, newtri, bits);
fullTriBus(code, newval, newtri, bits);
}
}
}
inline void chgTriQuad (vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
if (VL_UNLIKELY(diff)) {
@ -370,25 +370,25 @@ public:
}
}
}
inline void chgTriArray (vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
for (int word=0; word<(((bits-1)/32)+1); ++word) {
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
fullTriArray (code,newvalp,newtrip,bits);
fullTriArray(code,newvalp,newtrip,bits);
return;
}
}
}
inline void chgDouble (vluint32_t code, const double newval) {
inline void chgDouble(vluint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
fullDouble (code, newval);
fullDouble(code, newval);
}
}
inline void chgFloat (vluint32_t code, const float newval) {
inline void chgFloat(vluint32_t code, const float newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY((*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) != newval)) {
fullFloat (code, newval);
fullFloat(code, newval);
}
}
@ -431,23 +431,23 @@ public:
/// Flush dump
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); }
/// Write one cycle of dump data
void dump (vluint64_t timeui) { m_sptrace.dump(timeui); }
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.
void dump (double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump (vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump (int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// Set time units (s/ms, defaults to ns)
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
void set_time_unit (const char* unit) { m_sptrace.set_time_unit(unit); }
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); }
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
/// Set time resolution (s/ms, defaults to ns)
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
void set_time_resolution (const char* unit) { m_sptrace.set_time_resolution(unit); }
void set_time_resolution (const std::string& unit) { set_time_resolution(unit.c_str()); }
void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); }
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
/// Internal class access
inline VerilatedVcd* spTrace () { return &m_sptrace; };
inline VerilatedVcd* spTrace() { return &m_sptrace; };
};
#endif // guard

View File

@ -31,8 +31,8 @@
#if (SYSTEMC_VERSION>=20050714)
// SystemC 2.1.v1
// cppcheck-suppress unusedFunction
void VerilatedVcdSc::write_comment (const std::string &) {}
void VerilatedVcdSc::trace (const unsigned int &, const std::string &, const char **) {}
void VerilatedVcdSc::write_comment(const std::string &) {}
void VerilatedVcdSc::trace(const unsigned int &, const std::string &, const char **) {}
# define DECL_TRACE_METHOD_A(tp) \
void VerilatedVcdSc::trace( const tp& object, const std::string& name ) {}
@ -81,8 +81,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const std::string &, const cha
#elif (SYSTEMC_VERSION>20011000)
// SystemC 2.0.1
// cppcheck-suppress unusedFunction
void VerilatedVcdSc::write_comment (const sc_string &) {}
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
void VerilatedVcdSc::write_comment(const sc_string &) {}
void VerilatedVcdSc::trace(const unsigned int &, const sc_string &, const char **) {}
#define DECL_TRACE_METHOD_A(tp) \
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}
@ -124,8 +124,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char
#else
// SystemC 1.2.1beta
// cppcheck-suppress unusedFunction
void VerilatedVcdSc::write_comment (const sc_string &) {}
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
void VerilatedVcdSc::write_comment(const sc_string &) {}
void VerilatedVcdSc::trace(const unsigned int &, const sc_string &, const char **) {}
#define DECL_TRACE_METHOD_A(tp) \
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}

View File

@ -64,7 +64,7 @@ public:
// METHODS
/// Called by SystemC simulate()
virtual void cycle (bool delta_cycle) {
virtual void cycle(bool delta_cycle) {
# if (SYSTEMC_VERSION>20011000)
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
# else
@ -78,10 +78,10 @@ private:
#ifdef NC_SYSTEMC
// Cadence Incisive has these as abstract functions so we must create them
virtual void set_time_unit( int exponent10_seconds ) {} // deprecated
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
#endif
#if defined(NC_SYSTEMC) || (SYSTEMC_VERSION>=20111100)
virtual void set_time_unit( double v, sc_time_unit tu ) {}
virtual void set_time_unit(double v, sc_time_unit tu) {}
#endif
@ -89,12 +89,12 @@ private:
# if (SYSTEMC_VERSION>=20050714)
// SystemC 2.1.v1
# define DECL_TRACE_METHOD_A(tp) \
virtual void trace( const tp& object, const std::string& name );
virtual void trace(const tp& object, const std::string& name);
# define DECL_TRACE_METHOD_B(tp) \
virtual void trace( const tp& object, const std::string& name, int width );
virtual void trace(const tp& object, const std::string& name, int width);
virtual void write_comment (const std::string &);
virtual void trace (const unsigned int &, const std::string &, const char **);
virtual void write_comment(const std::string &);
virtual void trace(const unsigned int &, const std::string &, const char **);
#if (SYSTEMC_VERSION>=20171012)
DECL_TRACE_METHOD_A( sc_event )
@ -138,14 +138,14 @@ private:
# elif (SYSTEMC_VERSION>20011000)
// SystemC 2.0.1
# define DECL_TRACE_METHOD_A(tp) \
virtual void trace( const tp& object, const sc_string& name );
virtual void trace(const tp& object, const sc_string& name);
# define DECL_TRACE_METHOD_B(tp) \
virtual void trace( const tp& object, const sc_string& name, int width );
virtual void trace(const tp& object, const sc_string& name, int width);
virtual void write_comment (const sc_string &);
virtual void trace (const unsigned int &, const sc_string &, const char **);
virtual void delta_cycles (bool) {}
virtual void space( int n ) {}
virtual void write_comment(const sc_string &);
virtual void trace(const unsigned int &, const sc_string &, const char **);
virtual void delta_cycles(bool) {}
virtual void space(int n) {}
DECL_TRACE_METHOD_A( bool )
DECL_TRACE_METHOD_A( sc_bit )
@ -182,12 +182,12 @@ private:
# else
// SystemC 1.2.1beta
# define DECL_TRACE_METHOD_A(tp) \
virtual void trace( const tp& object, const sc_string& name );
virtual void trace(const tp& object, const sc_string& name);
# define DECL_TRACE_METHOD_B(tp) \
virtual void trace( const tp& object, const sc_string& name, int width );
virtual void trace(const tp& object, const sc_string& name, int width);
virtual void write_comment (const sc_string &);
virtual void trace (const unsigned int &, const sc_string &, const char **);
virtual void write_comment(const sc_string &);
virtual void trace(const unsigned int &, const sc_string &, const char **);
DECL_TRACE_METHOD_A( bool )
DECL_TRACE_METHOD_B( unsigned char )
@ -220,4 +220,4 @@ private:
# undef DECL_TRACE_METHOD_B
};
#endif // guard
#endif // Guard

View File

@ -157,21 +157,23 @@
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define VL_EQ_DELETE = delete
# define VL_HAS_UNIQUE_PTR
# define VL_HAS_UNORDERED_MAP
# define VL_HAS_UNORDERED_SET
# define vl_unique_ptr std::unique_ptr
// By default we use std:: types in C++11.
// Define VL_USE_UNORDERED_TYPES to test these pre-C++11 classes
# ifdef VL_USE_UNORDERED_TYPES
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
# else
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# endif
#else
# define VL_EQ_DELETE
# define vl_unique_ptr std::auto_ptr
# define vl_unordered_map std::map
# define vl_unordered_set std::set
# define VL_INCLUDE_UNORDERED_MAP <map>
# define VL_INCLUDE_UNORDERED_SET <set>
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
#endif
//=========================================================================

View File

@ -155,6 +155,221 @@ provided and documented in C<V3GraphAlg.cpp>.
=back
=head2 Multithreaded Mode
In --threads mode, the frontend of the Verilator pipeline is the same as
serial mode, up until V3Order.
V3Order builds a fine-grained, statement-level dependency graph that governs
the ordering of code within a single eval() call. In serial mode, that
dependency graph is used to order all statements into a total serial order.
In parallel mode, the same dependency graph is the starting point for a
partitioner (V3Partition).
The partitioner's goal is to coarsen the fine-grained DAG into a coarser
DAG, while maintaining as much available parallelism as possible. Often the
partitioner can transform an input graph with millions of nodes into a
coarsened execution graph with a few dozen nodes, while maintaining enough
parallelism to take advantage of a modern multicore CPU. Runtime
synchronization cost is not prohibitive with so few nodes.
=head3 Partitioning
Our partitioner is similar to the one Vivek Sarkar described in his 1989
paper "Partitioning and Scheduling Parallel Programs for Multiprocessors".
Let's define some terms:
=over 4
=item C<Par Factor>
The available parallelism or "par-factor" of a DAG is the total cost to
execute all nodes, divided by the cost to execute the longest critical path
through the graph. This is the speedup you would get from running the graph
in parallel, if given infinite CPU cores available and communication and
synchronization are zero.
=item C<Macro Task>
When the partitioner coarsens the graph, it combines nodes together. Each
fine-grained node represents an atomic "task"; combined nodes in the
coarsened graph are "macro-tasks". This term comes from Sarkar. Each
macro-task executes from start to end on one processor, without any
synchronization to any other macro-task during its
execution. (Synchronization only happens before the macro-task begins or
after it ends.)
=item C<Edge Contraction>
Our partitioner, like Sarkar's, primarily relies on "edge contraction" to
coarsen the graph. It starts with one macro-task per atomic task and
iteratively combines pairs of edge-connected macro-tasks.
=item C<Local Critical Path>
Each node in the graph has a "local" critical path. That's the critical
path from the start of the graph to the start of the node, plus the node's
cost, plus the critical path from the end of the node to the end of the
graph.
=back
Sarkar calls out an important trade-off: coarsening the graph reduces
runtime synchronization overhead among the macro-tasks, but it tends to
increase the critical path through the graph and thus reduces par-factor.
Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such
that the growth in critical path is minimized. Each candidate merge would
result in a new node, which would have some local critical path. We choose
the candidate that would produce the shortest local critical path. Repeat
until par-factor falls to a target threshold. It's a greedy algorithm, and
it's not guaranteed to produce the best partition (which Sarkar proves is
NP-hard).
=head3 Estimating Logic Costs
To compute the cost of any given path through the graph, Verilator
estimates an execution cost for each task. Each macro-task has an execution
cost which is simply the sum of its tasks' costs. We assume that
communication overhead and synchronization overhead are zero, so the cost
of any given path through the graph is simply the sum of macro-task
execution costs. Sarkar does almost the same thing, except that he has
nonzero estimates for synchronization costs.
Verilator's cost estimates are assigned by the InstrCountCostVisitor. This
class is perhaps the most fragile piece of the multithread implementation.
It's easy to have a bug where you count something cheap (eg. accessing one
element of a huge array) as if it were expensive (eg. by counting it as if
it were an access to the entire array.) Even without such gross bugs, the
estimates this produce are only loosely predictive of actual runtime cost.
Multithread performance would be better with better runtime costs
estimates. This is an area to improve.
=head3 Scheduling Macro-Tasks at Runtime
After coarsening the graph, we must schedule the macro-tasks for runtime.
Sarkar describes two options: you can dynamically schedule tasks at
runtime, with a runtime graph follower. Sarkar calls this the
"macro-dataflow model." Verilator does not support this; early experiments
with this approach had poor performance.
The other option is to statically assign macro-tasks to threads, with each
thread running its macro-tasks in a static order. Sarkar describes this in
Chapter 5. Verilator takes this static approach. The only dynamic aspect is
that each macro task may block before starting, to wait until its
prerequisites on other threads have finished.
The synchronization cost is cheap if the prereqs are done. If they're not,
fragmentation (idle CPU cores waiting) is possible. This is the major
source of overhead in this approach. The --prof-threads switch and the
C<verilator_gantt> script can visualize the time lost to such
fragmentation.
=head3 Locating Variables for Best Spatial Locality
After scheduling all code, we attempt to locate variables in memory such
that variables accessed by a single macro-task are close together in
memory. This provides "spatial locality" -- when we pull in a 64-byte
cache line to access a 2-byte variable, we want the other 62 bytes to be
ones we'll also likely access soon, for best cache performance.
This turns out to be critical for performance. It should allow Verilator
to scale to very large models. We don't rely on our working set fitting
in any CPU cache; instead we essentially "stream" data into caches from
memory. It's not literally streaming, where the address increases
monotonically, but it should have similar performance characteristics,
so long as each macro-task's dataset fits in one core's local caches.
To achieve spatial locality, we tag each variable with the set of
macro-tasks that access it. Let's call this set the "footprint" of that
variable. The variables in a given module have a set of footprints. We can
order those footprints to minimize the distance between them (distance is
the number of macro-tasks that are different across any two footprints) and
then emit all variables into the struct in ordered-footprint order.
The footprint ordering is literally the traveling salesman problem, and we
use a TSP-approximation algorithm to get close to an optimal sort.
This is an old idea. Simulators designed at DEC in the early 1990s used
similar techniques to optimize both single-thread and multi-thread modes.
(Verilator does not optimize variable placement for spatial locality in
serial mode; that is a possible area for improvement.)
=head3 Improving Multithreaded Performance Further (a TODO list)
=over 4
=item C<Wave Scheduling>
To allow the verilated model to run in parallel with the testbench, it
might be nice to support "wave" scheduling, in which work on a cycle begins
before eval() is called or continues after eval() returns. For now all
work on a cycle happens during the eval() call, leaving Verilator's threads
idle while the testbench (everything outside eval()) is working. This would
involve fundamental changes within the partitioner, however, it's probably
the best bet for hiding testbench latency.
=item C<Efficient Dynamic Scheduling>
To scale to more than a few threads, we may revisit a fully dynamic
scheduler. For large (>16 core) systems it might make sense to dedicate an
entire core to scheduling, so that scheduler data structures would fit in
its L1 cache and thus the cost of traversing priority-ordered ready lists
would not be prohibitive.
=item C<Static Scheduling with Runtime Repack>
We could modify the static scheduling approach by gathering actual
macro-task execution times at run time, and dynamically re-packing the
macro-tasks into the threads also at run time. Say, re-pack once every
10,000 cycles or something. This has the potential to do better than our
static estimates about macro-task run times. It could potentially react to
CPU cores that aren't performing equally, due to NUMA or thermal throttling
or nonuniform competing memory traffic or whatever.
=item C<Clock Domain Balancing>
Right now Verilator makes no attempt to balance clock domains across
macro-tasks. For a multi-domain model, that could lead to bad gantt chart
fragmentation. This could be improved if it's a real problem in practice.
=item C<Other Forms of MTask Balancing>
The largest source of runtime overhead is idle CPUs, which happens due to
variance between our predicted runtime for each MTask and its actual
runtime. That variance is magnified if MTasks are homogeneous, containing
similar repeating logic which was generally close together in source code
and which is still packed together even after going through Verilator's
digestive tract.
If Verilator could avoid doing that, and instead would take source logic
that was close together and distribute it across MTasks, that would
increase the diversity of any given MTask, and this should reduce variance
in the cost estimates.
One way to do that might be to make various "tie breaker" comparison
routines in the sources to rely more heavily on randomness, and generally
try harder not to keep input nodes together when we have the option to
scramble things.
=item C<Performance Regression>
It would be nice if we had a regression of large designs, with some
diversity of design styles, to test on both single- and multi-threaded
modes. This would help to avoid performance regressions, and also to
evaluate the optimizations while minimizing the impact of parasitic noise.
=item C<Per-Instance Classes>
If we have multiple instances of the same module, and they partition
differently (likely; we make no attempt to partition them the same) then
the variable sort will be suboptimal for either instance. A possible
improvement would be to emit a unique class for each instance of a module,
and sort its variables optimally for that instance's code stream.
=back
=head2 Verilated Flow
The evaluation loop outputted by Verilator is designed to allow a single
@ -268,8 +483,8 @@ technique lifted from graph traversal packages).
A visitor first clears the one it wants to use by calling
C<AstNode::user#ClearTree()>, then it can mark any node's user() with whatever
data it wants. Readers just call C<< nodep->user() >>, but may need to cast
appropriately, so you'll often see C<< nodep->userp()->castSOMETYPE() >>. At
the top of each visitor are comments describing how the C<user()> stuff
appropriately, so you'll often see C<< VN_CAST(nodep->userp(), SOMETYPE) >>.
At the top of each visitor are comments describing how the C<user()> stuff
applies to that visitor class. For example:
// NODE STATE
@ -299,16 +514,13 @@ as it slows the program down to have to pass vup everywhere.)
=head2 Iterators
C<AstNode> provides a set of iterators to facilitate walking over the
tree. Each takes two arguments, a visitor, C<v>, of type C<AstNVisitor> and
an optional pointer user data, C<vup>, of type C<AstNUser*>. The second is
one of the ways to pass parameters to visitors described in L</Visitor
Functions>, but its use is now deprecated and should I<not> be used for new
visitor classes.
C<AstNVisitor> provides a set of iterators to facilitate walking over the
tree. Each operates on the current C<AstNVisitor> class (as this) and takes
an argument type C<AstNode*>.
=over 4
=item C<iterate()>
=item C<iterate>
This just applies the C<accept> method of the C<AstNode> to the visitor
function.
@ -349,9 +561,9 @@ to be aware that calling iteration may cause the children to change. For
example:
// nodep->lhsp() is 0x1234000
nodep->lhsp()->iterateAndNext(...); // and under covers nodep->lhsp() changes
iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes
// nodep->lhsp() is 0x5678400
nodep->lhsp()->iterateAndNext(...);
iterateAndNextNull(nodep->lhsp());
Will work fine, as even if the first iterate causes a new node to take the
place of the lhsp(), that edit will update nodep->lhsp() and the second
@ -359,9 +571,9 @@ call will correctly see the change. Alternatively:
lp = nodep->lhsp();
// nodep->lhsp() is 0x1234000, lp is 0x1234000
lp->iterateAndNext(...); **lhsp=NULL;** // and under covers nodep->lhsp() changes
iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes
// nodep->lhsp() is 0x5678400, lp is 0x1234000
lp->iterateAndNext(...);
iterateAndNextNull(lp);
This will cause bugs or a core dump, as lp is a dangling pointer. Thus it
is advisable to set lhsp=NULL shown in the *'s above to make sure these
@ -370,7 +582,7 @@ V3Width is to use acceptSubtreeReturnEdits, which operates on a single node
and returns the new pointer if any. Note acceptSubtreeReturnEdits does not
follow nextp() links.
lp = lp->acceptSubtreeReturnEdits()
lp = acceptSubtreeReturnEdits(lp)
=head2 Identifying derived classes
@ -379,16 +591,16 @@ dealing with. For example a visitor might not implement separate C<visit>
methods for C<AstIf> and C<AstGenIf>, but just a single method for the base
class:
void visit (AstNodeIf* nodep, AstNUser* vup)
void visit (AstNodeIf* nodep)
However that method might want to specify additional code if it is called
for C<AstGenIf>. Verilator does this by providing a C<castSOMETYPE()>
for C<AstGenIf>. Verilator does this by providing a C<VN_CAST>
method for each possible node type, using C++ C<dynamic_cast>. This either
returns a pointer to the object cast to that type (if it is of class
C<SOMETYPE>, or a derived class of C<SOMETYPE>) or else NULL. So our
C<visit> method could use:
if (nodep->castAstGenIf()) {
if (VN_CAST(nodep, AstGenIf) {
<code specific to AstGenIf>
}

View File

@ -36,7 +36,6 @@ sub test {
-r "nodist/install_test" or die "%Error: Run from the top of the verilator kit,";
cleanenv();
$ENV{VERILATOR_NO_OPT_BUILD} = 1; # Don't build optimized executables; just slows this down
run("make distclean") if -r "Makefile";
# Try building from a scratch area
@ -59,11 +58,12 @@ sub test {
run("/bin/mkdir -p $prefix");
run("cd $blddir && make install");
run("test -e $prefix/share/man/man1/verilator.1");
run("test -e $prefix/share/verilator/examples/test_c/Makefile");
run("test -e $prefix/share/verilator/examples/tracing_c/Makefile");
run("test -e $prefix/share/verilator/include/verilated.h");
run("test -e $prefix/bin/verilator");
run("test -e $prefix/bin/verilator_bin");
run("test -e $prefix/bin/verilator_bin_dbg");
run("test -e $prefix/bin/verilator_gantt");
run("test -e $prefix/bin/verilator_profcfunc");
}
@ -92,6 +92,10 @@ sub test {
run("cd $dir/obj_dir && make -f Vfoo.mk");
run("cd $dir/obj_dir && ./Vfoo");
}
if ($Opt_Stage <= 9) {
print "*-* All Finished *-*\n";
}
}
sub write_verilog {
@ -116,7 +120,8 @@ sub write_verilog {
sub cleanenv {
foreach my $var (keys %ENV) {
if ($var eq "VERILATOR_ROOT"
|| $var eq "VERILATOR_INCLUDE") {
|| $var eq "VERILATOR_INCLUDE"
|| $var eq "VERILATOR_NO_OPT_BUILD") {
print "unset $var # Was '$ENV{$var}'\n";
delete $ENV{$var}
}

View File

@ -22,9 +22,10 @@
#### Start of system configuration section. ####
srcdir = @srcdir@
VPATH = @srcdir@
PERL = @PERL@
EXEEXT = @EXEEXT@
# VPATH only does sources; fix install_test's not creating ../bin
vpath %.h @srcdir@
#### End of system configuration section. ####
@ -42,9 +43,11 @@ UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
#*********************************************************************
obj_opt:
mkdir $@
mkdir -p $@
obj_dbg:
mkdir $@
mkdir -p $@
../bin:
mkdir -p $@
.SUFFIXES:
@ -52,23 +55,23 @@ obj_dbg:
opt: ../bin/verilator_bin
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
../bin/verilator_bin: ../bin/verilator_bin_dbg
../bin/verilator_bin: ../bin ../bin/verilator_bin_dbg
-rm -rf $@ $@.exe
-cp -p $<$(EXEEXT) $@$(EXEEXT)
else
../bin/verilator_bin: obj_opt prefiles
cd obj_opt && $(MAKE) -j 1 TGT=../$@ -f ../Makefile_obj serial
cd obj_opt && $(MAKE) TGT=../$@ -f ../Makefile_obj
../bin/verilator_bin: 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 prefiles
cd obj_dbg && $(MAKE) -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
../bin/verilator_bin_dbg: 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 prefiles
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
../bin/verilator_coverage_bin_dbg: 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
prefiles::
prefiles:: config_rev.h

View File

@ -78,7 +78,7 @@ ifeq ($(VL_DEBUG),)
COPT = -O2
else
# Debug
COPT = -ggdb -DVL_DEBUG
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
# Debug & Profile:
#LDFLAGS += -pg -g
#COPT = -ggdb -pg -g
@ -93,7 +93,7 @@ endif
LIBS = -lm -lstdc++
CPPFLAGS += -MMD
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir)
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
CPPFLAGS += -DYYDEBUG # Required to get nice error messages
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += $(COPT)
@ -195,10 +195,12 @@ RAW_OBJS = \
V3GraphAlg.o \
V3GraphAcyc.o \
V3GraphDfa.o \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hashed.o \
V3Inline.o \
V3Inst.o \
V3InstrCount.o \
V3Life.o \
V3LifePost.o \
V3LinkCells.o \
@ -215,9 +217,12 @@ RAW_OBJS = \
V3Order.o \
V3Os.o \
V3Param.o \
V3Partition.o \
V3PreShell.o \
V3Premit.o \
V3Reloop.o \
V3Scope.o \
V3Scoreboard.o \
V3Slice.o \
V3Split.o \
V3SplitAs.o \
@ -230,6 +235,7 @@ RAW_OBJS = \
V3Trace.o \
V3TraceDecl.o \
V3Tristate.o \
V3TSP.o \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \

View File

@ -44,6 +44,8 @@
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3Const.h"
#include "V3SenTree.h" // for SenTreeSet
#include VL_INCLUDE_UNORDERED_MAP
//***** See below for main transformation engine
@ -52,11 +54,7 @@
class ActiveBaseVisitor : public AstNVisitor {
protected:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
class ActiveNamer : public ActiveBaseVisitor {
@ -66,7 +64,11 @@ private:
AstScope* m_scopep; // Current scope to add statement to
AstActive* m_iActivep; // For current scope, the IActive we're building
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
vector<AstActive*> m_activeVec; // List of sensitive actives, for folding
SenTreeSet m_activeSens; // Sen lists for each active we've made
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
ActiveMap m_activeMap; // Map sentree to active, for folding.
// METHODS
void addActive(AstActive* nodep) {
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
@ -77,8 +79,9 @@ private:
m_scopep = nodep;
m_iActivep = NULL;
m_cActivep = NULL;
m_activeVec.clear();
nodep->iterateChildren(*this);
m_activeSens.clear();
m_activeMap.clear();
iterateChildren(nodep);
// Don't clear scopep, the namer persists beyond this visit
}
virtual void visit(AstSenTree* nodep) {
@ -91,7 +94,7 @@ private:
// Default
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// METHODS
public:
@ -116,22 +119,15 @@ public:
}
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
// Return a sentree in this scope that matches given sense list.
// Not the fastest, but scopes tend to have few clocks
AstActive* activep = NULL;
//sitemsp->dumpTree(cout," Lookingfor: ");
for (vector<AstActive*>::iterator it = m_activeVec.begin(); it!=m_activeVec.end(); ++it) {
activep = *it;
if (activep) { // Not deleted
// Compare the list
AstSenTree* asenp = activep->sensesp();
if (asenp->sameTree(sensesp)) {
UINFO(8," Found ACTIVE "<<activep<<endl);
goto found;
AstSenTree* activeSenp = m_activeSens.find(sensesp);
if (activeSenp) {
ActiveMap::iterator it = m_activeMap.find(activeSenp);
UASSERT(it != m_activeMap.end(), "Corrupt active map");
activep = it->second;
}
}
activep = NULL;
}
found:
// Not found, form a new one
if (!activep) {
AstSenTree* newsenp = sensesp->cloneTree(false);
@ -140,7 +136,8 @@ public:
UINFO(8," New ACTIVE "<<activep<<endl);
// Form the sensitivity list
addActive(activep);
m_activeVec.push_back(activep);
m_activeMap[newsenp] = activep;
m_activeSens.add(newsenp);
// Note actives may have also been added above in the Active visitor
}
return activep;
@ -154,7 +151,7 @@ public:
}
virtual ~ActiveNamer() {}
void main(AstScope* nodep) {
nodep->accept(*this);
iterate(nodep);
}
};
@ -193,7 +190,7 @@ private:
if (m_check == CT_SEQ) {
AstNode* las = m_assignp;
m_assignp = nodep;
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
m_assignp = las;
}
}
@ -217,7 +214,7 @@ private:
}
//--------------------
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -225,7 +222,7 @@ public:
m_alwaysp = nodep;
m_check = check;
m_assignp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~ActiveDlyVisitor() {}
};
@ -252,7 +249,7 @@ private:
// Clear last scope's names, and collect this scope's existing names
m_namer.main(nodep);
m_scopeFinalp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) {
// Actives are being formed, so we can ignore any already made
@ -314,8 +311,8 @@ private:
// Move always to appropriate ACTIVE based on its sense list
if (oldsensesp
&& oldsensesp->sensesp()
&& oldsensesp->sensesp()->castSenItem()
&& oldsensesp->sensesp()->castSenItem()->isNever()) {
&& VN_IS(oldsensesp->sensesp(), SenItem)
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
// Never executing. Kill it.
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
@ -325,7 +322,7 @@ private:
// Read sensitivitues
m_itemCombo = false;
m_itemSequent = false;
if (oldsensesp) oldsensesp->iterateAndNext(*this);
iterateAndNextNull(oldsensesp);
bool combo = m_itemCombo;
bool sequent = m_itemSequent;
@ -399,7 +396,7 @@ private:
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
nodep->v3fatalSrc("Strange activity type under SenGate");
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstSenItem* nodep) {
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
@ -421,7 +418,7 @@ private:
virtual void visit(AstVarScope* nodep) {}
//--------------------
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -429,7 +426,7 @@ public:
m_scopeFinalp = NULL;
m_itemCombo = false;
m_itemSequent = false;
nodep->accept(*this);
iterate(nodep);
}
virtual ~ActiveVisitor() {}
};

View File

@ -58,24 +58,20 @@ private:
SenTreeFinder m_finder; // Find global sentree's and add them
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstTopScope* nodep) {
m_topscopep = nodep;
m_finder.main(m_topscopep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_topscopep = NULL;
}
virtual void visit(AstNodeModule* nodep) {
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4," MOD "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) {
UINFO(4," ACTIVE "<<nodep<<endl);
@ -83,8 +79,8 @@ private:
AstSenTree* sensesp = nodep->sensesp();
if (!sensesp) nodep->v3fatalSrc("NULL");
if (sensesp->sensesp()
&& sensesp->sensesp()->castSenItem()
&& sensesp->sensesp()->castSenItem()->isNever()) {
&& VN_IS(sensesp->sensesp(), SenItem)
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
// Never executing. Kill it.
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
@ -118,7 +114,7 @@ private:
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
//nodep->iterateChildren(*this);
//iterateChildren(nodep);
}
virtual void visit(AstInitial* nodep) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
@ -143,13 +139,13 @@ private:
virtual void visit(AstVarScope* nodep) {}
//--------------------
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit ActiveTopVisitor(AstNetlist* nodep) {
m_topscopep = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~ActiveTopVisitor() {}
};

View File

@ -112,13 +112,13 @@ private:
AstNode* bodysp = NULL;
bool selfDestruct = false;
AstIf* ifp = NULL;
if (AstPslCover* snodep = nodep->castPslCover()) {
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
++m_statAsCover;
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* covincp = snodep->coverincp()->castCoverInc();
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
covincp->unlinkFrBack();
if (message!="") covincp->declp()->comment(message);
@ -129,7 +129,7 @@ private:
ifp = new AstIf (nodep->fileline(), propp, bodysp, NULL);
bodysp = ifp;
} else if (nodep->castPslAssert()) {
} else if (VN_IS(nodep, PslAssert)) {
++m_statAsPsl;
// Insert an automatic error message and $stop after
// any user-supplied statements.
@ -170,7 +170,7 @@ private:
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
//
if (nodep->castVAssert()) {
if (VN_IS(nodep, VAssert)) {
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
} else {
@ -179,7 +179,7 @@ private:
AstIf* ifp = new AstIf (nodep->fileline(), propp, passsp, failsp);
AstNode* newp = ifp;
if (nodep->castVAssert()) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
//
// Install it
nodep->replaceWith(newp);
@ -197,14 +197,14 @@ private:
// If this statement ends with 'else if', then nextIf will point to the
// nextIf statement. Otherwise it will be null.
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
ifp->condp()->iterateAndNext(*this);
iterateAndNextNull(ifp->condp());
// Recurse into the true case.
ifp->ifsp()->iterateAndNext(*this);
iterateAndNextNull(ifp->ifsp());
// If the last else is not an else if, recurse into that too.
if (ifp->elsesp() && !nextifp) {
ifp->elsesp()->iterateAndNext(*this);
iterateAndNextNull(ifp->elsesp());
}
// Build a bitmask of the true predicates
@ -242,16 +242,16 @@ private:
nodep->replaceWith(checkifp);
pushDeletep(nodep);
} else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
// VISITORS //========== Case assertions
virtual void visit(AstCase* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default=false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) has_default=true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
@ -270,7 +270,7 @@ private:
// Not parallel, but harmlessly so.
} else {
AstNode* propp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
AstNode* onep;
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
@ -306,7 +306,7 @@ private:
// VISITORS //========== Statements
virtual void visit(AstDisplay* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Replace the special types with standard text
if (nodep->displayType()==AstDisplayType::DT_INFO) {
replaceDisplay(nodep, "-Info");
@ -319,13 +319,13 @@ private:
}
virtual void visit(AstNodePslCoverOrAssert* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
}
virtual void visit(AstVAssert* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
++m_statAsSV;
}
@ -333,7 +333,7 @@ private:
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
//
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Reset defaults
m_modp = NULL;
}
@ -343,13 +343,13 @@ private:
AstBegin* lastp = m_beginp;
{
m_beginp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_beginp = lastp;
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
@ -357,7 +357,7 @@ public:
m_beginp = NULL;
m_modp = NULL;
// Process
nodep->accept(*this);
iterate(nodep);
}
virtual ~AssertVisitor() {
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);

View File

@ -49,11 +49,7 @@ private:
AstNodeSenItem* m_senip; // Last sensitivity
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
AstSenTree* newSenTree(AstNode* nodep) {
// Create sentree based on clocked or default clock
@ -89,12 +85,12 @@ private:
virtual void visit(AstNodePslCoverOrAssert* nodep) {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
}
virtual void visit(AstPslClocked* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
}
@ -112,12 +108,12 @@ private:
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstNodeModule* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Reset defaults
m_seniDefaultp = NULL;
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -126,7 +122,7 @@ public:
m_seniDefaultp = NULL;
clearAssertInfo();
// Process
nodep->accept(*this);
iterate(nodep);
}
virtual ~AssertPreVisitor() {}
};

View File

@ -58,7 +58,7 @@ int AstNodeDType::s_uniqueNum = 0;
//######################################################################
// V3AstType
ostream& operator<<(ostream& os, AstType rhs);
std::ostream& operator<<(std::ostream& os, AstType rhs);
//######################################################################
// Creators
@ -290,8 +290,7 @@ void AstNode::addNextHere(AstNode* newp) {
// Add to m_nextp on exact node passed, not at the end.
// This could be at head, tail, or both (single)
// New could be head of single node, or list
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
UASSERT(newp, "Null item passed to addNext");
UASSERT(newp,"Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
this->debugTreeChange("-addHereThs: ", __LINE__, false);
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
@ -418,7 +417,7 @@ void AstNode::replaceWith(AstNode* newp) {
repHandle.relink(newp);
}
void AstNRelinker::dump(ostream& str) const {
void AstNRelinker::dump(std::ostream& str) const {
str<<" BK="<<(uint32_t*)m_backp;
str<<" ITER="<<(uint32_t*)m_iterpp;
str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":"");
@ -646,7 +645,6 @@ AstNode* AstNode::cloneTreeIterList() {
}
AstNode* AstNode::cloneTree(bool cloneNextLink) {
if (!this) return NULL; // verilog.y relies on this
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
cloneClearTree();
AstNode* newp;
@ -802,7 +800,6 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
}
void AstNode::iterateListBackwards(AstNVisitor& v) {
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
AstNode* nodep=this;
while (nodep->m_nextp) nodep=nodep->m_nextp;
while (nodep) {
@ -822,13 +819,13 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v) {
void AstNode::iterateAndNextConst(AstNVisitor& v) {
// Keep following the current list even if edits change it
if (!this) return; // A few cases could be cleaned up, but want symmetry with iterateAndNext
for (AstNode* nodep=this; nodep; ) { // effectively: if (!this) return; // Callers rely on this
AstNode* nodep=this;
do {
AstNode* nnextp = nodep->m_nextp;
ASTNODE_PREFETCH(nnextp);
nodep->accept(v);
nodep = nnextp;
}
} while (nodep);
}
AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
@ -839,7 +836,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
// To solve this, this function returns the pointer to the replacement node,
// which in many cases is just the same node that was passed in.
AstNode* nodep = this; // Note "this" may point to bogus point later in this function
if (nodep->castNetlist()) {
if (VN_IS(nodep, Netlist)) {
// Calling on top level; we know the netlist won't get replaced
nodep->accept(v);
} else if (!nodep->backp()) {
@ -886,7 +883,7 @@ void AstNode::cloneRelinkTree() {
//======================================================================
// Comparison
bool AstNode::gateTreeIter() {
bool AstNode::gateTreeIter() const {
// private: Return true if the two trees are identical
if (!isGateOptimizable()) return false;
if (m_op1p && !m_op1p->gateTreeIter()) return false;
@ -896,7 +893,7 @@ bool AstNode::gateTreeIter() {
return true;
}
bool AstNode::sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool gateOnly) {
bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly) {
// private: Return true if the two trees are identical
if (!node1p && !node2p) return true;
if (!node1p || !node2p) return false;
@ -917,9 +914,9 @@ bool AstNode::sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool
//======================================================================
// Static utilities
ostream& operator<<(ostream& os, const V3Hash& rhs) {
return os<<hex<<setw(2)<<setfill('0')<<rhs.depth()
<<"_"<<setw(6)<<setfill('0')<<rhs.hshval();
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) {
@ -938,7 +935,7 @@ void AstNode::checkTreeIter(AstNode* backp) {
if (backp != this->backp()) {
this->v3fatalSrc("Back node inconsistent");
}
if (castNodeTermop() || castNodeVarRef()) {
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
// Termops have a short-circuited iterateChildren, so check usage
if (op1p()||op2p()||op3p()||op4p())
this->v3fatalSrc("Terminal operation with non-terminals");
@ -975,17 +972,14 @@ void AstNode::checkTree() {
}
void AstNode::dumpGdb() { // For GDB only
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
dumpGdbHeader();
cout<<" "; dump(cout); cout<<endl;
}
void AstNode::dumpGdbHeader() const { // For GDB only
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
dumpPtrs(cout);
cout<<" Fileline = "<<fileline()<<endl;
}
void AstNode::dumpTreeGdb() { // For GDB only
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
dumpGdbHeader();
dumpTree(cout);
}
@ -1002,7 +996,7 @@ void AstNode::checkIter() const {
}
}
void AstNode::dumpPtrs(ostream& os) const {
void AstNode::dumpPtrs(std::ostream& os) const {
os<<"This="<<typeName()<<" "<<(void*)this;
os<<" back="<<(void*)backp();
if (nextp()) os<<" next="<<(void*)nextp();
@ -1024,7 +1018,7 @@ void AstNode::dumpPtrs(ostream& os) const {
os<<endl;
}
void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) {
os<<indent<<" "<<this<<endl;
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
if (maxDepth==1) {
@ -1037,7 +1031,7 @@ void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
}
}
void AstNode::dumpTreeAndNext(ostream& os, const string& indent, int maxDepth) {
void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDepth) {
// Audited to make sure this is never NULL
for (AstNode* nodep=this; nodep; nodep=nodep->nextp()) {
nodep->dumpTree(os, indent, maxDepth);
@ -1050,8 +1044,8 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
UINFO(2,"Dumping "<<filename<<endl);
const vl_unique_ptr<std::ofstream> logsp (V3File::new_ofstream(filename, append));
if (logsp->fail()) v3fatal("Can't write "<<filename);
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<dec<<editCountLast()<<">";
*logsp<<" to <e"<<dec<<editCountGbl()<<">"<<endl;
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<std::dec<<editCountLast()<<">";
*logsp<<" to <e"<<std::dec<<editCountGbl()<<">"<<endl;
if (editCountGbl()==editCountLast()
&& !(v3Global.opt.dumpTree()>=9)) {
*logsp<<endl;
@ -1066,23 +1060,19 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
checkTree();
// Broken isn't part of check tree because it can munge iterp's
// set by other steps if it is called in the middle of other operations
if (AstNetlist* netp=this->castNetlist()) V3Broken::brokenAll(netp);
if (AstNetlist* netp=VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
}
// Next dump can indicate start from here
editCountSetLast();
}
void AstNode::v3errorEndFatal(ostringstream& str) const { v3errorEnd(str); assert(0); }
void AstNode::v3errorEndFatal(std::ostringstream& str) const { v3errorEnd(str); assert(0); }
void AstNode::v3errorEnd(ostringstream& str) const {
if (!dynamic_cast<const AstNode*>(this)) {
// No known cases cause this, but better than a core dump
if (debug()) UINFO(0, "-node: NULL. Please report this along with a --gdbbt backtrace as a Verilator bug.\n");
V3Error::v3errorEnd(str);
} else if (!m_fileline) {
void AstNode::v3errorEnd(std::ostringstream& str) const {
if (!m_fileline) {
V3Error::v3errorEnd(str);
} else {
ostringstream nsstr;
std::ostringstream nsstr;
nsstr<<str.str();
if (debug()) {
nsstr<<endl;
@ -1153,7 +1143,7 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
// AstNVisitor
void AstNVisitor::doDeletes() {
for (vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
for (std::vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
(*it)->deleteTree();
}
m_deleteps.clear();

View File

@ -29,19 +29,36 @@
#include <vector>
#include <cmath>
#include <map>
#include VL_INCLUDE_UNORDERED_SET
#include "V3Ast__gen_classes.h" // From ./astgen
// Things like:
// class V3AstNode;
// Forward declarations
class V3Graph;
class ExecMTask;
// Hint class so we can choose constructors
class VFlagLogicPacked {};
class VFlagBitPacked {};
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
// Used as key for another map, needs operator<, hence not an unordered_set
typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
//######################################################################
// For broken() function, return error string if have a match
#define BROKEN_RTN(test) do { if (VL_UNLIKELY(test)) return # test; } while(0)
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
#define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep))
// (V)erilator (N)ode cast: Cast to given type if can; effectively dynamic_cast(nodep)(nodetypename)
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
//######################################################################
class AstType {
@ -61,7 +78,7 @@ public:
inline bool operator== (AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
//######################################################################
@ -107,7 +124,7 @@ public:
inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
//######################################################################
@ -497,7 +514,7 @@ public:
inline bool operator== (AstVarType lhs, AstVarType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstVarType lhs, AstVarType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstVarType::en lhs, AstVarType rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
//######################################################################
@ -529,7 +546,7 @@ public:
inline bool operator== (AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
//######################################################################
@ -562,7 +579,7 @@ public:
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstVarAttrClocker::en lhs, AstVarAttrClocker rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
//######################################################################
@ -661,7 +678,7 @@ public:
inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstParseRefExp lhs, AstParseRefExp::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
//######################################################################
// VNumRange - Structure containing numberic range information
@ -715,9 +732,9 @@ struct VNumRange {
int hiMaxSelect() const { return (lo()<0 ? hi()-lo() : hi()); } // Maximum value a [] select may index
bool representableByWidth() const // Could be represented by just width=1, or [width-1:0]
{ return (!m_ranged || (m_lo==0 && m_hi>=1 && !m_littleEndian)); }
void dump(ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
void dump(std::ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
};
inline ostream& operator<<(ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
//######################################################################
@ -891,10 +908,12 @@ public:
class AstNVisitor {
private:
vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
// MEMBERS
std::vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
protected:
friend class AstNode;
public:
// METHODS
/// At the end of the visitor (or doDeletes()), delete this pushed node
/// along with all children and next(s). This is often better to use
/// than an immediate deleteTree, as any pointers into this node will
@ -908,6 +927,21 @@ public:
virtual ~AstNVisitor() {
doDeletes();
}
/// Call visit()s on nodep
void iterate(AstNode* nodep);
/// Call visit()s on nodep's children
void iterateChildren(AstNode* nodep);
/// Call visit()s on nodep's children in backp() order
void iterateChildrenBackwards(AstNode* nodep);
/// Call visit()s on const nodep's children
void iterateChildrenConst(AstNode* nodep);
/// Call visit()s on nodep (maybe NULL) and nodep's nextp() list
void iterateAndNextNull(AstNode* nodep);
/// Call visit()s on const nodep (maybe NULL) and nodep's nextp() list
void iterateAndNextConstNull(AstNode* nodep);
/// Return edited nodep; see comments in V3Ast.cpp
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
#include "V3Ast__gen_visitor.h" // From ./astgen
// Things like:
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
@ -932,9 +966,9 @@ public:
AstNRelinker() { m_oldp=NULL; m_backp=NULL; m_chg=RELINK_BAD; m_iterpp=NULL;}
void relink(AstNode* newp);
AstNode* oldp() const { return m_oldp; }
void dump(ostream& str=cout) const;
void dump(std::ostream& str=std::cout) const;
};
inline ostream& operator<<(ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
//######################################################################
// V3Hash -- Node hashing for V3Combine
@ -984,7 +1018,7 @@ public:
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
setBoth(1,((h1.hshval()*31+h2.hshval())*31+h3.hshval())*31+h4.hshval()); }
};
ostream& operator<<(ostream& os, const V3Hash& rhs);
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
//######################################################################
// AstNode -- Base type of all Ast types
@ -1042,14 +1076,13 @@ class AstNode {
void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; }
void init(); // initialize value of AstNode
void iterateListBackwards(AstNVisitor& v);
private:
AstNode* cloneTreeIter();
AstNode* cloneTreeIterList();
void checkTreeIter(AstNode* backp);
void checkTreeIterList(AstNode* backp);
bool gateTreeIter();
bool sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool gateOnly);
bool gateTreeIter() const;
static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly);
void deleteTreeIter();
void deleteNode();
public:
@ -1241,11 +1274,11 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a NULL
bool isZero();
bool isOne();
bool isNeqZero();
bool isAllOnes();
bool isAllOnesV(); // Verilog width rules apply
bool isZero() const;
bool isOne() const;
bool isNeqZero() const;
bool isAllOnes() const;
bool isAllOnesV() const; // Verilog width rules apply
// METHODS - data type changes especially for initial creation
void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } }
@ -1276,10 +1309,10 @@ public:
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(ostringstream& str) const;
void v3errorEndFatal(ostringstream& str) const VL_ATTR_NORETURN;
void v3errorEnd(std::ostringstream& str) const;
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN;
string warnMore() const;
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
void dumpGdb(); // For GDB only
void dumpGdbHeader() const;
@ -1303,19 +1336,21 @@ public:
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument
// METHODS - Iterate on a tree
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) { // Clone or return NULL if NULL
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; }
AstNode* cloneTree(bool cloneNextLink);
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
bool sameGateTree(AstNode* node2p); // Does tree of this == node2p?, not allowing non-isGateOptimizable
bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
bool sameGateTree(const AstNode* node2p) const; // Does tree of this == node2p?, not allowing non-isGateOptimizable
void deleteTree(); // Always deletes the next link
void checkTree(); // User Interface version
void checkIter() const;
void clearIter() { m_iterpp=NULL; }
void dumpPtrs(ostream& str=cout) const;
void dumpTree(ostream& str=cout, const string& indent=" ", int maxDepth=0);
void dumpPtrs(std::ostream& str=std::cout) const;
void dumpTree(std::ostream& str=std::cout, const string& indent=" ", int maxDepth=0);
void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); }
void dumpTreeGdb(); // For GDB only
void dumpTreeAndNext(ostream& str=cout, const string& indent=" ", int maxDepth=0);
void dumpTreeAndNext(std::ostream& str=std::cout, const string& indent=" ", int maxDepth=0);
void dumpTreeFile(const string& filename, bool append=false, bool doDump=true);
static void dumpTreeFileGdb(const char* filenamep=NULL);
@ -1341,21 +1376,27 @@ public:
// INVOKERS
virtual void accept(AstNVisitor& v) = 0;
void iterate(AstNVisitor& v) { this->accept(v); } // Does this; excludes following this->next
void iterateAndNext(AstNVisitor& v);
void iterateAndNextConst(AstNVisitor& v);
void iterateChildren(AstNVisitor& v); // Excludes following this->next
void iterateChildrenBackwards(AstNVisitor& v); // Excludes following this->next
void iterateChildrenConst(AstNVisitor& v); // Excludes following this->next
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Return edited nodep; see comments in V3Ast.cpp
protected:
// All AstNVisitor related functions are called as methods off the visitor
friend class AstNVisitor;
void iterateChildren(AstNVisitor& v); // Use instead AstNVisitor::iterateChildren
void iterateChildrenBackwards(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenBackwards
void iterateChildrenConst(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenConst
void iterateAndNext(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextNull
void iterateAndNextConst(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextConstNull
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Use instead AstNVisitor::iterateSubtreeReturnEdits
private:
void iterateListBackwards(AstNVisitor& v);
// CONVERSION
public:
#include "V3Ast__gen_interface.h" // From ./astgen
// Things like:
// AstAlways* castAlways();
};
inline ostream& operator<<(ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
inline std::ostream& operator<<(std::ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
//######################################################################
@ -1364,6 +1405,8 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
#define ASTNODE_BASE_FUNCS(name) \
virtual ~Ast ##name() {} \
static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \
Ast ##name * cloneTree(bool cloneNext) { return static_cast<Ast ##name *>(AstNode::cloneTree(cloneNext)); } \
Ast ##name * clonep() const { return static_cast<Ast ##name *>(AstNode::clonep()); }
@ -1381,7 +1424,7 @@ public:
virtual bool cleanOut() = 0; // True if output has extra upper bits zero
// Someday we will generically support data types on every math node
// Until then isOpaque indicates we shouldn't constant optimize this node type
bool isOpaque() { return castCvtPackString()!=NULL; }
bool isOpaque() { return VN_IS(this, CvtPackString); }
};
class AstNodeTermop : public AstNodeMath {
@ -1523,7 +1566,7 @@ public:
AstNode* fromp() const { return lhsp(); }
AstNode* rhsp() const { return op2p(); }
AstNode* thsp() const { return op3p(); }
AstAttrOf* attrp() const { return op4p()->castAttrOf(); }
AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
@ -1618,7 +1661,7 @@ public:
ASTNODE_BASE_FUNCS(NodeCase)
virtual int instrCount() const { return instrCountBranch(); }
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
AstCaseItem* itemsp() const { return op2p()->castCaseItem(); } // op2 = list of case expressions
AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); } // op2 = list of case expressions
AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
void addNotParallelp(AstNode* nodep) { setOp3p(nodep); }
@ -1694,7 +1737,7 @@ public:
m_text = textp; // Copy it
}
ASTNODE_BASE_FUNCS(NodeText)
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual V3Hash sameHash() const { return V3Hash(text()); }
virtual bool same(const AstNode* samep) const {
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
@ -1720,8 +1763,8 @@ public:
}
ASTNODE_BASE_FUNCS(NodeDType)
// ACCESSORS
virtual void dump(ostream& str);
virtual void dumpSmall(ostream& str);
virtual void dump(std::ostream& str);
virtual void dumpSmall(std::ostream& str);
virtual bool hasDType() const { return true; }
virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type
virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type
@ -1755,7 +1798,7 @@ public:
bool generic() const { return m_generic; }
void generic(bool flag) { m_generic = flag; }
AstNodeDType* dtypeDimensionp(int depth);
pair<uint32_t,uint32_t> dimensions(bool includeBasic);
std::pair<uint32_t,uint32_t> dimensions(bool includeBasic);
uint32_t arrayUnpackedElements(); // 1, or total multiplication of all dimensions
static int uniqueNumInc() { return ++s_uniqueNum; }
const char* charIQWN() const { return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); }
@ -1764,7 +1807,7 @@ public:
class AstNodeClassDType : public AstNodeDType {
private:
// TYPES
typedef map<string,AstMemberDType*> MemberNameMap;
typedef std::map<string,AstMemberDType*> MemberNameMap;
// MEMBERS
bool m_packed;
bool m_isFourstate;
@ -1779,11 +1822,12 @@ public:
}
ASTNODE_BASE_FUNCS(NodeClassDType)
virtual const char* broken() const;
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
// For basicp() we reuse the size to indicate a "fake" basic type of same size
virtual AstBasicDType* basicp() const {
return (isFourstate() ? findLogicDType(width(),width(),numeric())->castBasicDType()
: findBitDType(width(),width(),numeric())->castBasicDType()); }
return (isFourstate()
? VN_CAST(findLogicDType(width(),width(),numeric()), BasicDType)
: VN_CAST(findBitDType(width(),width(),numeric()), BasicDType)); }
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
@ -1793,7 +1837,7 @@ public:
virtual bool similarDType(AstNodeDType* samep) const {
return this==samep; // We don't compare members, require exact equivalence
}
AstMemberDType* membersp() const { return op1p()->castMemberDType(); } // op1 = AstMember list
AstMemberDType* membersp() const { return VN_CAST(op1p(), MemberDType); } // op1 = AstMember list
void addMembersp(AstNode* nodep) { addNOp1p(nodep); }
bool packed() const { return m_packed; }
bool packedUnsup() const { return true; } // packed() but as don't support unpacked, presently all structs
@ -1822,8 +1866,8 @@ public:
m_refDTypep = NULL;
}
ASTNODE_BASE_FUNCS(NodeArrayDType)
virtual void dump(ostream& str);
virtual void dumpSmall(ostream& str);
virtual void dump(std::ostream& str);
virtual void dumpSmall(std::ostream& str);
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep()))); return NULL; }
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
@ -1844,13 +1888,13 @@ public:
}
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(msb()),V3Hash(lsb())); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
AstRange* rangep() const { return op2p()->castRange(); } // op2 = Array(s) of variable
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable
void rangep(AstRange* nodep);
// METHODS
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
@ -1919,7 +1963,7 @@ public:
cname(name); // Might be overridden by dpi import/export
}
ASTNODE_BASE_FUNCS(NodeFTask)
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual string name() const { return m_name; } // * = Var name
virtual bool maybePointedTo() const { return true; }
virtual bool isGateOptimizable() const { return !((m_dpiExport || m_dpiImport) && !m_pure); }
@ -1935,7 +1979,7 @@ public:
AstNode* stmtsp() const { return op3p(); } // op3 = List of statements
void addStmtsp(AstNode* nodep) { addNOp3p(nodep); }
// op4 = scope name
AstScopeName* scopeNamep() const { return op4p()->castScopeName(); }
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
// MORE ACCESSORS
void dpiOpenParentInc() { ++m_dpiOpenParent; }
void dpiOpenParentClear() { m_dpiOpenParent=0; }
@ -1985,7 +2029,7 @@ public:
virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) {
m_taskp = m_taskp->clonep();
}}
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual string name() const { return m_name; } // * = Var name
virtual bool isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
string dotted() const { return m_dotted; } // * = Scope name or ""
@ -2004,7 +2048,7 @@ public:
AstNode* pinsp() const { return op2p(); }
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
// op3 = scope tracking
AstScopeName* scopeNamep() const { return op3p()->castScopeName(); }
AstScopeName* scopeNamep() const { return VN_CAST(op3p(), ScopeName); }
void scopeNamep(AstNode* nodep) { setNOp3p(nodep); }
};
@ -2033,11 +2077,11 @@ public:
,m_internal(false), m_recursive(false), m_recursiveClone(false)
,m_level(0), m_varNum(0), m_typeNum(0) { }
ASTNODE_BASE_FUNCS(NodeModule)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual bool maybePointedTo() const { return true; }
virtual string name() const { return m_name; }
AstNode* stmtsp() const { return op2p(); } // op2 = List of statements
AstActive* activesp() const { return op3p()->castActive(); } // op3 = List of i/sblocks
AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks
// METHODS
void addInlinesp(AstNode* nodep) { addOp1p(nodep); }
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
@ -2080,6 +2124,32 @@ public:
#include "V3Ast__gen_impl.h" // From ./astgen
// Things like:
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
// inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; }
//######################################################################
// Inline AstNVisitor METHODS
inline void AstNVisitor::iterate(AstNode* nodep) {
nodep->accept(*this);
}
inline void AstNVisitor::iterateChildren(AstNode* nodep) {
nodep->iterateChildren(*this);
}
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
nodep->iterateChildrenBackwards(*this);
}
inline void AstNVisitor::iterateChildrenConst(AstNode* nodep) {
nodep->iterateChildrenConst(*this);
}
inline void AstNVisitor::iterateAndNextNull(AstNode* nodep) {
if (VL_LIKELY(nodep)) nodep->iterateAndNext(*this);
}
inline void AstNVisitor::iterateAndNextConstNull(AstNode* nodep) {
if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this);
}
inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
return nodep->iterateSubtreeReturnEdits(*this);
}
//######################################################################
// Inline ACCESSORS
@ -2091,19 +2161,19 @@ inline bool AstNode::width1() const { // V3Const uses to know it can optimize
inline int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); }
inline bool AstNode::isDouble() const {
return dtypep() && dtypep()->castBasicDType() && dtypep()->castBasicDType()->isDouble(); }
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() { return (this->castConst() && this->castConst()->num().isEqZero()); }
inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst()->num().isNeqZero()); }
inline bool AstNode::isOne() { return (this->castConst() && this->castConst()->num().isEqOne()); }
inline bool AstNode::isAllOnes() { return (this->castConst() && this->castConst()->isEqAllOnes()); }
inline bool AstNode::isAllOnesV() { return (this->castConst() && this->castConst()->isEqAllOnesV()); }
inline bool AstNode::sameTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, false); }
inline bool AstNode::sameGateTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, true); }
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::init() { if (m_varp) dtypep(m_varp->dtypep()); }

View File

@ -31,6 +31,8 @@
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
//======================================================================
// Special methods
@ -44,7 +46,7 @@ const char* AstIfaceRefDType::broken() const {
}
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep);
}
const char* AstNodeVarRef::broken() const {
@ -58,12 +60,13 @@ void AstNodeVarRef::cloneRelink() {
}
int AstNodeSel::bitConst() const {
AstConst* constp=bitp()->castConst(); return (constp?constp->toSInt():0);
AstConst* constp=VN_CAST(bitp(), Const);
return (constp ? constp->toSInt() : 0);
}
void AstNodeClassDType::repairMemberCache() {
clearCache();
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
if (m_members.find(itemp->name())!=m_members.end()) {
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
else m_members.insert(make_pair(itemp->name(), itemp));
@ -72,7 +75,7 @@ void AstNodeClassDType::repairMemberCache() {
const char* AstNodeClassDType::broken() const {
vl_unordered_set<AstMemberDType*> exists;
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
exists.insert(itemp);
}
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
@ -150,22 +153,26 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
}
AstExecGraph::AstExecGraph(FileLine* fileline)
: AstNode(fileline) {
m_depGraphp = new V3Graph;
}
AstExecGraph::~AstExecGraph() {
delete m_depGraphp; VL_DANGLING(m_depGraphp);
}
bool AstVar::isSigPublic() const {
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()));
}
bool AstVar::isScQuad() const {
return (isSc() && isQuad() && !isScBv() && !isScBigUint());
}
bool AstVar::isScBv() const {
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
}
bool AstVar::isScUint() const {
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
}
bool AstVar::isScBigUint() const {
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
}
@ -244,7 +251,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
arg += " (& "+name()+")";
for (AstNodeDType* dtp=dtypep(); dtp; ) {
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstUnpackArrayDType* adtypep = dtp->castUnpackArrayDType()) {
if (AstUnpackArrayDType* adtypep = VN_CAST(dtp, UnpackArrayDType)) {
arg += "["+cvtToStr(adtypep->declRange().elements())+"]";
dtp = adtypep->subDTypep();
} else break;
@ -316,7 +323,7 @@ string AstVar::vlPropInit() const {
bool first = true;
for (AstNodeDType* dtp=dtypep(); dtp; ) {
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstNodeArrayDType* adtypep = dtp->castNodeArrayDType()) {
if (AstNodeArrayDType* adtypep = VN_CAST(dtp, NodeArrayDType)) {
if (first) {
out += ", VerilatedVarProps::Unpacked()";
first = false;
@ -423,15 +430,15 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
// See if this is a SC assignment; if so return that type
// Historically sc variables are identified by a variable
// attribute. TODO it would better be a data type attribute.
if (AstVar* anodep = nodep->castVar()) {
if (AstVar* anodep = VN_CAST(nodep, Var)) {
if (anodep->isSc()) return anodep;
else return NULL;
}
else if (nodep->castVarRef()) {
if (nodep->castVarRef()->varp()->isSc()) return nodep->castVarRef()->varp();
else if (VN_IS(nodep, VarRef)) {
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
else return NULL;
}
else if (nodep->castArraySel()) {
else if (VN_IS(nodep, ArraySel)) {
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
@ -440,6 +447,16 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
return NULL;
}
string AstVar::mtasksString() const {
std::ostringstream os;
os<<" all: ";
for (MTaskIdSet::const_iterator it = m_mtaskIds.begin();
it != m_mtaskIds.end(); ++it) {
os<<*it<<" ";
}
return os.str();
}
AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
// dimension passed from AstArraySel::dimension
// Dimension 0 means the VAR itself, 1 is the closest SEL to the AstVar,
@ -455,17 +472,16 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
// TODO this function should be removed in favor of recursing the dtype(),
// as that allows for more complicated data types.
int dim = 0;
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if ((dim++)==dimension) {
return dtypep;
}
dtypep = adtypep->subDTypep();
continue;
}
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
// AstBasicDType - nothing below, return null
if (adtypep->isRanged()) {
if ((dim++) == dimension) {
@ -474,7 +490,7 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
}
return NULL;
}
else if (AstNodeClassDType* adtypep = dtypep->castNodeClassDType()) {
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
if (adtypep->packed()) {
if ((dim++) == dimension) {
return adtypep;
@ -490,10 +506,9 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
uint32_t AstNodeDType::arrayUnpackedElements() {
uint32_t entries=1;
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType()) {
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
entries *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
}
@ -505,20 +520,19 @@ uint32_t AstNodeDType::arrayUnpackedElements() {
return entries;
}
pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
// How many array dimensions (packed,unpacked) does this Var have?
uint32_t packed = 0;
uint32_t unpacked = 0;
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
for (AstNodeDType* dtypep=this; dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
if (adtypep->castPackArrayDType()) packed++;
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
if (VN_IS(adtypep, PackArrayDType)) packed++;
else unpacked++;
dtypep = adtypep->subDTypep();
continue;
}
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
if (includeBasic && adtypep->isRanged()) packed++;
}
break;
@ -538,15 +552,15 @@ int AstNodeDType::widthPow2() const {
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
// Else AstArraySel etc; search for the base
while (nodep) {
if (nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); continue; }
else if (nodep->castSel()) { nodep=nodep->castSel()->fromp(); continue; }
if (VN_IS(nodep, ArraySel)) { nodep=VN_CAST(nodep, ArraySel)->fromp(); continue; }
else if (VN_IS(nodep, Sel)) { nodep=VN_CAST(nodep, Sel)->fromp(); continue; }
// AstNodeSelPre stashes the associated variable under an ATTROF of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
else if (nodep->castAttrOf()) { nodep=nodep->castAttrOf()->fromp(); continue; }
else if (nodep->castNodePreSel()) {
if (nodep->castNodePreSel()->attrp()) {
nodep=nodep->castNodePreSel()->attrp();
else if (VN_IS(nodep, AttrOf)) { nodep=VN_CAST(nodep, AttrOf)->fromp(); continue; }
else if (VN_IS(nodep, NodePreSel)) {
if (VN_CAST(nodep, NodePreSel)->attrp()) {
nodep=VN_CAST(nodep, NodePreSel)->attrp();
} else {
nodep=nodep->castNodePreSel()->lhsp();
nodep=VN_CAST(nodep, NodePreSel)->lhsp();
}
continue;
}
@ -580,7 +594,7 @@ string AstScope::nameDotless() const {
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
out += textp->text();
}
// TOP will be replaced by top->name()
@ -592,7 +606,7 @@ string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
string out;
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
out += textp->text();
}
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
@ -610,28 +624,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
bool AstSenTree::hasClocked() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isClocked()) return true;
}
return false;
}
bool AstSenTree::hasSettle() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isSettle()) return true;
}
return false;
}
bool AstSenTree::hasInitial() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isInitial()) return true;
}
return false;
}
bool AstSenTree::hasCombo() const {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
if (senp->isCombo()) return true;
}
return false;
@ -652,7 +666,7 @@ void AstTypeTable::clearCache() {
m_detailedMap.clear();
// Clear generic()'s so dead detection will work
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
bdtypep->generic(false);
}
}
@ -662,7 +676,7 @@ void AstTypeTable::repairCache() {
// After we mass-change widthMin in V3WidthCommit, we need to correct the table.
clearCache();
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
(void)findInsertSameDType(bdtypep);
}
}
@ -689,7 +703,7 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
pair<int,int> widths = make_pair(width,widthMin);
std::pair<int,int> widths = make_pair(width,widthMin);
LogicMap& mapr = m_logicMap[idx][(int)numeric];
LogicMap::const_iterator it = mapr.find(widths);
if (it != mapr.end()) return it->second;
@ -792,12 +806,12 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
//======================================================================
// Per-type Debugging
void AstNode::dump(ostream& str) {
void AstNode::dump(std::ostream& str) {
str<<typeName()<<" "<<(void*)this
//<<" "<<(void*)this->m_backp
<<" <e"<<dec<<editCount()
<<" <e"<<std::dec<<editCount()
<<((editCount()>=editCountLast())?"#>":">")
<<" {"<<fileline()->filenameLetters()<<dec<<fileline()->lineno()<<"}";
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
if (user1p()) str<<" u1="<<(void*)user1p();
if (user2p()) str<<" u2="<<(void*)user2p();
if (user3p()) str<<" u3="<<(void*)user3p();
@ -813,50 +827,50 @@ void AstNode::dump(ostream& str) {
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<(void*)dtypep();
}
if (name()!="") {
if (castConst()) str<<" "<<name(); // Already quoted
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
else str<<" "<<V3Number::quoteNameControls(name());
}
}
void AstAlways::dump(ostream& str) {
void AstAlways::dump(std::ostream& str) {
this->AstNode::dump(str);
if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<<keyword().ascii()<<"]";
}
void AstAttrOf::dump(ostream& str) {
void AstAttrOf::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" ["<<attrType().ascii()<<"]";
}
void AstBasicDType::dump(ostream& str) {
void AstBasicDType::dump(std::ostream& str) {
this->AstNodeDType::dump(str);
str<<" kwd="<<keyword().ascii();
if (isRanged() && !rangep()) str<<" range=["<<left()<<":"<<right()<<"]";
}
void AstCCast::dump(ostream& str) {
void AstCCast::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" sz"<<size();
}
void AstCell::dump(ostream& str) {
void AstCell::dump(std::ostream& str) {
this->AstNode::dump(str);
if (recursive()) str<<" [RECURSIVE]";
if (modp()) { str<<" -> "; modp()->dump(str); }
else { str<<" ->UNLINKED:"<<modName(); }
}
void AstCellInline::dump(ostream& str) {
void AstCellInline::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> "<<origModName();
}
void AstDisplay::dump(ostream& str) {
void AstDisplay::dump(std::ostream& str) {
this->AstNode::dump(str);
//str<<" "<<displayType().ascii();
}
void AstEnumItemRef::dump(ostream& str) {
void AstEnumItemRef::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (itemp()) { itemp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstIfaceRefDType::dump(ostream& str) {
void AstIfaceRefDType::dump(std::ostream& str) {
this->AstNode::dump(str);
if (cellName()!="") { str<<" cell="<<cellName(); }
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
@ -865,54 +879,54 @@ void AstIfaceRefDType::dump(ostream& str) {
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstIfaceRefDType::dumpSmall(ostream& str) {
void AstIfaceRefDType::dumpSmall(std::ostream& str) {
this->AstNodeDType::dumpSmall(str);
str<<"iface";
}
void AstJumpGo::dump(ostream& str) {
void AstJumpGo::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (labelp()) { labelp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstModportFTaskRef::dump(ostream& str) {
void AstModportFTaskRef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (isExport()) str<<" EXPORT";
if (isImport()) str<<" IMPORT";
if (ftaskp()) { str<<" -> "; ftaskp()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstModportVarRef::dump(ostream& str) {
void AstModportVarRef::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" "<<varType();
if (varp()) { str<<" -> "; varp()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstPin::dump(ostream& str) {
void AstPin::dump(std::ostream& str) {
this->AstNode::dump(str);
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }
else { str<<" ->UNLINKED"; }
if (svImplicit()) str<<" [.SV]";
}
void AstTypedef::dump(ostream& str) {
void AstTypedef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (attrPublic()) str<<" [PUBLIC]";
}
void AstRange::dump(ostream& str) {
void AstRange::dump(std::ostream& str) {
this->AstNode::dump(str);
if (littleEndian()) str<<" [LITTLE]";
}
void AstRefDType::dump(ostream& str) {
void AstRefDType::dump(std::ostream& str) {
this->AstNodeDType::dump(str);
if (defp()) { str<<" -> "; defp()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstNodeClassDType::dump(ostream& str) {
void AstNodeClassDType::dump(std::ostream& str) {
this->AstNode::dump(str);
if (packed()) str<<" [PACKED]";
if (isFourstate()) str<<" [4STATE]";
}
void AstNodeDType::dump(ostream& str) {
void AstNodeDType::dump(std::ostream& str) {
this->AstNode::dump(str);
if (generic()) str<<" [GENERIC]";
if (AstNodeDType* dtp = virtRefDTypep()) {
@ -920,7 +934,7 @@ void AstNodeDType::dump(ostream& str) {
dtp->dumpSmall(str);
}
}
void AstNodeDType::dumpSmall(ostream& str) {
void AstNodeDType::dumpSmall(std::ostream& str) {
str<<"("
<<(generic()?"G/":"")
<<((isSigned()&&!isDouble())?"s":"")
@ -933,16 +947,16 @@ void AstNodeDType::dumpSmall(ostream& str) {
if (!widthSized()) str<<"/"<<widthMin();
str<<")";
}
void AstNodeArrayDType::dumpSmall(ostream& str) {
void AstNodeArrayDType::dumpSmall(std::ostream& str) {
this->AstNodeDType::dumpSmall(str);
if (castPackArrayDType()) str<<"p"; else str<<"u";
if (VN_IS(this, PackArrayDType)) str<<"p"; else str<<"u";
str<<declRange();
}
void AstNodeArrayDType::dump(ostream& str) {
void AstNodeArrayDType::dump(std::ostream& str) {
this->AstNodeDType::dump(str);
str<<" "<<declRange();
}
void AstNodeModule::dump(ostream& str) {
void AstNodeModule::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" L"<<level();
if (modPublic()) str<<" [P]";
@ -951,33 +965,38 @@ void AstNodeModule::dump(ostream& str) {
if (recursiveClone()) str<<" [RECURSIVE-CLONE]";
else if (recursive()) str<<" [RECURSIVE]";
}
void AstPackageExport::dump(ostream& str) {
void AstPackageExport::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> "<<packagep();
}
void AstPackageImport::dump(ostream& str) {
void AstPackageImport::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> "<<packagep();
}
void AstSel::dump(ostream& str) {
void AstSel::dump(std::ostream& str) {
this->AstNode::dump(str);
if (declRange().ranged()) {
str<<" decl"<<declRange()<<"]";
if (declElWidth()!=1) str<<"/"<<declElWidth();
}
}
void AstSliceSel::dump(ostream& str) {
void AstSliceSel::dump(std::ostream& str) {
this->AstNode::dump(str);
if (declRange().ranged()) {
str<<" decl"<<declRange();
}
}
void AstTypeTable::dump(ostream& str) {
void AstMTaskBody::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" ";
m_execMTaskp->dump(str);
}
void AstTypeTable::dump(std::ostream& str) {
this->AstNode::dump(str);
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
if (AstBasicDType* subnodep=m_basicps[i]) {
str<<endl; // Newline from caller, so newline first
str<<"\t\t"<<setw(8)<<AstBasicDTypeKwd(i).ascii();
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
str<<" -> ";
subnodep->dump(str);
}
@ -988,10 +1007,10 @@ void AstTypeTable::dump(ostream& str) {
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
AstBasicDType* dtypep = it->second;
str<<endl; // Newline from caller, so newline first
stringstream nsstr;
std::stringstream nsstr;
nsstr<<(isbit?"bw":"lw")
<<it->first.first<<"/"<<it->first.second;
str<<"\t\t"<<setw(8)<<nsstr.str();
str<<"\t\t"<<std::setw(8)<<nsstr.str();
if (issigned) str<<" s"; else str<<" u";
str<<" -> ";
dtypep->dump(str);
@ -1009,17 +1028,17 @@ void AstTypeTable::dump(ostream& str) {
}
// Note get newline from caller too.
}
void AstUnsizedArrayDType::dumpSmall(ostream& str) {
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) {
this->AstNodeDType::dumpSmall(str);
str<<"[]";
}
void AstVarScope::dump(ostream& str) {
void AstVarScope::dump(std::ostream& str) {
this->AstNode::dump(str);
if (isCircular()) str<<" [CIRC]";
if (varp()) { str<<" -> "; varp()->dump(str); }
else { str<<" ->UNLINKED"; }
}
void AstVarXRef::dump(ostream& str) {
void AstVarXRef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
if (lvalue()) str<<" [LV] => ";
@ -1030,7 +1049,7 @@ void AstVarXRef::dump(ostream& str) {
else if (varp()) { varp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstVarRef::dump(ostream& str) {
void AstVarRef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
if (lvalue()) str<<" [LV] => ";
@ -1039,7 +1058,7 @@ void AstVarRef::dump(ostream& str) {
else if (varp()) { varp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstVar::dump(ostream& str) {
void AstVar::dump(std::ostream& str) {
this->AstNode::dump(str);
if (isSc()) str<<" [SC]";
if (isPrimaryIO()) str<<(isInout()?" [PIO]":(isInput()?" [PI]":" [PO]"));
@ -1063,35 +1082,35 @@ void AstVar::dump(ostream& str) {
if (!attrClocker().unknown()) str<<" ["<<attrClocker().ascii()<<"] ";
str<<" "<<varType();
}
void AstSenTree::dump(ostream& str) {
void AstSenTree::dump(std::ostream& str) {
this->AstNode::dump(str);
if (isMulti()) str<<" [MULTI]";
}
void AstSenItem::dump(ostream& str) {
void AstSenItem::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" ["<<edgeType().ascii()<<"]";
}
void AstParseRef::dump(ostream& str) {
void AstParseRef::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" ["<<expect().ascii()<<"]";
}
void AstPackageRef::dump(ostream& str) {
void AstPackageRef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
str<<" -> ";
if (packagep()) { packagep()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstDot::dump(ostream& str) {
void AstDot::dump(std::ostream& str) {
this->AstNode::dump(str);
}
void AstActive::dump(ostream& str) {
void AstActive::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" => ";
if (sensesp()) { sensesp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstNodeFTaskRef::dump(ostream& str) {
void AstNodeFTaskRef::dump(std::ostream& str) {
this->AstNode::dump(str);
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
str<<" -> ";
@ -1099,7 +1118,7 @@ void AstNodeFTaskRef::dump(ostream& str) {
if (taskp()) { taskp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstNodeFTask::dump(ostream& str) {
void AstNodeFTask::dump(std::ostream& str) {
this->AstNode::dump(str);
if (taskPublic()) str<<" [PUBLIC]";
if (prototype()) str<<" [PROTOTYPE]";
@ -1109,34 +1128,34 @@ void AstNodeFTask::dump(ostream& str) {
if (dpiOpenParent()) str<<" [DPIOPENPARENT]";
if ((dpiImport() || dpiExport()) && cname()!=name()) str<<" [c="<<cname()<<"]";
}
void AstBegin::dump(ostream& str) {
void AstBegin::dump(std::ostream& str) {
this->AstNode::dump(str);
if (unnamed()) str<<" [UNNAMED]";
if (generate()) str<<" [GEN]";
if (genforp()) str<<" [GENFOR]";
}
void AstCoverDecl::dump(ostream& str) {
void AstCoverDecl::dump(std::ostream& str) {
this->AstNode::dump(str);
if (this->dataDeclNullp()) {
str<<" -> ";
this->dataDeclNullp()->dump(str);
} else {
if (binNum()) { str<<" bin"<<dec<<binNum(); }
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
}
}
void AstCoverInc::dump(ostream& str) {
void AstCoverInc::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (declp()) { declp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstTraceInc::dump(ostream& str) {
void AstTraceInc::dump(std::ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (declp()) { declp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstNodeText::dump(ostream& str) {
void AstNodeText::dump(std::ostream& str) {
this->AstNode::dump(str);
string out = text();
string::size_type pos;
@ -1147,19 +1166,19 @@ void AstNodeText::dump(ostream& str) {
str<<" \""<<out<<"\"";
}
void AstCFile::dump(ostream& str) {
void AstCFile::dump(std::ostream& str) {
this->AstNode::dump(str);
if (source()) str<<" [SRC]";
if (slow()) str<<" [SLOW]";
}
void AstCCall::dump(ostream& str) {
void AstCCall::dump(std::ostream& str) {
this->AstNode::dump(str);
if (funcp()) {
str<<" "<<funcp()->name()<<" => ";
funcp()->dump(str);
}
}
void AstCFunc::dump(ostream& str) {
void AstCFunc::dump(std::ostream& str) {
this->AstNode::dump(str);
if (slow()) str<<" [SLOW]";
if (pure()) str<<" [PURE]";

View File

@ -28,14 +28,19 @@
//######################################################################
// Standard defines for all AstNode final classes
#define ASTNODE_NODE_FUNCS(name) \
virtual ~Ast ##name() {} \
#define ASTNODE_NODE_FUNCS_NO_DTOR(name) \
virtual void accept(AstNVisitor& v) { v.visit(this); } \
virtual AstType type() const { return AstType::at ## name; } \
virtual AstNode* clone() { return new Ast ##name (*this); } \
virtual void accept(AstNVisitor& v) { v.visit(this); } \
static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \
Ast ##name * cloneTree(bool cloneNext) { return static_cast<Ast ##name *>(AstNode::cloneTree(cloneNext)); } \
Ast ##name * clonep() const { return static_cast<Ast ##name *>(AstNode::clonep()); }
#define ASTNODE_NODE_FUNCS(name) \
virtual ~Ast ##name() {} \
ASTNODE_NODE_FUNCS_NO_DTOR(name)
//######################################################################
//=== Ast* : Specific types
// Netlist interconnect
@ -132,15 +137,15 @@ public:
AstNode* lsbp() const { return op3p(); } // op3 = Lsb expression
AstNode* leftp() const { return littleEndian()?lsbp():msbp(); } // How to show a declaration
AstNode* rightp() const { return littleEndian()?msbp():lsbp(); }
int msbConst() const { AstConst* constp=msbp()->castConst(); return (constp?constp->toSInt():0); }
int lsbConst() const { AstConst* constp=lsbp()->castConst(); return (constp?constp->toSInt():0); }
int msbConst() const { AstConst* constp=VN_CAST(msbp(), Const); return (constp?constp->toSInt():0); }
int lsbConst() const { AstConst* constp=VN_CAST(lsbp(), Const); return (constp?constp->toSInt():0); }
int elementsConst() const { return (msbConst()>lsbConst()) ? msbConst()-lsbConst()+1 : lsbConst()-msbConst()+1; }
int leftConst() const { AstConst* constp=leftp()->castConst(); return (constp?constp->toSInt():0); }
int rightConst() const { AstConst* constp=rightp()->castConst(); return (constp?constp->toSInt():0); }
int leftConst() const { AstConst* constp=VN_CAST(leftp(), Const); return (constp?constp->toSInt():0); }
int rightConst() const { AstConst* constp=VN_CAST(rightp(), Const); return (constp?constp->toSInt():0); }
int leftToRightInc() const { return littleEndian()?1:-1; }
bool littleEndian() const { return m_littleEndian; }
void littleEndian(bool flag) { m_littleEndian=flag; }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string emitC() { V3ERROR_NA; return ""; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
@ -168,7 +173,7 @@ public:
virtual string emitC() { V3ERROR_NA; return ""; }
virtual bool cleanOut() { return true; }
AstNode* exprp() const { return op1p(); } // op1 = Pin expression
AstRange* rangep() const { return op2p()->castRange(); } // op2 = Range of pin
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Range of pin
};
//######################################################################
@ -188,7 +193,7 @@ public:
}
ASTNODE_NODE_FUNCS(ParamTypeDType)
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
@ -196,7 +201,7 @@ public:
virtual AstNodeDType* skipRefToConstp() const { return subDTypep()->skipRefToConstp(); }
virtual AstNodeDType* skipRefToEnump() const { return subDTypep()->skipRefToEnump(); }
virtual bool similarDType(AstNodeDType* samep) const {
AstParamTypeDType* sp = samep->castParamTypeDType();
const AstParamTypeDType* sp = static_cast<const AstParamTypeDType*>(samep);
return (sp && this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp()));
}
virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); }
@ -225,9 +230,9 @@ public:
m_attrPublic = false;
}
ASTNODE_NODE_FUNCS(Typedef)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
@ -279,7 +284,7 @@ public:
return type()==samep->type() && same(samep); }
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
void* containerp() const { return m_containerp; }
@ -370,10 +375,10 @@ public:
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
return (subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
}
virtual void dumpSmall(ostream& str);
virtual void dumpSmall(std::ostream& str);
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
@ -457,7 +462,7 @@ private:
}
public:
ASTNODE_NODE_FUNCS(BasicDType)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m.m_keyword), V3Hash(m.m_nrange.hi())); }
virtual bool same(const AstNode* samep) const { // width/widthMin/numeric compared elsewhere
const AstBasicDType* sp = static_cast<const AstBasicDType*>(samep);
@ -466,7 +471,7 @@ public:
return type()==samep->type() && same(samep); }
virtual string name() const { return m.m_keyword.ascii(); }
virtual const char* broken() const { BROKEN_RTN(dtypep()!=this); return NULL; }
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable
void rangep(AstRange* nodep) { setNOp1p(nodep); }
void setSignedState(VSignedState signst) {
// Note NOSIGN does NOT change the state; this is required by the parser
@ -498,7 +503,7 @@ public:
bool implicit() const { return keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT; }
VNumRange declRange() const { return isRanged() ? VNumRange(msb(), lsb(), littleEndian()) : VNumRange(); }
void cvtRangeConst() { // Convert to smaller represenation
if (rangep() && rangep()->msbp()->castConst() && rangep()->lsbp()->castConst()) {
if (rangep() && VN_IS(rangep()->msbp(), Const) && VN_IS(rangep()->lsbp(), Const)) {
m.m_nrange.init(rangep()->msbConst(), rangep()->lsbConst(),
rangep()->littleEndian());
rangep()->unlinkFrBackWithNext()->deleteTree();
@ -534,7 +539,7 @@ public:
return skipRefp()->similarDType(samep->skipRefp()); }
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } // node's type() included elsewhere
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
@ -568,8 +573,8 @@ public:
ASTNODE_NODE_FUNCS(IfaceRefDType)
// METHODS
virtual const char* broken() const;
virtual void dump(ostream& str=cout);
virtual void dumpSmall(ostream& str);
virtual void dump(std::ostream& str=std::cout);
virtual void dumpSmall(std::ostream& str);
virtual void cloneRelink();
virtual AstBasicDType* basicp() const { return NULL; }
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
@ -621,7 +626,7 @@ public:
virtual bool similarDType(AstNodeDType* samep) const {
return skipRefp()->similarDType(samep->skipRefp()); }
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(m_packagep)); }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual string name() const { return m_name; }
virtual AstBasicDType* basicp() const { return subDTypep() ? subDTypep()->basicp() : NULL; }
virtual AstNodeDType* skipRefp() const {
@ -698,7 +703,7 @@ public:
virtual bool hasDType() const { return true; }
virtual bool maybePointedTo() const { return true; }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
@ -738,7 +743,7 @@ public:
virtual bool maybePointedTo() const { return true; }
virtual bool hasDType() const { return true; }
void name(const string& flag) { m_name = flag; }
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range for name appending
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range for name appending
void rangep(AstNode* nodep) { addOp1p(nodep); }
AstNode* valuep() const { return op2p(); } // op2 = Value
void valuep(AstNode* nodep) { addOp2p(nodep); }
@ -754,11 +759,11 @@ public:
dtypeFrom(m_itemp);
}
ASTNODE_NODE_FUNCS(EnumItemRef)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return itemp()->name(); }
virtual const char* broken() const { BROKEN_RTN(!itemp()); return NULL; }
virtual int instrCount() const { return 0; }
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = m_itemp->clonep()->castEnumItem(); }
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = VN_CAST(m_itemp->clonep(), EnumItem); }
virtual bool same(const AstNode* samep) const {
const AstEnumItemRef* sp = static_cast<const AstEnumItemRef*>(samep);
return itemp() == sp->itemp(); }
@ -798,13 +803,13 @@ public:
virtual bool similarDType(AstNodeDType* samep) const { return this==samep; }
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Data type
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Data type
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
AstEnumItem* itemsp() const { return op2p()->castEnumItem(); } // op2 = AstEnumItem's
AstEnumItem* itemsp() const { return VN_CAST(op2p(), EnumItem); } // op2 = AstEnumItem's
void addValuesp(AstNode* nodep) { addOp2p(nodep); }
// METHODS
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
@ -841,9 +846,9 @@ class AstArraySel : public AstNodeSel {
// Children: varref|arraysel, math
private:
void init(AstNode* fromp) {
if (fromp && fromp->dtypep()->skipRefp()->castNodeArrayDType()) {
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
// Strip off array to find what array references
dtypeFrom(fromp->dtypep()->skipRefp()->castNodeArrayDType()->subDTypep());
dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep());
}
}
public:
@ -949,9 +954,9 @@ public:
AstSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, AstNode* widthp)
:AstNodeTriop(fl, fromp, lsbp, widthp) {
m_declElWidth = 1;
if (widthp->castConst()) {
dtypeSetLogicSized(widthp->castConst()->toUInt(),
widthp->castConst()->toUInt(),
if (VN_IS(widthp, Const)) {
dtypeSetLogicSized(VN_CAST(widthp, Const)->toUInt(),
VN_CAST(widthp, Const)->toUInt(),
AstNumeric::UNSIGNED);
}
}
@ -962,7 +967,7 @@ public:
dtypeSetLogicSized(bitwidth,bitwidth,AstNumeric::UNSIGNED);
}
ASTNODE_NODE_FUNCS(Sel)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit, const V3Number& width) {
out.opSel(from, bit.toUInt()+width.toUInt()-1, bit.toUInt()); }
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
@ -977,12 +982,12 @@ public:
virtual bool sizeMattersThs() {return false;}
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode*) const { return true; }
virtual int instrCount() const { return widthInstrs()*(lsbp()->castConst()?3:10); }
virtual int instrCount() const { return widthInstrs()*(VN_CAST(lsbp(), Const)?3:10); }
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
AstNode* lsbp() const { return op2p(); } // op2 = Msb selection expression
AstNode* widthp() const { return op3p(); } // op3 = Width
int widthConst() const { return widthp()->castConst()->toSInt(); }
int lsbConst() const { return lsbp()->castConst()->toSInt(); }
int widthConst() const { return VN_CAST(widthp(), Const)->toSInt(); }
int lsbConst() const { return VN_CAST(lsbp(), Const)->toSInt(); }
int msbConst() const { return lsbConst()+widthConst()-1; }
VNumRange& declRange() { return m_declRange; }
void declRange(const VNumRange& flag) { m_declRange = flag; }
@ -1003,7 +1008,7 @@ public:
new AstConst(fl, declRange.elements()))
, m_declRange(declRange) { }
ASTNODE_NODE_FUNCS(SliceSel)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& lo, const V3Number& width) {
V3ERROR_NA; }
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
@ -1119,6 +1124,7 @@ private:
bool m_noSubst:1; // Do not substitute out references
bool m_trace:1; // Trace this variable
AstVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
void init() {
m_input=false; m_output=false; m_tristate=false; m_declOutput=false;
@ -1174,7 +1180,7 @@ public:
dtypeFrom(examplep);
}
ASTNODE_NODE_FUNCS(Var)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; } // * = Var name
virtual bool hasDType() const { return true; }
virtual bool maybePointedTo() const { return true; }
@ -1194,7 +1200,7 @@ public:
string vlPropInit() const; // Return VerilatorVarProps initializer
void combineType(AstVarType type);
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
// (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType)
AstBasicDType* basicp() const { return subDTypep()->basicp(); }
@ -1318,6 +1324,10 @@ public:
if (varType()==AstVarType::INPUT || varType()==AstVarType::OUTPUT) m_varType = AstVarType::WIRE;
}
static AstVar* scVarRecurse(AstNode* nodep);
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
string mtasksString() const;
};
class AstDefParam : public AstNode {
@ -1402,7 +1412,7 @@ public:
ASTNODE_NODE_FUNCS(TopScope)
AstNode* stmtsp() const { return op1p(); }
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
AstScope* scopep() const { return op2p()->castScope(); } // op1 = AstVarScope's
AstScope* scopep() const { return VN_CAST(op2p(), Scope); } // op1 = AstVarScope's
};
class AstVarScope : public AstNode {
@ -1433,7 +1443,7 @@ public:
BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; }
virtual bool maybePointedTo() const { return true; }
virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual bool hasDType() const { return true; }
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under
@ -1460,7 +1470,7 @@ public:
varScopep(varscp);
}
ASTNODE_NODE_FUNCS(VarRef)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()),V3Hash(hiername())); }
virtual bool same(const AstNode* samep) const {
return same(static_cast<const AstVarRef*>(samep)); }
@ -1496,7 +1506,7 @@ public:
dtypeFrom(varp);
}
ASTNODE_NODE_FUNCS(VarXRef)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
string prettyDotted() const { return prettyName(dotted()); }
@ -1542,7 +1552,7 @@ public:
setNOp1p(exprp);
}
ASTNODE_NODE_FUNCS(Pin)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual const char* broken() const {
BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists());
BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists());
@ -1643,7 +1653,7 @@ public:
ASTNODE_NODE_FUNCS(PackageExport)
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; }
AstPackage* packagep() const { return m_packagep; }
void packagep(AstPackage* nodep) { m_packagep=nodep; }
@ -1660,7 +1670,7 @@ public:
ASTNODE_NODE_FUNCS(PackageImport)
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; }
AstPackage* packagep() const { return m_packagep; }
void packagep(AstPackage* nodep) { m_packagep=nodep; }
@ -1687,7 +1697,7 @@ public:
: AstNode(fl), m_name(name), m_export(isExport), m_ftaskp(NULL) { }
ASTNODE_NODE_FUNCS(ModportFTaskRef)
virtual const char* broken() const { BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists()); return NULL; }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; }
virtual void cloneRelink() { if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep(); }
bool isImport() const { return !m_export; }
@ -1709,7 +1719,7 @@ public:
: AstNode(fl), m_name(name), m_type(type), m_varp(NULL) { }
ASTNODE_NODE_FUNCS(ModportVarRef)
virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); return NULL; }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual void cloneRelink() { if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep(); }
virtual string name() const { return m_name; }
AstVarType varType() const { return m_type; } // * = Type of variable
@ -1752,7 +1762,7 @@ public:
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
ASTNODE_NODE_FUNCS(Cell)
// No cloneRelink, we presume cloneee's want the same module linkages
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual const char* broken() const { BROKEN_RTN(m_modp && !m_modp->brokeExists()); return NULL; }
virtual bool maybePointedTo() const { return true; }
// ACCESSORS
@ -1762,9 +1772,9 @@ public:
void origName(const string& name) { m_origName = name; }
string modName() const { return m_modName; } // * = Instance name
void modName(const string& name) { m_modName = name; }
AstPin* pinsp() const { return op1p()->castPin(); } // op1 = List of cell ports
AstPin* paramsp() const { return op2p()->castPin(); } // op2 = List of parameter #(##) values
AstRange* rangep() const { return op3p()->castRange(); } // op3 = Range of arrayed instants (NULL=not ranged)
AstPin* pinsp() const { return VN_CAST(op1p(), Pin); } // op1 = List of cell ports
AstPin* paramsp() const { return VN_CAST(op2p(), Pin); } // op2 = List of parameter #(##) values
AstRange* rangep() const { return VN_CAST(op3p(), Range); } // op3 = Range of arrayed instants (NULL=not ranged)
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
void addPinsp(AstPin* nodep) { addOp1p(nodep); }
void addParamsp(AstPin* nodep) { addOp2p(nodep); }
@ -1789,7 +1799,7 @@ public:
: AstNode(fl)
, m_name(name), m_origModName(origModName) {}
ASTNODE_NODE_FUNCS(CellInline)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
// ACCESSORS
virtual string name() const { return m_name; } // * = Cell name
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
@ -1855,7 +1865,7 @@ public:
AstBind(FileLine* fl, const string& name, AstNode* cellsp)
: AstNode(fl)
, m_name(name) {
if (!cellsp->castCell()) cellsp->v3fatalSrc("Only cells allowed to be bound");
if (!VN_IS(cellsp, Cell)) cellsp->v3fatalSrc("Only cells allowed to be bound");
addNOp1p(cellsp);
}
ASTNODE_NODE_FUNCS(Bind)
@ -1910,7 +1920,7 @@ public:
AstParseRef(FileLine* fl, AstParseRefExp expect, const string& name, AstNode* lhsp, AstNodeFTaskRef* ftaskrefp)
:AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); }
ASTNODE_NODE_FUNCS(ParseRef)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; } // * = Var name
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); }
virtual bool same(const AstNode* samep) const {
@ -1943,7 +1953,7 @@ public:
virtual bool same(const AstNode* samep) const {
return (m_packagep==static_cast<const AstPackageRef*>(samep)->m_packagep); }
virtual V3Hash sameHash() const { return V3Hash(m_packagep); }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
AstPackage* packagep() const { return m_packagep; }
void packagep(AstPackage* nodep) { m_packagep=nodep; }
};
@ -1959,7 +1969,7 @@ public:
if (!packagep) return rhsp;
return new AstDot(fl, new AstPackageRef(fl, packagep), rhsp);
}
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string emitVerilog() { V3ERROR_NA; return ""; }
virtual string emitC() { V3ERROR_NA; return ""; }
AstNode* lhsp() const { return op1p(); }
@ -2063,14 +2073,14 @@ public:
m_edgeType = AstEdgeType::ET_NEVER;
}
ASTNODE_NODE_FUNCS(SenItem)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual V3Hash sameHash() const { return V3Hash(edgeType()); }
virtual bool same(const AstNode* samep) const {
return edgeType()==static_cast<const AstSenItem*>(samep)->edgeType(); }
AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge
void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge
AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized
AstNodeVarRef* varrefp() const { return op1p()->castNodeVarRef(); } // op1 = Signal sensitized
AstNodeVarRef* varrefp() const { return VN_CAST(op1p(), NodeVarRef); } // op1 = Signal sensitized
//
virtual bool isClocked() const { return edgeType().clockedStmt(); }
virtual bool isCombo() const { return edgeType()==AstEdgeType::ET_COMBO; }
@ -2092,7 +2102,7 @@ public:
}
ASTNODE_NODE_FUNCS(SenGate)
virtual string emitVerilog() { return "(%l) %f&& (%r)"; }
AstSenItem* sensesp() const { return op1p()->castSenItem(); }
AstSenItem* sensesp() const { return VN_CAST(op1p(), SenItem); }
AstNode* rhsp() const { return op2p(); }
void sensesp(AstSenItem* nodep) { addOp1p(nodep); }
void rhsp(AstNode* nodep) { setOp2p(nodep); }
@ -2116,10 +2126,11 @@ public:
addNOp1p(sensesp);
}
ASTNODE_NODE_FUNCS(SenTree)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual bool maybePointedTo() const { return true; }
virtual V3Hash sameHash() const { return V3Hash(); }
bool isMulti() const { return m_multi; }
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
void addSensesp(AstNodeSenItem* nodep) { addOp1p(nodep); }
void multi(bool flag) { m_multi = true; }
// METHODS
@ -2138,8 +2149,8 @@ public:
}
ASTNODE_NODE_FUNCS(Always)
//
virtual void dump(ostream& str);
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
virtual void dump(std::ostream& str);
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
VAlwaysKwd keyword() const { return m_keyword; }
@ -2159,7 +2170,7 @@ public:
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
//
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
// Special accessors
@ -2345,7 +2356,7 @@ public:
}
return NULL; }
virtual void cloneRelink() { if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual int instrCount() const { return 1+2*instrCountLd(); }
virtual bool maybePointedTo() const { return true; }
int column() const { return m_column; }
@ -2384,7 +2395,7 @@ public:
ASTNODE_NODE_FUNCS(CoverInc)
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual int instrCount() const { return 1+2*instrCountLd(); }
virtual V3Hash sameHash() const { return V3Hash(declp()); }
virtual bool same(const AstNode* samep) const {
@ -2415,7 +2426,7 @@ public:
virtual bool isPredictOptimizable() const { return true; }
virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter
// but isPure() true
AstCoverInc* incp() const { return op1p()->castCoverInc(); }
AstCoverInc* incp() const { return VN_CAST(op1p(), CoverInc); }
void incp(AstCoverInc* nodep) { setOp1p(nodep); }
AstNode* origp() const { return op2p(); }
AstNode* changep() const { return op3p(); }
@ -2525,7 +2536,7 @@ public:
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text=text; }
AstScopeName* scopeNamep() const { return op2p()->castScopeName(); }
AstScopeName* scopeNamep() const { return VN_CAST(op2p(), ScopeName); }
void scopeNamep(AstNode* nodep) { setNOp2p(nodep); }
bool formatScopeTracking() const { // Track scopeNamep(); Ok if false positive
return (name().find("%m") != string::npos || name().find("%M") != string::npos); }
@ -2554,7 +2565,7 @@ public:
m_displayType = dispType;
}
ASTNODE_NODE_FUNCS(Display)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; }
virtual string verilogKwd() const { return (filep() ? (string)"$f"+(string)displayType().ascii()
: (string)"$"+(string)displayType().ascii()); }
@ -2571,7 +2582,7 @@ public:
void displayType(AstDisplayType type) { m_displayType = type; }
bool addNewline() const { return displayType().addNewline(); } // * = Add a newline for $display
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
AstSFormatF* fmtp() const { return op1p()->castSFormatF(); }
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
AstNode* filep() const { return op3p(); }
void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); }
};
@ -2600,7 +2611,7 @@ public:
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
AstSFormatF* fmtp() const { return op1p()->castSFormatF(); }
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
AstNode* lhsp() const { return op3p(); }
void lhsp(AstNode* nodep) { setOp3p(nodep); }
};
@ -3080,7 +3091,7 @@ public:
ASTNODE_NODE_FUNCS(JumpGo)
virtual const char* broken() const { BROKEN_RTN(!labelp()->brokeExistsAbove()); return NULL; }
virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(labelp()); }
virtual bool same(const AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
@ -3100,7 +3111,7 @@ public:
addNOp2p(stablesp); addNOp3p(bodysp);
}
ASTNODE_NODE_FUNCS(UntilStable)
AstVarRef* stablesp() const { return op2p()->castVarRef(); } // op2= list of variables that must become stable
AstVarRef* stablesp() const { return VN_CAST(op2p(), VarRef); } // op2= list of variables that must become stable
AstNode* bodysp() const { return op3p(); } // op3= body of loop
void addStablesp(AstVarRef* newp) { addOp2p(newp); }
void addBodysp(AstNode* newp) { addOp3p(newp); }
@ -3172,7 +3183,7 @@ public:
m_generate = generate;
}
ASTNODE_NODE_FUNCS(Begin)
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual string name() const { return m_name; } // * = Block name
virtual void name(const string& name) { m_name = name; }
// op1 = Statements
@ -3244,7 +3255,7 @@ class AstInitArray : public AstNode {
// If default is specified, the vector may be sparse, and not provide each value.
// Parents: ASTVAR::init()
// Children: CONSTs...
deque<uint32_t> m_indices; // Which array index each entry in the list is for (if defaultp)
std::deque<uint32_t> m_indices; // Which array index each entry in the list is for (if defaultp)
public:
AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* defaultp)
: AstNode(fl) {
@ -3377,7 +3388,7 @@ public:
ASTNODE_NODE_FUNCS(TraceInc)
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); }
virtual void dump(ostream& str);
virtual void dump(std::ostream& str);
virtual int instrCount() const { return 10+2*instrCountLd(); }
virtual bool hasDType() const { return true; }
virtual V3Hash sameHash() const { return V3Hash(declp()); }
@ -3410,7 +3421,7 @@ public:
m_sensesp = sensesp;
}
ASTNODE_NODE_FUNCS(Active)
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual string name() const { return m_name; }
virtual const char* broken() const { BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists()); return NULL; }
virtual void cloneRelink() {
@ -3424,7 +3435,7 @@ public:
AstSenTree* sensesp() const { return m_sensesp; }
// op1 = Sensitivity tree, if a clocked block in early stages
void sensesStorep(AstSenTree* nodep) { addOp1p(nodep); }
AstSenTree* sensesStorep() const { return op1p()->castSenTree(); }
AstSenTree* sensesStorep() const { return VN_CAST(op1p(), SenTree); }
// op2 = Combo logic
AstNode* stmtsp() const { return op2p(); }
void addStmtsp(AstNode* nodep) { addOp2p(nodep); }
@ -3448,7 +3459,7 @@ public:
AstNode* fromp() const { return op1p(); }
AstNode* dimp() const { return op2p(); }
AstAttrType attrType() const { return m_attrType; }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
};
class AstScopeName : public AstNodeMath {
@ -3469,9 +3480,9 @@ public:
virtual string emitVerilog() { return ""; }
virtual string emitC() { V3ERROR_NA; return ""; }
virtual bool cleanOut() { return true; }
AstText* scopeAttrp() const { return op1p()->castText(); }
AstText* scopeAttrp() const { return VN_CAST(op1p(), Text); }
void scopeAttrp(AstNode* nodep) { addOp1p(nodep); }
AstText* scopeEntrp() const { return op2p()->castText(); }
AstText* scopeEntrp() const { return VN_CAST(op2p(), Text); }
void scopeEntrp(AstNode* nodep) { addOp2p(nodep); }
string scopeSymName() const { // Name for __Vscope variable including children
return scopeNameFormatter(scopeAttrp()); }
@ -3492,7 +3503,7 @@ public:
addNOp1p(bodysp);
}
ASTNODE_NODE_FUNCS(UdpTable)
AstUdpTableLine* bodysp() const { return op1p()->castUdpTableLine(); } // op1 = List of UdpTableLines
AstUdpTableLine* bodysp() const { return VN_CAST(op1p(), UdpTableLine); } // op1 = List of UdpTableLines
};
class AstUdpTableLine : public AstNode {
@ -3896,7 +3907,7 @@ public:
virtual bool sizeMattersLhs() {return false;}
AstNode* lhsp() const { return op1p(); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op2p()->castNodeDType(); }
AstNodeDType* childDTypep() const { return VN_CAST(op2p(), NodeDType); }
};
class AstCastParse : public AstNode {
@ -3955,7 +3966,7 @@ public:
virtual V3Hash sameHash() const { return V3Hash(size()); }
virtual bool same(const AstNode* samep) const {
return size() == static_cast<const AstCCast*>(samep)->size(); }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
//
int size() const { return m_size; }
};
@ -5045,7 +5056,7 @@ class AstReplicate : public AstNodeBiop {
private:
void init() {
if (lhsp()) {
if (AstConst* constp=rhsp()->castConst()) {
if (const AstConst* constp = VN_CAST(rhsp(), Const)) {
dtypeSetLogicSized(lhsp()->width()*constp->toUInt(), lhsp()->width()*constp->toUInt(), AstNumeric::UNSIGNED);
}
}
@ -5194,7 +5205,7 @@ public:
virtual bool cleanOut() {V3ERROR_NA; return "";}
virtual int instrCount() const { return widthInstrs(); }
AstNodeDType* getChildDTypep() const { return childDTypep(); }
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
@ -5258,7 +5269,7 @@ public:
addNOp2p(bodysp);
}
ASTNODE_NODE_FUNCS(Clocking)
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Body
};
@ -5278,7 +5289,7 @@ public:
}
ASTNODE_NODE_FUNCS(PslClocked)
virtual bool hasDType() const { return true; } // Used under PslCover, which expects a bool child
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
AstNode* disablep() const { return op2p(); } // op2 = disable
AstNode* propp() const { return op3p(); } // op3 = property
};
@ -5302,7 +5313,7 @@ public:
virtual bool same(const AstNode* samep) const { return samep->name() == name(); }
virtual void name(const string& name) { m_name = name; }
AstNode* propp() const { return op1p(); } // op1 = property
AstSenTree* sentreep() const { return op2p()->castSenTree(); } // op2 = clock domain
AstSenTree* sentreep() const { return VN_CAST(op2p(), SenTree); } // op2 = clock domain
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
AstNode* stmtsp() const { return op4p(); } // op4 = statements
};
@ -5432,7 +5443,7 @@ public:
virtual string name() const { return m_name; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
bool slow() const { return m_slow; }
void slow(bool flag) { m_slow = flag; }
bool source() const { return m_source; }
@ -5467,6 +5478,7 @@ private:
bool m_dpiExport:1; // From dpi export
bool m_dpiExportWrapper:1; // From dpi export; static function with dispatch table
bool m_dpiImport:1; // From dpi import
bool m_dpiImportWrapper:1; // Wrapper from dpi import
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="")
: AstNode(fl) {
@ -5488,12 +5500,13 @@ public:
m_dpiExport = false;
m_dpiExportWrapper = false;
m_dpiImport = false;
m_dpiImportWrapper = false;
}
ASTNODE_NODE_FUNCS(CFunc)
virtual string name() const { return m_name; }
virtual const char* broken() const { BROKEN_RTN((m_scopep && !m_scopep->brokeExists())); return NULL; }
virtual bool maybePointedTo() const { return true; }
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const {
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
@ -5545,6 +5558,8 @@ public:
void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; }
bool dpiImport() const { return m_dpiImport; }
void dpiImport(bool flag) { m_dpiImport = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
//
// If adding node accessors, see below emptyBody
AstNode* argsp() const { return op1p(); }
@ -5582,7 +5597,7 @@ public:
if (oldp->argsp()) addNOp1p(oldp->argsp()->unlinkFrBackWithNext());
}
ASTNODE_NODE_FUNCS(CCall)
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
virtual void cloneRelink() { if (m_funcp && m_funcp->clonep()) {
m_funcp = m_funcp->clonep();
}}
@ -5665,7 +5680,7 @@ public:
virtual bool isPredictOptimizable() const { return false; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
AstVarRef* varrefp() const { return op1p()->castVarRef(); } // op1= varref to reset
AstVarRef* varrefp() const { return VN_CAST(op1p(), VarRef); } // op1= varref to reset
};
class AstCStmt : public AstNodeStmt {
@ -5688,26 +5703,72 @@ public:
AstNode* bodysp() const { return op1p(); } // op1= expressions to print
};
class AstMTaskBody : public AstNode {
// Hold statements for each MTask
private:
ExecMTask* m_execMTaskp;
public:
explicit AstMTaskBody(FileLine* flp)
: AstNode(flp)
, m_execMTaskp(NULL) {}
ASTNODE_NODE_FUNCS(MTaskBody);
virtual const char* broken() const { BROKEN_RTN(!m_execMTaskp); return NULL; }
AstNode* stmtsp() const { return op1p(); }
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
ExecMTask* execMTaskp() const { return m_execMTaskp; }
void execMTaskp(ExecMTask* execMTaskp) { m_execMTaskp = execMTaskp; }
virtual void dump(std::ostream& str=std::cout);
};
class AstExecGraph : public AstNode {
// For parallel execution, this node contains a dependency graph. Each
// node in the graph is an ExecMTask, which contains a body for the
// mtask, which contains a set of AstActive's, each of which calls a
// leaf AstCFunc. whew!
//
// The mtask bodies are also children of this node, so we can visit
// them without traversing the graph (it's not always needed to
// traverse the graph.)
private:
V3Graph *m_depGraphp; // contains ExecMTask's
public:
explicit AstExecGraph(FileLine* fileline);
ASTNODE_NODE_FUNCS_NO_DTOR(ExecGraph)
virtual ~AstExecGraph();
virtual const char* broken() const { BROKEN_RTN(!m_depGraphp); return NULL; }
const V3Graph* depGraphp() const { return m_depGraphp; }
V3Graph* mutableDepGraphp() { return m_depGraphp; }
void addMTaskBody(AstMTaskBody* bodyp) { addOp1p(bodyp); }
};
class AstSplitPlaceholder : public AstNode {
public:
// Dummy node used within V3Split; never exists outside of V3Split.
explicit AstSplitPlaceholder(FileLine* filelinep)
: AstNode(filelinep) {}
ASTNODE_NODE_FUNCS(SplitPlaceholder)
};
//######################################################################
// Right below top
class AstTypeTable : public AstNode {
// Container for hash of standard data types
// Children: NODEDTYPEs
typedef map<pair<int,int>,AstBasicDType*> LogicMap;
typedef std::map<std::pair<int,int>,AstBasicDType*> LogicMap;
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
//
enum { IDX0_LOGIC, IDX0_BIT, _IDX0_MAX };
LogicMap m_logicMap[_IDX0_MAX][AstNumeric::_ENUM_MAX]; // uses above IDX enums
//
typedef map<VBasicTypeKey,AstBasicDType*> DetailedMap;
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
DetailedMap m_detailedMap;
public:
explicit AstTypeTable(FileLine* fl) : AstNode(fl) {
for (int i=0; i<AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = NULL;
}
ASTNODE_NODE_FUNCS(TypeTable)
AstNodeDType* typesp() const { return op1p()->castNodeDType();} // op1 = List of dtypes
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType);} // op1 = List of dtypes
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
@ -5717,7 +5778,7 @@ public:
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
void clearCache();
void repairCache();
virtual void dump(ostream& str=cout);
virtual void dump(std::ostream& str=std::cout);
};
//######################################################################
@ -5731,22 +5792,26 @@ private:
AstTypeTable* m_typeTablep; // Reference to top type table, for faster lookup
AstPackage* m_dollarUnitPkgp;
AstCFunc* m_evalp; // The '_eval' function
AstExecGraph* m_execGraphp; // Execution MTask graph for threads>1 mode
public:
AstNetlist()
: AstNode(new FileLine("AstRoot",0))
, m_typeTablep(NULL)
, m_dollarUnitPkgp(NULL)
, m_evalp(NULL) { }
, m_evalp(NULL)
, m_execGraphp(NULL) { }
ASTNODE_NODE_FUNCS(Netlist)
virtual const char* broken() const {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
return NULL;
}
AstNodeModule* modulesp() const { return op1p()->castNodeModule();} // op1 = List of modules
AstNodeModule* topModulep() const { return op1p()->castNodeModule(); } // * = Top module in hierarchy (first one added, for now)
AstNodeModule* modulesp() const { // op1 = List of modules
return VN_CAST(op1p(), NodeModule); }
AstNodeModule* topModulep() const { // * = Top module in hierarchy (first one added, for now)
return VN_CAST(op1p(), NodeModule); }
void addModulep(AstNodeModule* modulep) { addOp1p(modulep); }
AstCFile* filesp() const { return op2p()->castCFile();} // op2 = List of files
AstCFile* filesp() const { return VN_CAST(op2p(), CFile);} // op2 = List of files
void addFilesp(AstCFile* filep) { addOp2p(filep); }
AstNode* miscsp() const { return op3p();} // op3 = List of dtypes etc
void addMiscsp(AstNode* nodep) { addOp3p(nodep); }
@ -5764,6 +5829,8 @@ public:
return m_dollarUnitPkgp; }
AstCFunc* evalp() const { return m_evalp; }
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
AstExecGraph* execGraphp() const { return m_execGraphp; }
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
};
//######################################################################

View File

@ -77,17 +77,13 @@ private:
int m_ifDepth; // Current if depth
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_repeatNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstNodeFTask* nodep) {
@ -107,7 +103,7 @@ private:
m_namedScope = "";
m_unnamedScope = "";
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
}
m_namedScope = oldScope;
@ -143,7 +139,7 @@ private:
}
// Remap var names and replace lower Begins
nodep->stmtsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->stmtsp());
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
}
m_namedScope = oldScope;
@ -184,7 +180,7 @@ private:
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVarXRef* nodep) {
UINFO(9, " VARXREF "<<nodep<<endl);
@ -204,12 +200,12 @@ private:
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_namedScope));
if (afterp) nodep->scopeAttrp(afterp);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCoverDecl* nodep) {
// Don't need to fix path in coverage statements, they're not under
// any BEGINs, but V3Coverage adds them all under the module itself.
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// VISITORS - LINT CHECK
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
@ -223,11 +219,11 @@ private:
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
m_ifDepth = -1;
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ifDepth = prevIfDepth;
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -237,7 +233,7 @@ public:
m_ftaskp = NULL;
m_repeatNum = 0;
m_ifDepth = 0;
nodep->accept(*this);
iterate(nodep);
}
virtual ~BeginVisitor() {}
};
@ -257,14 +253,14 @@ private:
UINFO(9, " relinkFTask "<<nodep<<endl);
nodep->name(nodep->taskp()->name());
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVarRef* nodep) {
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef "<<nodep<<endl);
nodep->name(nodep->varp()->name());
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstIfaceRefDType* nodep) {
// May have changed cell names
@ -272,16 +268,16 @@ private:
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
UINFO(8," rename to "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//--------------------
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~BeginRelinkVisitor() {}
};

View File

@ -49,7 +49,7 @@ private:
AstUser1InUse m_inuser1;
// TYPES
typedef vector<AstCFunc*> CFuncVec;
typedef std::vector<AstCFunc*> CFuncVec;
// STATE
int m_likely; // Excuses for branch likely taken
@ -57,11 +57,7 @@ private:
CFuncVec m_cfuncsp; // List of all tasks
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void reset() {
m_likely = false;
@ -82,12 +78,12 @@ private:
{
// Do if
reset();
nodep->ifsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->ifsp());
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
// Do else
reset();
nodep->elsesp()->iterateAndNext(*this);
iterateAndNextNull(nodep->elsesp());
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
// Compute
@ -104,16 +100,16 @@ private:
virtual void visit(AstCCall* nodep) {
checkUnlikely(nodep);
nodep->funcp()->user1Inc();
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
checkUnlikely(nodep);
m_cfuncsp.push_back(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
checkUnlikely(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// METHODS
@ -130,7 +126,7 @@ public:
// CONSTUCTORS
explicit BranchVisitor(AstNetlist* nodep) {
reset();
nodep->iterateChildren(*this);
iterateChildren(nodep);
calc_tasks();
}
virtual ~BranchVisitor() {}

View File

@ -155,9 +155,9 @@ public:
// Use only AstNode::dump instead of the virtual one, as there
// may be varp() and other cross links that are bad.
if (v3Global.opt.debugCheck()) {
cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
((AstNode*)(it->first))->AstNode::dump(cerr);
cerr<<endl;
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
((AstNode*)(it->first))->AstNode::dump(std::cerr);
std::cerr<<endl;
V3Error::incErrors();
}
it->second |= FLAG_LEAKED;
@ -193,7 +193,7 @@ private:
// METHODS
void processAndIterate(AstNode* nodep) {
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
nodep->iterateChildrenConst(*this);
iterateChildrenConst(nodep);
}
// VISITORS
virtual void visit(AstNode* nodep) {
@ -202,7 +202,7 @@ private:
public:
// CONSTUCTORS
explicit BrokenMarkVisitor(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~BrokenMarkVisitor() {}
};
@ -212,7 +212,7 @@ public:
class BrokenCheckVisitor : public AstNVisitor {
private:
void checkWidthMin(AstNode* nodep) {
void checkWidthMin(const AstNode* nodep) {
if (nodep->width() != nodep->widthMin()
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
nodep->v3fatalSrc("Width != WidthMin");
@ -225,10 +225,9 @@ private:
}
if (nodep->dtypep()) {
if (!nodep->dtypep()->brokeExists()) {
nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep());
} else if (!nodep->dtypep()->castNodeDType()) {
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "<<(void*)nodep->dtypep());
}
nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep()); }
else if (!VN_IS(nodep->dtypep(), NodeDType)) {
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "<<(void*)nodep->dtypep()); }
}
if (v3Global.assertDTypesResolved()) {
if (nodep->hasDType()) {
@ -237,18 +236,18 @@ private:
if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<<nodep->prettyTypeName());
}
if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed");
if (AstNodeDType* dnodep = nodep->castNodeDType()) checkWidthMin(dnodep);
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
}
checkWidthMin(nodep);
nodep->iterateChildrenConst(*this);
iterateChildrenConst(nodep);
BrokenTable::setUnder(nodep,false);
}
virtual void visit(AstNodeAssign* nodep) {
processAndIterate(nodep);
if (v3Global.assertDTypesResolved()
&& nodep->brokeLhsMustBeLvalue()
&& nodep->lhsp()->castNodeVarRef()
&& !nodep->lhsp()->castNodeVarRef()->lvalue()) {
&& VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
}
}
@ -258,7 +257,7 @@ private:
public:
// CONSTUCTORS
explicit BrokenCheckVisitor(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~BrokenCheckVisitor() {}
};

View File

@ -113,9 +113,9 @@ void V3CCtors::evalAsserts() {
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = np->castVar()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (varp->isPrimaryIn() && !varp->isSc()) {
if (AstBasicDType* basicp = varp->dtypeSkipRefp()->castBasicDType()) {
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
int storedWidth = basicp->widthAlignBytes() * 8;
int lastWordWidth = varp->width() % storedWidth;
if (lastWordWidth != 0) {
@ -145,12 +145,12 @@ void V3CCtors::evalAsserts() {
void V3CCtors::cctorsAll() {
UINFO(2,__FUNCTION__<<": "<<endl);
evalAsserts();
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
// Process each module in turn
{
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* varp = np->castVar()) {
if (AstVar* varp = VN_CAST(np, Var)) {
if (!varp->isIfaceParent() && !varp->isIfaceRef()) {
var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true)));
}
@ -162,7 +162,7 @@ void V3CCtors::cctorsAll() {
(modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
"if (0 && vlSymsp && first) {} // Prevent unused\n");
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* coverp = np->castCoverDecl()) {
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
AstNode* backp = coverp->backp();
coverp->unlinkFrBack();
configure_coverage.add(coverp);

View File

@ -30,9 +30,9 @@
// Or, converts to a if/else tree.
// FUTURES:
// Large 16+ bit tables with constants and no masking (address muxes)
// Enter all into multimap, sort by value and use a tree of < and == compares.
// Enter all into std::multimap, sort by value and use a tree of < and == compares.
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
// Ignoring mask, check each value is unique (using multimap as above?)
// Ignoring mask, check each value is unique (using std::multimap as above?)
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
//
//*************************************************************************
@ -59,19 +59,16 @@ class CaseLintVisitor : public AstNVisitor {
private:
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// METHODS
VL_DEBUG_FUNC; // Declare debug()
virtual void visit(AstNodeCase* nodep) {
if (nodep->castCase() && nodep->castCase()->casex()) {
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
}
// Detect multiple defaults
bool hitDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (itemp->isDefault()) {
if (hitDefault) {
nodep->v3error("Multiple default statements in case statement.");
@ -83,9 +80,9 @@ private:
// Check for X/Z in non-casex statements
{
m_caseExprp = nodep;
nodep->exprp()->accept(*this);
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
itemp->condsp()->iterateAndNext(*this);
iterate(nodep->exprp());
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
iterateAndNextNull(itemp->condsp());
}
m_caseExprp = NULL;
}
@ -93,12 +90,12 @@ private:
virtual void visit(AstConst* nodep) {
// See also neverItem
if (m_caseExprp && nodep->num().isFourState()) {
if (m_caseExprp->castGenCase()) {
if (VN_IS(m_caseExprp, GenCase)) {
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casex()) {
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
// Don't sweat it, we already complained about casex in general
} else if (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez()
|| m_caseExprp->castCase()->caseInside())) {
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
if (nodep->num().isUnknown()) {
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
}
@ -108,13 +105,13 @@ private:
}
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit CaseLintVisitor(AstNodeCase* nodep) {
m_caseExprp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~CaseLintVisitor() {}
};
@ -140,22 +137,18 @@ private:
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
bool isCaseTreeFast(AstCase* nodep) {
int width = 0;
bool opaque = false;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = true;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
if (icondp->width() > width) width = icondp->width();
if (icondp->isDouble()) opaque = true;
if (!icondp->castConst()) width = CASE_BARF; // Can't parse; not a constant
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
m_caseItems++;
}
}
@ -170,10 +163,10 @@ private:
// Now pick up the values for each assignment
// We can cheat and use uint32_t's because we only support narrow case's
bool bitched = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
AstConst* iconstp = icondp->castConst();
AstConst* iconstp = VN_CAST(icondp, Const);
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
if (neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
@ -189,7 +182,7 @@ private:
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
} else if (!itemp->ignoreOverlap() && !bitched) {
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<hex<<i<<")");
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
bitched = true;
m_caseNoOverlapsAllCovered = false;
}
@ -206,7 +199,7 @@ private:
}
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<hex<<i<<")");
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
m_caseNoOverlapsAllCovered = false;
return false;
}
@ -214,8 +207,8 @@ private:
if (m_caseItems <= 3) return false; // Not worth simplifing
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
m_valueItem[i] = m_valueItem[i]->castCaseItem()->bodysp();
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
}
return true; // All is fine
}
@ -280,7 +273,7 @@ private:
if (debug()>=9) {
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
if (AstNode* itemp = m_valueItem[i]) {
UINFO(9,"Value "<<hex<<i<<" "<<itemp<<endl);
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
}
}
}
@ -310,7 +303,7 @@ private:
// the appropriate IF AND terms.
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
bool hadDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
if (!itemp->condsp()) {
// Default clause. Just make true, we'll optimize it away later
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
@ -324,14 +317,14 @@ private:
icondp->unlinkFrBack();
AstNode* condp = NULL; // Default is to use and1p/and2p
AstConst* iconstp = icondp->castConst();
AstConst* iconstp = VN_CAST(icondp, Const);
if (iconstp && neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
// For simplicity, make expression that is not equal, and let later
// optimizations remove it
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
} else if (AstInsideRange* irangep = icondp->castInsideRange()) {
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
// Similar logic in V3Width::visit(AstInside)
AstNode* ap = AstGte::newTyped(itemp->fileline(),
cexprp->cloneTree(false),
@ -387,7 +380,7 @@ private:
AstNode* grouprootp = NULL;
AstIf* groupnextp = NULL;
AstIf* itemnextp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
if (istmtsp) istmtsp->unlinkFrBackWithNext();
// Expressioned clause
@ -453,7 +446,7 @@ private:
// VISITORS
virtual void visit(AstCase* nodep) {
V3Case::caseLint(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
// It's a simple priority encoder or complete statement
@ -468,14 +461,19 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit CaseVisitor(AstNetlist* nodep) {
m_caseWidth = 0;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = false;
nodep->accept(*this);
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
m_valueItem[i] = NULL;
}
iterate(nodep);
}
virtual ~CaseVisitor() {
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);

View File

@ -65,11 +65,7 @@ private:
// STATE
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
UINFO(4," NeedCast "<<nodep<<endl);
@ -101,26 +97,26 @@ private:
// Otherwise a (uint64)(a>b) would return wrong value, as
// less than has undeterministic signedness.
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
&& !nodep->lhsp()->castCCast()) {
&& !VN_IS(nodep->lhsp(), CCast)) {
insertCast(nodep->lhsp(), VL_WORDSIZE);
}
}
// VISITORS
virtual void visit(AstNodeUniop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
}
virtual void visit(AstNodeBiop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1());
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
}
virtual void visit(AstNodeTriop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1()
| nodep->rhsp()->user1()
| nodep->thsp()->user1());
@ -129,12 +125,12 @@ private:
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
}
virtual void visit(AstCCast* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureLower32Cast(nodep);
nodep->user1(1);
}
virtual void visit(AstNegate* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->lhsp()->widthMin()==1) {
// We want to avoid a GCC "converting of negative value" warning
@ -147,9 +143,9 @@ private:
}
virtual void visit(AstVarRef* nodep) {
if (!nodep->lvalue()
&& !nodep->backp()->castCCast()
&& nodep->backp()->castNodeMath()
&& !nodep->backp()->castArraySel()
&& !VN_IS(nodep->backp(), CCast)
&& VN_IS(nodep->backp(), NodeMath)
&& !VN_IS(nodep->backp(), ArraySel)
&& nodep->backp()->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
@ -171,13 +167,13 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit CastVisitor(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~CastVisitor() {}
};

View File

@ -51,11 +51,7 @@
class CdcBaseVisitor : public AstNVisitor {
public:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
@ -77,6 +73,7 @@ public:
, m_asyncPath(false) {}
virtual ~CdcEitherVertex() {}
// ACCESSORS
virtual FileLine* fileline() const { return nodep()->fileline(); }
AstScope* scopep() const { return m_scopep; }
AstNode* nodep() const { return m_nodep; }
AstSenTree* srcDomainp() const { return m_srcDomainp; }
@ -135,7 +132,7 @@ private:
// NODE STATE
//Entire netlist:
// {statement}Node::user3 -> bool, indicating not hazard
ofstream* m_ofp; // Output file
std::ofstream* m_ofp; // Output file
string m_prefix;
virtual void visit(AstNode* nodep) {
@ -145,22 +142,22 @@ private:
*m_ofp<<nodep->prettyTypeName()<<" "<<endl;
string lastPrefix = m_prefix;
m_prefix = lastPrefix + "1:";
nodep->op1p()->iterateAndNext(*this);
iterateAndNextNull(nodep->op1p());
m_prefix = lastPrefix + "2:";
nodep->op2p()->iterateAndNext(*this);
iterateAndNextNull(nodep->op2p());
m_prefix = lastPrefix + "3:";
nodep->op3p()->iterateAndNext(*this);
iterateAndNextNull(nodep->op3p());
m_prefix = lastPrefix + "4:";
nodep->op4p()->iterateAndNext(*this);
iterateAndNextNull(nodep->op4p());
m_prefix = lastPrefix;
}
public:
// CONSTUCTORS
CdcDumpVisitor(AstNode* nodep, ofstream* ofp, const string& prefix) {
CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix) {
m_ofp = ofp;
m_prefix = prefix;
nodep->accept(*this);
iterate(nodep);
}
virtual ~CdcDumpVisitor() {}
};
@ -173,7 +170,7 @@ private:
size_t m_maxFilenameLen;
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Keeping line+filename lengths separate is much faster than calling ascii().length()
if (nodep->fileline()->lineno() >= m_maxLineno) {
m_maxLineno = nodep->fileline()->lineno()+1;
@ -187,7 +184,7 @@ public:
explicit CdcWidthVisitor(AstNode* nodep) {
m_maxLineno = 0;
m_maxFilenameLen = 0;
nodep->accept(*this);
iterate(nodep);
}
virtual ~CdcWidthVisitor() {}
// ACCESSORS
@ -225,7 +222,7 @@ private:
bool m_inDly; // In delayed assign
int m_inSenItem; // Number of senitems
string m_ofFilename; // Output filename
ofstream* m_ofp; // Output file
std::ofstream* m_ofp; // Output file
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
int m_filelineWidth; // Characters in longest fileline
@ -241,7 +238,7 @@ private:
m_logicVertexp->dstDomainp(m_domainp);
m_logicVertexp->dstDomainSet(true);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_logicVertexp = NULL;
if (0 && debug()>=9) {
@ -284,7 +281,7 @@ private:
nodep->v3warnCode(code,msg);
if (!told_file) {
told_file = 1;
cerr<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
std::cerr<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
}
*m_ofp<<"%Warning-"<<code.ascii()<<": "<<nodep->fileline()<<" "<<msg<<endl;
}
@ -448,7 +445,7 @@ private:
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
AstNode* nodep = vertexp->nodep();
string front = pad(filelineWidth(),nodep->fileline()->ascii()+":")+" "+prefix+" +- ";
if (nodep->castVarScope()) {
if (VN_IS(nodep, VarScope)) {
*m_ofp<<front<<"Variable: "<<nodep->prettyName()<<endl;
}
else {
@ -500,7 +497,7 @@ private:
if (ofp->fail()) v3fatal("Can't write "<<filename);
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
deque<string> report; // Sort output by name
std::deque<string> report; // Sort output by name
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
AstVar* varp = vvertexp->varScp()->varp();
@ -508,26 +505,26 @@ private:
const char* whatp = "wire";
if (varp->isPrimaryIO()) whatp = (varp->isInout()?"inout":varp->isInput()?"input":"output");
ostringstream os;
os.setf(ios::left);
std::ostringstream os;
os.setf(std::ios::left);
// Module name - doesn't work due to flattening having lost the original
// so we assume the modulename matches the filebasename
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
os<<" "<<setw(20)<<fname;
os<<" "<<setw(8)<<whatp;
os<<" "<<setw(40)<<vvertexp->varScp()->prettyName();
os<<" "<<std::setw(20)<<fname;
os<<" "<<std::setw(8)<<whatp;
os<<" "<<std::setw(40)<<vvertexp->varScp()->prettyName();
os<<" SRC=";
if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
os<<" DST=";
if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
os<<setw(0);
os<<std::setw(0);
os<<endl;
report.push_back(os.str());
}
}
}
stable_sort(report.begin(), report.end());
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
for (std::deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
*ofp << *it;
}
}
@ -541,7 +538,7 @@ private:
// Variables from flops already are domained
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed
typedef set<AstSenTree*> SenSet;
typedef std::set<AstSenTree*> SenSet;
SenSet senouts; // List of all sensitivities for new signal
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
if (vvertexp) {} // Unused
@ -584,7 +581,7 @@ private:
}
// If multiple domains need to do complicated optimizations
if (senedited) {
senoutp = V3Const::constifyExpensiveEdit(senoutp)->castSenTree();
senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree);
}
if (traceDests) {
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
@ -606,14 +603,14 @@ private:
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstScope* nodep) {
UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
m_logicVertexp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_scopep = NULL;
}
virtual void visit(AstActive* nodep) {
@ -657,14 +654,14 @@ private:
}
virtual void visit(AstAssignDly* nodep) {
m_inDly = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_inDly = false;
}
virtual void visit(AstSenItem* nodep) {
// Note we look at only AstSenItems, not AstSenGate's
// The gating term of a AstSenGate is normal logic
m_inSenItem = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_inSenItem = false;
}
virtual void visit(AstAlways* nodep) {
@ -691,21 +688,21 @@ private:
// Math that shouldn't cause us to clear hazard
virtual void visit(AstConst* nodep) { }
virtual void visit(AstReplicate* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstConcat* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNot* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstSel* nodep) {
if (!nodep->lsbp()->castConst()) setNodeHazard(nodep);
nodep->iterateChildren(*this);
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNodeSel* nodep) {
if (!nodep->bitp()->castConst()) setNodeHazard(nodep);
nodep->iterateChildren(*this);
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
iterateChildren(nodep);
}
// Ignores
@ -718,10 +715,10 @@ private:
// Default
virtual void visit(AstNodeMath* nodep) {
setNodeHazard(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -747,7 +744,7 @@ public:
*m_ofp<<"repeating recursively forwards to the destination flop(s).\n";
*m_ofp<<"%% Indicates the operator considered potentially hazardous.\n";
nodep->accept(*this);
iterate(nodep);
analyze();
if (debug()>=1) edgeReport(); // Not useful to users at the moment

View File

@ -85,7 +85,7 @@ public:
if (!m_tlChgFuncp->stmtsp()) {
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
} else {
AstCReturn* returnp = m_tlChgFuncp->stmtsp()->castCReturn();
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
// This is currently using AstLogOr which will shortcut the evaluation if
// any function returns true. This is likely what we want and is similar to the logic already in use
@ -159,7 +159,7 @@ private:
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
nodep->subDTypep()->skipRefp()->accept(*this);
iterate(nodep->subDTypep()->skipRefp());
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
@ -181,7 +181,7 @@ private:
}
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-");
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
" (probably with UNOPTFLAT warning suppressed): "
@ -209,7 +209,7 @@ public:
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
}
vscp->dtypep()->skipRefp()->accept(*this);
iterate(vscp->dtypep()->skipRefp());
m_varEqnp->deleteTree();
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
@ -231,11 +231,7 @@ private:
ChangedState* m_statep; // Shared state across visitors
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void genChangeDet(AstVarScope* vscp) {
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
@ -248,7 +244,7 @@ private:
if (nodep->isTop()) {
m_statep->m_topModp = nodep;
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstTopScope* nodep) {
@ -271,7 +267,7 @@ private:
m_statep->maybeCreateChgFuncp();
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVarScope* nodep) {
if (nodep->isCircular()) {
@ -287,14 +283,14 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
m_statep = statep;
nodep->accept(*this);
iterate(nodep);
}
virtual ~ChangedVisitor() {}
};

View File

@ -59,11 +59,7 @@ private:
AstNodeModule* m_modp;
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// Width resetting
int cppWidth(AstNode* nodep) {
@ -78,7 +74,7 @@ private:
if (old_dtypep->width() != width) {
// Since any given dtype's cppWidth() is the same, we can just
// remember one convertion for each, and reuse it
if (AstNodeDType* new_dtypep = old_dtypep->user3p()->castNodeDType()) {
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
nodep->dtypep(new_dtypep);
} else {
nodep->dtypeChgWidth(width, nodep->widthMin());
@ -90,8 +86,8 @@ private:
}
void computeCppWidth (AstNode* nodep) {
if (!nodep->user2() && nodep->hasDType()) {
if (nodep->castVar() || nodep->castNodeDType() // Don't want to change variable widths!
|| nodep->dtypep()->skipRefp()->castUnpackArrayDType()) { // Or arrays
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
} else {
setCppWidth(nodep);
}
@ -148,7 +144,7 @@ private:
// Base type handling methods
void operandBiop(AstNodeBiop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
@ -159,7 +155,7 @@ private:
//no setClean.. must do it in each user routine.
}
void operandTriop(AstNodeTriop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
@ -176,11 +172,11 @@ private:
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstNodeUniop* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
@ -204,12 +200,12 @@ private:
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstNodeMath* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstNodeAssign* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
@ -226,53 +222,53 @@ private:
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstUCFunc* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
setClean (nodep, false);
// We always clean, as we don't trust those pesky users.
if (!nodep->backp()->castAnd()) {
if (!VN_IS(nodep->backp(), And)) {
insertClean(nodep);
}
insureCleanAndNext (nodep->bodysp());
}
virtual void visit(AstTraceInc* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureCleanAndNext (nodep->valuep());
}
virtual void visit(AstTypedef* nodep) {
// No cleaning, or would loose pointer to enum
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstParamTypeDType* nodep) {
// No cleaning, or would loose pointer to enum
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// Control flow operators
virtual void visit(AstNodeCond* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureClean(nodep->condp());
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
}
virtual void visit(AstWhile* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureClean(nodep->condp());
}
virtual void visit(AstNodeIf* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureClean(nodep->condp());
}
virtual void visit(AstSFormatF* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureCleanAndNext (nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
}
virtual void visit(AstUCStmt* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureCleanAndNext (nodep->bodysp());
}
virtual void visit(AstCCall* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
insureCleanAndNext (nodep->argsp());
setClean (nodep, true);
}
@ -280,14 +276,15 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
computeCppWidth(nodep);
}
public:
// CONSTUCTORS
explicit CleanVisitor(AstNetlist* nodep) {
nodep->accept(*this);
m_modp = NULL;
iterate(nodep);
}
virtual ~CleanVisitor() {}
};

View File

@ -51,11 +51,7 @@
class GaterBaseVisitor : public AstNVisitor {
protected:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
@ -196,7 +192,7 @@ private:
bool m_isSimple; // Set false when we know it isn't simple
// METHODS
inline void okIterate(AstNode* nodep) {
if (m_isSimple) nodep->iterateChildren(*this);
if (m_isSimple) iterateChildren(nodep);
}
// VISITORS
virtual void visit(AstOr* nodep) { okIterate(nodep); }
@ -213,13 +209,13 @@ private:
virtual void visit(AstNode* nodep) {
m_isSimple = false;
//nodep->iterateChildren(*this);
//iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit GaterCondVisitor(AstNode* nodep) {
m_isSimple = true;
nodep->accept(*this);
iterate(nodep);
}
virtual ~GaterCondVisitor() {}
// PUBLIC METHODS
@ -283,7 +279,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
uint32_t childstate;
{
m_state = STATE_UNKNOWN;
nodep->iterateChildren(*this);
iterateChildren(nodep);
childstate = m_state;
}
m_state = oldstate;
@ -305,7 +301,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
}
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -315,7 +311,7 @@ public:
m_state = STATE_UNKNOWN;
m_cloning = false;
if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: ");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: ");
// If there's no statements we shouldn't have had a resulting graph
// vertex asking for this creation
@ -672,7 +668,7 @@ class GaterVisitor : public GaterBaseVisitor {
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
#else
// Make a SenGate
AstSenItem* oldsenitemsp = nodep->sensesp()->sensesp()->castSenItem();
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
@ -802,17 +798,17 @@ class GaterVisitor : public GaterBaseVisitor {
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
{
nodep->condp()->iterateAndNext(*this); // directlyUnder stays as-is
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
}
{
m_aboveVertexp = vertexp; // Vars will point at this edge
m_aboveTrue = VU_IF;
nodep->ifsp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
}
{
m_aboveVertexp = vertexp; // Vars will point at this edge
m_aboveTrue = VU_ELSE;
nodep->elsesp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
}
m_aboveVertexp = lastabovep;
m_aboveTrue = lasttrue;
@ -857,7 +853,7 @@ class GaterVisitor : public GaterBaseVisitor {
AstVarScope* lastvscp = m_stmtVscp;
bool lastpli = m_stmtInPli;
m_directlyUnderAlw = under;
if (nodep->castNodeStmt()) { // Restored below
if (VN_IS(nodep, NodeStmt)) { // Restored below
UINFO(9," Stmt: "<<nodep<<endl);
m_stmtVscp = NULL;
m_stmtInPli = false;
@ -868,10 +864,10 @@ class GaterVisitor : public GaterBaseVisitor {
scoreboardPli(nodep);
}
{
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_directlyUnderAlw = lastdua;
if (nodep->castNodeStmt()) { // Reset what set above; else propagate up to above statement
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
m_stmtVscp = lastvscp;
m_stmtInPli = lastpli;
}
@ -882,7 +878,7 @@ public:
explicit GaterVisitor(AstNetlist* nodep) {
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
clear();
nodep->accept(*this);
iterate(nodep);
}
void clear() {
m_nonopt = "";

View File

@ -68,13 +68,10 @@ private:
AstCFunc* m_settleFuncp; // Top settlement function we are creating
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
AstIf* m_lastIfp; // Last sensitivity if active to add more under
AstMTaskBody* m_mtaskBodyp; // Current mtask body
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
if (vscp->user1p()) return ((AstVarScope*)vscp->user1p());
@ -154,11 +151,11 @@ private:
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
// Nodep may be a list of elements; we need to walk it
AstNode* senEqnp = NULL;
for (AstNodeSenItem* senp = nodesp; senp; senp=senp->nextp()->castNodeSenItem()) {
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
AstNode* senOnep = NULL;
if (AstSenItem* itemp = senp->castSenItem()) {
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
senOnep = createSenItemEquation(itemp);
} else if (AstSenGate* itemp = senp->castSenGate()) {
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
senOnep = createSenGateEquation(itemp);
} else {
senp->v3fatalSrc("Strange node under sentree");
@ -239,7 +236,7 @@ private:
m_settleFuncp = funcp;
}
// Process the activates
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Done, clear so we can detect errors
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
clearLastSen();
@ -249,13 +246,13 @@ private:
virtual void visit(AstNodeModule* nodep) {
//UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp= NULL;
}
virtual void visit(AstScope* nodep) {
//UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (AstNode* movep = nodep->finalClksp()) {
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
movep->unlinkFrBackWithNext();
@ -311,7 +308,7 @@ private:
nodep->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstCFunc* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Link to global function
if (nodep->formCallTree()) {
UINFO(4, " formCallTree "<<nodep<<endl);
@ -342,6 +339,30 @@ private:
// Only empty blocks should be leftover on the non-top. Killem.
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else if (m_mtaskBodyp) {
UINFO(4," TR ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
UINFO(4," sameSenseTree\n");
} else {
clearLastSen();
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
m_mtaskBodyp->addStmtsp(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
} else if (nodep->hasInitial() || nodep->hasSettle()) {
nodep->v3fatalSrc("MTask should not include initial/settle logic.");
} else {
// Combo logic. Move statements to mtask func.
clearLastSen();
m_mtaskBodyp->addStmtsp(stmtsp);
}
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else {
UINFO(4," ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
@ -376,11 +397,25 @@ private:
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
}
virtual void visit(AstExecGraph* nodep) {
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
m_mtaskBodyp;
m_mtaskBodyp = VN_CAST(m_mtaskBodyp->nextp(), MTaskBody)) {
clearLastSen();
iterate(m_mtaskBodyp);
}
clearLastSen();
// Move the ExecGraph into _eval. Its location marks the
// spot where the graph will execute, relative to other
// (serial) logic in the cycle.
nodep->unlinkFrBack();
addToEvalLoop(nodep);
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -395,8 +430,9 @@ public:
m_lastSenp = NULL;
m_lastIfp = NULL;
m_scopep = NULL;
m_mtaskBodyp = NULL;
//
nodep->accept(*this);
iterate(nodep);
// Allow downstream modules to find _eval()
// easily without iterating through the tree.
nodep->evalp(m_evalFuncp);

View File

@ -60,13 +60,8 @@ protected:
// STATE
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
virtual ~CombBaseVisitor() {}
VL_DEBUG_FUNC; // Declare debug()
//***** optimization levels
static bool emptyFunctionDeletion() { return true; }
@ -86,7 +81,7 @@ class CombCallVisitor : CombBaseVisitor {
private:
// NODE STATE
bool m_find; // Find mode vs. delete mode
typedef multimap<AstCFunc*,AstCCall*> CallMmap;
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
CallMmap m_callMmap; // Associative array of {function}{call}
// METHODS
public:
@ -95,7 +90,7 @@ public:
if (newfuncp) {
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
} else UINFO(4, " Remove "<<oldfuncp<<endl);
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
@ -121,7 +116,7 @@ public:
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
}
void deleteCall(AstCCall* nodep) {
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
@ -141,7 +136,7 @@ private:
virtual void visit(AstNodeAssign* nodep) {}
virtual void visit(AstNodeMath* nodep) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
@ -150,7 +145,7 @@ public:
}
virtual ~CombCallVisitor() {}
void main(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
};
@ -165,12 +160,12 @@ private:
// VISITORS
virtual void visit(AstNode* nodep) {
nodep->user3(true);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit CombMarkVisitor(AstNode* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~CombMarkVisitor() {}
};
@ -214,18 +209,18 @@ private:
CombineState oldState = m_state;
{
m_state = STATE_HASH;
nodep->accept(*this);
iterate(nodep);
}
m_state = oldState;
}
void walkEmptyFuncs() {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* node1p = it->second;
AstCFunc* oldfuncp = node1p->castCFunc();
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
if (oldfuncp
&& oldfuncp->emptyBody()
&& !oldfuncp->dontCombine()) {
UINFO(5," EmptyFunc "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
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, NULL);
@ -238,7 +233,7 @@ private:
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
V3Hash hashval = it->first;
AstNode* node1p = it->second;
if (!node1p->castCFunc()) continue;
if (!VN_IS(node1p, CFunc)) continue;
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
AstNode* node2p = eqit->second;
@ -247,16 +242,16 @@ private:
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(node2p->castCFunc(), node1p->castCFunc());
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
// Replacement may promote a slow routine to fast path
if (!node2p->castCFunc()->slow()) node1p->castCFunc()->slow(false);
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
}
}
}
}
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
UINFO(5," DupFunc "<<hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
UINFO(5," and "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
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);
@ -265,7 +260,7 @@ private:
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
}
void replaceOnlyCallFunc(AstCCall* nodep) {
if (AstCFunc* oldfuncp = nodep->backp()->castCFunc()) {
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
//oldfuncp->dumpTree(cout,"MAYDEL: ");
if (nodep->nextp()==NULL
&& oldfuncp->initsp()==NULL
@ -288,7 +283,7 @@ private:
AstNode* bestLast1p = NULL;
AstNode* bestLast2p = NULL;
//
pair <V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
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;
@ -396,7 +391,7 @@ private:
//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.
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) {
UINFO(4," MOD "<<nodep<<endl);
@ -406,7 +401,7 @@ private:
m_hashed.clear();
// Compute hash of all statement trees in the function
m_state = STATE_HASH;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_state = STATE_IDLE;
if (debug()>=9) {
m_hashed.dumpFilePrefixed("combine");
@ -422,7 +417,7 @@ private:
// Walk the statements looking for large replicated code sections
if (statementCombine()) {
m_state = STATE_DUP;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_state = STATE_IDLE;
}
m_modp = NULL;
@ -433,7 +428,7 @@ private:
if (m_state == STATE_HASH) {
hashStatement(nodep); // Hash the entire function - it might be identical
} else if (m_state == STATE_DUP) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
m_funcp = NULL;
@ -453,16 +448,19 @@ private:
virtual void visit(AstTraceDecl*) {}
virtual void visit(AstTraceInc*) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit CombineVisitor(AstNetlist* nodep) {
m_modp=NULL;
m_funcp = NULL;
m_state = STATE_IDLE;
nodep->accept(*this);
m_modp = NULL;
m_funcp = NULL;
m_modNFuncs = 0;
m_walkLast1p = NULL;
m_walkLast2p = NULL;
iterate(nodep);
}
virtual ~CombineVisitor() {
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);

View File

@ -47,11 +47,11 @@ public:
return (m_on>rh.m_on);
}
};
ostream& operator<<(ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
class V3ConfigIgnores {
typedef multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
typedef map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
// MEMBERS
string m_lastFilename; // Last filename looked up

File diff suppressed because it is too large Load Diff

View File

@ -45,7 +45,7 @@
class CoverageVisitor : public AstNVisitor {
private:
// TYPES
typedef map<string,int> FileMap;
typedef std::map<string,int> FileMap;
struct ToggleEnt {
string m_comment; // Comment for coverage dump
@ -74,11 +74,7 @@ private:
string m_beginHier; // AstBegin hier name for user coverage points
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
const char* varIgnoreToggle(AstVar* nodep) {
// Return true if this shouldn't be traced
@ -132,7 +128,7 @@ private:
m_modp = nodep;
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
m_fileps.clear();
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
m_inModOff = true;
}
@ -142,12 +138,12 @@ private:
bool oldtog = m_inToggleOff;
{
m_inToggleOff = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_inToggleOff = oldtog;
}
virtual void visit(AstVar* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_modp && !m_inModOff && !m_inToggleOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
const char* disablep = varIgnoreToggle(nodep);
@ -200,7 +196,7 @@ private:
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
const ToggleEnt& above,
AstVar* varp, AstVar* chgVarp) { // Constant
if (AstBasicDType* bdtypep = dtypep->castBasicDType()) {
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
if (bdtypep->isRanged()) {
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
int index_code = index_docs - bdtypep->lsb();
@ -218,7 +214,7 @@ private:
varp, chgVarp);
}
}
else if (AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType()) {
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
int index_code = index_docs - adtypep->lsb();
ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]",
@ -230,7 +226,7 @@ private:
newent.cleanup();
}
}
else if (AstPackArrayDType* adtypep = dtypep->castPackArrayDType()) {
else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
int index_code = index_docs - adtypep->lsb();
@ -245,9 +241,9 @@ private:
newent.cleanup();
}
}
else if (AstStructDType* adtypep = dtypep->castStructDType()) {
else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
// For now it's packed, so similar to array
for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
int index_code = itemp->lsb();
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
@ -261,7 +257,7 @@ private:
newent.cleanup();
}
}
else if (AstUnionDType* adtypep = dtypep->castUnionDType()) {
else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
// Arbitrarially handle only the first member of the union
if (AstMemberDType* itemp = adtypep->membersp()) {
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
@ -284,11 +280,11 @@ private:
UINFO(4," IF: "<<nodep<<endl);
if (m_checkBlock) {
// An else-if. When we iterate the if, use "elsif" marking
bool elsif = (nodep->elsesp()->castIf()
&& !nodep->elsesp()->castIf()->nextp());
if (elsif) nodep->elsesp()->castIf()->user1(true);
bool elsif = (VN_IS(nodep->elsesp(), If)
&& !VN_CAST(nodep->elsesp(), If)->nextp());
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
//
nodep->ifsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->ifsp());
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
@ -301,7 +297,7 @@ private:
// Don't do empty else's, only empty if/case's
if (nodep->elsesp()) {
m_checkBlock = true;
nodep->elsesp()->iterateAndNext(*this);
iterateAndNextNull(nodep->elsesp());
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
@ -317,7 +313,7 @@ private:
UINFO(4," CASEI: "<<nodep<<endl);
if (m_checkBlock && !m_inModOff
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
if (m_checkBlock) { // if the case body didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
@ -328,7 +324,7 @@ private:
virtual void visit(AstPslCover* nodep) {
UINFO(4," PSLCOVER: "<<nodep<<endl);
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->coverincp()) {
// Note the name may be overridden by V3Assert processing
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
@ -346,7 +342,7 @@ private:
m_checkBlock = false;
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
} else {
if (m_checkBlock) nodep->iterateChildren(*this);
if (m_checkBlock) iterateChildren(nodep);
}
}
virtual void visit(AstBegin* nodep) {
@ -362,7 +358,7 @@ private:
if (nodep->name()!="") {
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_beginHier = oldHier;
m_inToggleOff = oldtog;
@ -372,7 +368,7 @@ private:
virtual void visit(AstNode* nodep) {
// Default: Just iterate
if (m_checkBlock) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_checkBlock = true; // Reset as a child may have cleared it
}
}
@ -386,7 +382,7 @@ public:
m_beginHier = "";
m_inToggleOff = false;
m_inModOff = true;
rootp->iterateChildren(*this);
iterateChildren(rootp);
}
virtual ~CoverageVisitor() {}
};

View File

@ -46,7 +46,7 @@ private:
//AstUser4InUse In V3Hashed
// TYPES
typedef vector<AstCoverToggle*> ToggleList;
typedef std::vector<AstCoverToggle*> ToggleList;
// STATE
ToggleList m_toggleps; // List of of all AstCoverToggle's
@ -54,11 +54,7 @@ private:
V3Double0 m_statToggleJoins; // Statistic tracking
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void detectDuplicates() {
UINFO(9,"Finding duplicates\n");
@ -82,7 +78,7 @@ private:
AstNode* duporigp = hashed.iteratorNodep(dupit);
// 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:
AstCoverToggle* removep = duporigp->backp()->castCoverToggle();
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
@ -105,24 +101,24 @@ private:
// VISITORS
virtual void visit(AstNetlist* nodep) {
// Find all Coverage's
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Simplify
detectDuplicates();
}
virtual void visit(AstCoverToggle* nodep) {
m_toggleps.push_back(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//--------------------
virtual void visit(AstNodeMath* nodep) {} // Accelerate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit CoverageJoinVisitor(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~CoverageJoinVisitor() {
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);

View File

@ -59,18 +59,18 @@ private:
// ** Shared with DeadVisitor **
// VISITORS
virtual void visit(AstCell* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->modp()->user1Inc(-1);
}
//-----
virtual void visit(AstNodeMath* nodep) {} // Accelerate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit DeadModVisitor(AstNodeModule* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~DeadModVisitor() {}
};
@ -89,15 +89,15 @@ private:
AstUser1InUse m_inuser1;
// TYPES
typedef multimap<AstVarScope*,AstNodeAssign*> AssignMap;
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
// STATE
AstNodeModule* m_modp; // Current module
vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
std::vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
std::vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
std::vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
std::vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
AssignMap m_assignMap; // List of all simple assignments for each variable
bool m_elimUserVars; // Allow removal of user's vars
bool m_elimDTypes; // Allow removal of DTypes
@ -106,11 +106,7 @@ private:
bool m_sideEffect; // Side effects discovered in assign RHS
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void checkAll(AstNode* nodep) {
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
@ -125,7 +121,7 @@ private:
void checkDType(AstNodeDType* nodep) {
if (!nodep->generic() // Don't remove generic types
&& m_elimDTypes // dtypes stick around until post-widthing
&& !nodep->castMemberDType() // Keep member names iff upper type exists
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
) {
m_dtypesp.push_back(nodep);
}
@ -138,18 +134,18 @@ private:
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
if (!nodep->dead()) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
}
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
}
virtual void visit(AstScope* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
@ -158,14 +154,14 @@ private:
}
}
virtual void visit(AstCell* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
m_cellsp.push_back(nodep);
nodep->modp()->user1Inc();
}
virtual void visit(AstNodeVarRef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varScopep()) {
nodep->varScopep()->user1Inc();
@ -180,7 +176,7 @@ private:
}
}
virtual void visit(AstNodeFTaskRef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
@ -188,7 +184,7 @@ private:
}
}
virtual void visit(AstRefDType* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
if (nodep->packagep()) {
@ -197,12 +193,12 @@ private:
}
}
virtual void visit(AstNodeDType* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
}
virtual void visit(AstEnumItemRef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->packagep()) {
if (m_elimCells) nodep->packagep(NULL);
@ -211,7 +207,7 @@ private:
checkAll(nodep);
}
virtual void visit(AstModport* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_elimCells) {
if (!nodep->varsp()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
@ -221,7 +217,7 @@ private:
checkAll(nodep);
}
virtual void visit(AstTypedef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_elimCells && !nodep->attrPublic()) {
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
@ -229,10 +225,10 @@ private:
checkAll(nodep);
// Don't let packages with only public variables disappear
// Normal modules may disappear, e.g. if they are parameterized then removed
if (nodep->attrPublic() && m_modp && m_modp->castPackage()) m_modp->user1Inc();
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
}
virtual void visit(AstVarScope* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
if (mightElimVar(nodep->varp())) {
@ -240,9 +236,9 @@ private:
}
}
virtual void visit(AstVar* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
if (nodep->isSigPublic() && m_modp && m_modp->castPackage()) m_modp->user1Inc();
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
if (mightElimVar(nodep)) {
m_varsp.push_back(nodep);
}
@ -251,16 +247,16 @@ private:
// See if simple assignments to variables may be eliminated because that variable is never used.
// Similar code in V3Life
m_sideEffect = false;
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
checkAll(nodep);
// Has to be direct assignment without any EXTRACTing.
AstVarRef* varrefp = nodep->lhsp()->castVarRef();
AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef);
if (varrefp && !m_sideEffect
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
checkAll(varrefp); // Must track reference to dtype()
} else { // Track like any other statement
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
}
checkAll(nodep);
}
@ -268,7 +264,7 @@ private:
//-----
virtual void visit(AstNode* nodep) {
if (nodep->isOutputter()) m_sideEffect=true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkAll(nodep);
}
@ -281,7 +277,7 @@ private:
retry=false;
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = modp->nextp()->castNodeModule();
nextmodp = VN_CAST(modp->nextp(), NodeModule);
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4," Dead module "<<modp<<endl);
@ -307,7 +303,7 @@ private:
void deadCheckScope() {
for (bool retry=true; retry; ) {
retry = false;
for (vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();++it) {
for (std::vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();++it) {
AstScope* scp = *it;
if (!scp)
continue;
@ -326,7 +322,7 @@ private:
}
void deadCheckCells() {
for (vector<AstCell*>::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) {
for (std::vector<AstCell*>::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) {
AstCell* cellp = *it;
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
cellp->modp()->user1Inc(-1);
@ -337,11 +333,11 @@ private:
void deadCheckVar() {
// Delete any unused varscopes
for (vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
for (std::vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
AstVarScope* vscp = *it;
if (vscp->user1() == 0) {
UINFO(4," Dead "<<vscp<<endl);
pair <AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
AstNodeAssign* assp = itr->second;
UINFO(4," Dead assign "<<assp<<endl);
@ -355,7 +351,7 @@ private:
}
for (bool retry=true; retry; ) {
retry = false;
for (vector<AstVar *>::iterator it = m_varsp.begin(); it != m_varsp.end();++it) {
for (std::vector<AstVar *>::iterator it = m_varsp.begin(); it != m_varsp.end();++it) {
AstVar* varp = *it;
if (!varp)
continue;
@ -370,15 +366,16 @@ private:
}
}
}
for (vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) {
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) {
if ((*it)->user1() == 0) {
AstNodeClassDType *classp;
// It's possible that there if a reference to each individual member, but
// not to the dtype itself. Check and don't remove the parent dtype if
// members are still alive.
if ((classp = (*it)->castNodeClassDType())) {
if ((classp = VN_CAST((*it), NodeClassDType))) {
bool cont = true;
for (AstMemberDType *memberp = classp->membersp(); memberp; memberp = memberp->nextp()->castMemberDType()) {
for (AstMemberDType *memberp = classp->membersp();
memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) {
if (memberp->user1() != 0) {
cont = false;
break;
@ -404,7 +401,7 @@ public:
// Prepare to remove some datatypes
nodep->typeTablep()->clearCache();
// Operate on whole netlist
nodep->accept(*this);
iterate(nodep);
deadCheckVar();
// We only elimate scopes when in a flattened structure

View File

@ -98,18 +98,14 @@ private:
bool m_inDly; // True in delayed assignments
bool m_inLoop; // True in for loops
bool m_inInitial; // True in intial blocks
typedef std::map<pair<AstNodeModule*,string>,AstVar*> VarMap;
typedef std::map<std::pair<AstNodeModule*,string>,AstVar*> VarMap;
VarMap m_modVarMap; // Table of new var names created under module
V3Double0 m_statSharedSet;// Statistic tracking
typedef std::map<AstVarScope*,int> ScopeVecMap;
ScopeVecMap m_scopeVecMap; // Next var number for each scope
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void markVarUsage(AstVarScope* nodep, uint32_t flags) {
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
@ -192,36 +188,36 @@ private:
AstNode* newlhsp = NULL; // NULL = unlink old assign
AstSel* bitselp = NULL;
AstArraySel* arrayselp = NULL;
if (lhsp->castSel()) {
bitselp = lhsp->castSel();
arrayselp = bitselp->fromp()->castArraySel();
if (VN_IS(lhsp, Sel)) {
bitselp = VN_CAST(lhsp, Sel);
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
} else {
arrayselp = lhsp->castArraySel();
arrayselp = VN_CAST(lhsp, ArraySel);
}
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
if (arrayselp->dtypep()->skipRefp()->castUnpackArrayDType()) {
if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) {
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
}
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
//
//=== Dimensions: __Vdlyvdim__
deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
AstNode* dimselp=arrayselp;
for (; dimselp->castArraySel(); dimselp=dimselp->castArraySel()->fromp()) {
AstNode* valp = dimselp->castArraySel()->bitp()->unlinkFrBack();
for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) {
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
dimvalp.push_front(valp);
}
AstVarRef* varrefp = dimselp->castVarRef();
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
varrefp->unlinkFrBack();
AstVar* oldvarp = varrefp->varp();
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
//
deque<AstNode*> dimreadps; // Read value for each dimension of assignment
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
AstNode* dimp = dimvalp[dimension];
if (dimp->castConst()) { // bit = const, can just use it
if (VN_IS(dimp, Const)) { // bit = const, can just use it
dimreadps.push_front(dimp);
} else {
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
@ -240,7 +236,7 @@ private:
AstNode* bitreadp=NULL; // Code to read Vdlyvlsb
if (bitselp) {
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
if (bitselp->fromp()->castConst()) {// vlsb = constant, can just push constant into where we use it
if (VN_IS(bitselp->fromp(), Const)) { // vlsb = constant, can just push constant into where we use it
bitreadp = lsbvaluep;
} else {
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
@ -255,7 +251,7 @@ private:
//
//=== Value: __Vdlyvval__
AstNode* valreadp; // Code to read Vdlyvval
if (nodep->rhsp()->castConst()) { // vval = constant, can just push constant into where we use it
if (VN_IS(nodep->rhsp(), Const)) { // vval = constant, can just push constant into where we use it
valreadp = nodep->rhsp()->unlinkFrBack();
} else {
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
@ -273,7 +269,7 @@ private:
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
// This is good for code like:
// for (i=0; i<5; i++) vector[i] <= something;
setvscp = nodep->user3p()->castVarScope();
setvscp = VN_CAST(nodep->user3p(), VarScope);
++m_statSharedSet;
} else { // Create new one
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
@ -308,9 +304,9 @@ private:
// Build "IF (changeit) ...
UINFO(9," For "<<setvscp<<endl);
UINFO(9," & "<<varrefp<<endl);
AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castAlwaysPost();
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
if (finalp) {
AstActive* oldactivep = finalp->user2p()->castActive();
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
checkActivePost(varrefp, oldactivep);
if (setinitp) oldactivep->addStmtsp(setinitp);
} else { // first time we've dealt with this memory
@ -326,7 +322,7 @@ private:
if (finalp->user3p() == setvscp) {
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
// we can share the IF statement too
postLogicp = finalp->user4p()->castIf();
postLogicp = VN_CAST(finalp->user4p(), If);
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
postLogicp = new AstIf (nodep->fileline(),
@ -346,16 +342,16 @@ private:
virtual void visit(AstNetlist* nodep) {
//VV***** We reset all userp() on the netlist
m_modVarMap.clear();
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstScope* nodep) {
UINFO(4," MOD "<<nodep<<endl);
AstNode::user3ClearTree();
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
m_cfuncp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_cfuncp = NULL;
}
virtual void visit(AstActive* nodep) {
@ -363,16 +359,16 @@ private:
bool oldinit = m_inInitial;
m_inInitial = nodep->hasInitial();
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_inInitial = oldinit;
}
virtual void visit(AstAssignDly* nodep) {
m_inDly = true;
m_nextDlyp = nodep->nextp()->castAssignDly(); // Next assignment in same block, maybe NULL.
m_nextDlyp = VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL.
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
if (nodep->lhsp()->castArraySel()
|| (nodep->lhsp()->castSel()
&& nodep->lhsp()->castSel()->fromp()->castArraySel())) {
if (VN_IS(nodep->lhsp(), ArraySel)
|| (VN_IS(nodep->lhsp(), Sel)
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newlhsp = createDlyArray(nodep, lhsp);
if (m_inLoop) nodep->v3warn(BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
@ -384,7 +380,7 @@ private:
lhsp->deleteTree(); VL_DANGLING(lhsp);
}
else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_inDly = false;
m_nextDlyp = NULL;
@ -401,9 +397,9 @@ private:
}
AstVarScope* oldvscp = nodep->varScopep();
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
AstVarScope* dlyvscp = oldvscp->user1p()->castVarScope();
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
if (dlyvscp) { // Multiple use of delayed variable
AstActive* oldactivep = dlyvscp->user2p()->castActive();
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
checkActivePost(nodep, oldactivep);
}
if (!dlyvscp) { // First use of this delayed variable
@ -445,14 +441,14 @@ private:
virtual void visit(AstWhile* nodep) {
bool oldloop = m_inLoop;
m_inLoop = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_inLoop = oldloop;
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -465,7 +461,7 @@ public:
m_inLoop = false;
m_inInitial = false;
nodep->accept(*this);
iterate(nodep);
}
virtual ~DelayedVisitor() {
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);

View File

@ -52,11 +52,7 @@ private:
int m_maxdepth; // Maximum depth in an expression
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void createDeepTemp(AstNode* nodep) {
UINFO(6," Deep "<<nodep<<endl);
@ -89,21 +85,21 @@ private:
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_funcp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
m_funcp = nodep;
m_depth = 0;
m_maxdepth = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_funcp = NULL;
}
void visitStmt(AstNodeStmt* nodep) {
m_depth = 0;
m_maxdepth = 0;
m_stmtp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_stmtp = NULL;
}
virtual void visit(AstNodeStmt* nodep) {
@ -116,13 +112,13 @@ private:
// We have some operator defines that use 2 parens, so += 2.
m_depth += 2;
if (m_depth>m_maxdepth) m_maxdepth=m_depth;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_depth -= 2;
if (m_stmtp
&& (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
&& (m_maxdepth-m_depth) > v3Global.opt.compLimitParens()
&& !nodep->backp()->castNodeStmt() // Not much point if we're about to use it
&& !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it
) {
m_maxdepth = m_depth;
createDeepTemp(nodep);
@ -141,7 +137,7 @@ private:
}
virtual void visit(AstUCFunc* nodep) {
needNonStaticFunc(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstUCStmt* nodep) {
needNonStaticFunc(nodep);
@ -152,7 +148,7 @@ private:
// Default: Just iterate
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -164,7 +160,7 @@ public:
m_depth=0;
m_maxdepth=0;
//
nodep->accept(*this);
iterate(nodep);
}
virtual ~DepthVisitor() {}
};

View File

@ -49,11 +49,7 @@ private:
int m_deepNum; // How many functions made
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
AstCFunc* createDeepFunc(AstNode* nodep) {
AstNRelinker relinkHandle;
@ -80,7 +76,7 @@ private:
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_deepNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
@ -90,7 +86,7 @@ private:
{
m_depth = 0;
m_funcp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_depth = lastDepth;
m_funcp = lastFuncp;
@ -98,16 +94,16 @@ private:
void visitStmt(AstNodeStmt* nodep) {
m_depth++;
if (m_depth > v3Global.opt.compLimitBlocks()
&& !nodep->castCCall()) { // Already done
&& !VN_IS(nodep, CCall)) { // Already done
UINFO(4, "DeepBlocks "<<m_depth<<" "<<nodep<<endl);
AstNode* backp = nodep->backp(); // Only for debug
if (debug()>=9) backp->dumpTree(cout,"- pre : ");
AstCFunc* funcp = createDeepFunc(nodep);
funcp->accept(*this);
iterate(funcp);
if (debug()>=9) backp->dumpTree(cout,"- post: ");
if (debug()>=9) funcp->dumpTree(cout,"- func: ");
} else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_depth--;
}
@ -120,7 +116,7 @@ private:
// Default: Just iterate
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -131,7 +127,7 @@ public:
m_depth = 0;
m_deepNum = 0;
//
nodep->accept(*this);
iterate(nodep);
}
virtual ~DepthBlockVisitor() {}
};

View File

@ -48,7 +48,7 @@ private:
AstUser1InUse m_inuser1;
// TYPES
typedef multimap<string,AstCFunc*> FuncMmap;
typedef std::multimap<string,AstCFunc*> FuncMmap;
// STATE
AstNodeModule* m_modp; // Current module
@ -58,17 +58,13 @@ private:
FuncMmap m_modFuncs; // Name of public functions added
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
static bool modIsSingleton(AstNodeModule* modp) {
// True iff there's exactly one instance of this module in the design.
int instances = 0;
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
if (stmtp->castScope()) {
if (VN_IS(stmtp, Scope)) {
if (++instances > 1) { return false; }
}
}
@ -187,7 +183,7 @@ private:
funcp->declPrivate(true);
AstNode* argsp = NULL;
for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
if (AstVar* portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
AstNode* newp = new AstVarRef(portp->fileline(), portp, portp->isOutput());
if (argsp) argsp = argsp->addNextNull(newp);
@ -234,13 +230,13 @@ private:
m_modp = nodep;
m_modFuncs.clear();
m_modSingleton = modIsSingleton(m_modp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
makePublicFuncWrappers();
m_modp = NULL;
}
virtual void visit(AstScope* nodep) {
m_scopep = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_scopep = NULL;
}
virtual void visit(AstVarScope* nodep) {
@ -249,7 +245,7 @@ private:
pushDeletep(nodep);
}
virtual void visit(AstNodeVarRef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Convert the hierch name
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
bool hierThis;
@ -259,7 +255,7 @@ private:
}
virtual void visit(AstCCall* nodep) {
//UINFO(9," "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Convert the hierch name
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope");
@ -271,7 +267,7 @@ private:
virtual void visit(AstCFunc* nodep) {
if (!nodep->user1()) {
m_needThis = false;
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user1(true);
if (m_needThis) {
nodep->isStatic(false);
@ -292,7 +288,7 @@ private:
}
virtual void visit(AstVar*) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
@ -301,7 +297,7 @@ public:
m_scopep(NULL),
m_modSingleton(false),
m_needThis(false) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~DescopeVisitor() {}
};

File diff suppressed because it is too large Load Diff

View File

@ -66,12 +66,12 @@ public:
v3Global.rootp()->addFilesp(cfilep);
return cfilep;
}
string cFuncArgs(AstCFunc* nodep) {
string cFuncArgs(const AstCFunc* nodep) {
// Return argument list for given C function
string args = nodep->argTypes();
// Might be a user function with argument list.
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
if (args != "") args+= ", ";
if (nodep->dpiImport() || nodep->dpiExportWrapper())
@ -102,13 +102,13 @@ private:
// VISITORS
virtual void visit(AstNode* nodep) {
m_count++;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit EmitCBaseCounterVisitor(AstNode* nodep) {
m_count = 0;
nodep->accept(*this);
iterate(nodep);
}
virtual ~EmitCBaseCounterVisitor() {}
int count() const { return m_count; }

View File

@ -51,13 +51,13 @@ class EmitCInlines : EmitCBaseVisitor {
virtual void visit(AstNodeStmt*) {}
// Default
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//---------------------------------------
// ACCESSORS
public:
explicit EmitCInlines(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
if (v3Global.needHInlines()) {
emitInt();
}

View File

@ -57,11 +57,11 @@ class EmitCSyms : EmitCBaseVisitor {
ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, AstNodeModule* modp, AstScope* scopep)
: m_scopeName(scopeName), m_varBasePretty(varBasePretty), m_varp(varp), m_modp(modp), m_scopep(scopep) {}
};
typedef map<string,ScopeFuncData> ScopeFuncs;
typedef map<string,ScopeVarData> ScopeVars;
typedef map<string,ScopeNameData> ScopeNames;
typedef pair<AstScope*,AstNodeModule*> ScopeModPair;
typedef pair<AstNodeModule*,AstVar*> ModVarPair;
typedef std::map<string,ScopeFuncData> ScopeFuncs;
typedef std::map<string,ScopeVarData> ScopeVars;
typedef std::map<string,ScopeNameData> ScopeNames;
typedef std::pair<AstScope*,AstNodeModule*> ScopeModPair;
typedef std::pair<AstNodeModule*,AstVar*> ModVarPair;
struct CmpName {
inline bool operator () (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const {
return lhsp.first->name() < rhsp.first->name();
@ -80,9 +80,9 @@ class EmitCSyms : EmitCBaseVisitor {
// STATE
AstCFunc* m_funcp; // Current function
AstNodeModule* m_modp; // Current module
vector<ScopeModPair> m_scopes; // Every scope by module
vector<AstCFunc*> m_dpis; // DPI functions
vector<ModVarPair> m_modVars; // Each public {mod,var}
std::vector<ScopeModPair> m_scopes; // Every scope by module
std::vector<AstCFunc*> m_dpis; // DPI functions
std::vector<ModVarPair> m_modVars; // Each public {mod,var}
ScopeNames m_scopeNames; // Each unique AstScopeName
ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func}
ScopeVars m_scopeVars; // Each {scope,public-var}
@ -113,9 +113,9 @@ class EmitCSyms : EmitCBaseVisitor {
// We didn'e have all m_scopes loaded when we encountered variables, so expand them now
// It would be less code if each module inserted its own variables.
// Someday. For now public isn't common.
for (vector<ScopeModPair>::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) {
for (std::vector<ScopeModPair>::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) {
AstScope* scopep = itsc->first; AstNodeModule* smodp = itsc->second;
for (vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) {
for (std::vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) {
AstNodeModule* modp = it->first;
AstVar* varp = it->second;
if (modp == smodp) {
@ -166,7 +166,7 @@ class EmitCSyms : EmitCBaseVisitor {
// VISITORS
virtual void visit(AstNetlist* nodep) {
// Collect list of scopes
nodep->iterateChildren(*this);
iterateChildren(nodep);
varsExpand();
// Sort by names, so line/process order matters less
@ -185,7 +185,7 @@ class EmitCSyms : EmitCBaseVisitor {
nameCheck(nodep);
m_modp = nodep;
m_labelNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstScope* nodep) {
@ -210,7 +210,7 @@ class EmitCSyms : EmitCBaseVisitor {
}
virtual void visit(AstVar* nodep) {
nameCheck(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isSigUserRdPublic()
&& !nodep->isParam()) { // The VPI functions require a pointer to allow modification, but parameters are constants
m_modVars.push_back(make_pair(m_modp, nodep));
@ -224,7 +224,7 @@ class EmitCSyms : EmitCBaseVisitor {
}
virtual void visit(AstJumpLabel* nodep) {
nodep->labelNum(++m_labelNum);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
nameCheck(nodep);
@ -232,14 +232,14 @@ class EmitCSyms : EmitCBaseVisitor {
m_dpis.push_back(nodep);
}
m_funcp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_funcp = NULL;
}
// NOPs
virtual void visit(AstConst*) {}
// Default
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//---------------------------------------
// ACCESSORS
@ -249,7 +249,7 @@ public:
m_modp = NULL;
m_coverBins = 0;
m_labelNum = 0;
nodep->accept(*this);
iterate(nodep);
}
};
@ -279,13 +279,13 @@ void EmitCSyms::emitSymHdr() {
// for
puts("\n// INCLUDE MODULE CLASSES\n");
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
puts("#include \""+modClassName(nodep)+".h\"\n");
}
if (v3Global.dpi()) {
puts ("\n// DPI TYPES for DPI Export callbacks (Internal use)\n");
map<string,int> types; // Remove duplicates and sort
std::map<string,int> types; // Remove duplicates and sort
for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) {
AstCFunc* funcp = it->second.m_funcp;
if (funcp->dpiExport()) {
@ -293,7 +293,7 @@ void EmitCSyms::emitSymHdr() {
types["typedef void (*"+cbtype+") ("+cFuncArgs(funcp)+");\n"] = 1;
}
}
for (map<string,int>::iterator it = types.begin(); it != types.end(); ++it) {
for (std::map<string,int>::iterator it = types.begin(); it != types.end(); ++it) {
puts(it->first);
}
}
@ -310,7 +310,7 @@ void EmitCSyms::emitSymHdr() {
puts("bool __Vm_didInit;\n");
puts("\n// SUBCELL STATE\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
if (modp->isTop()) {
ofp()->printf("%-30s ", (modClassName(modp)+"*").c_str());
@ -370,7 +370,7 @@ void EmitCSyms::emitSymImp() {
// Includes
puts("#include \""+symClassName()+".h\"\n");
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
puts("#include \""+modClassName(nodep)+".h\"\n");
}
@ -386,7 +386,7 @@ void EmitCSyms::emitSymImp() {
puts("\t, __Vm_didInit(false)\n");
puts("\t// Setup submodule names\n");
char comma=',';
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
if (modp->isTop()) {
} else {
@ -404,7 +404,7 @@ void EmitCSyms::emitSymImp() {
puts("// Pointer to top level\n");
puts("TOPp = topp;\n");
puts("// Setup each module's pointers to their submodules\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
if (!modp->isTop()) {
string arrow = scopep->name();
@ -421,7 +421,7 @@ void EmitCSyms::emitSymImp() {
puts("// Setup each module's pointer back to symbol table (for public functions)\n");
puts("TOPp->__Vconfigure(this, true);\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
if (!modp->isTop()) {
// first is used by AstCoverDecl's call to __vlCoverInsert
@ -482,10 +482,10 @@ void EmitCSyms::emitSymImp() {
}
for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) {
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
bounds += " ,"; bounds += cvtToStr(adtypep->msb());
bounds += ","; bounds += cvtToStr(adtypep->lsb());
if (dtypep->castPackArrayDType()) pdim++; else udim++;
if (VN_IS(dtypep, PackArrayDType)) pdim++; else udim++;
dtypep = adtypep->subDTypep();
}
else break; // AstBasicDType - nothing below, 1
@ -534,7 +534,7 @@ void EmitCSyms::emitSymImp() {
}
puts( "os"+op+"__Vm_didInit;\n");
puts( "// SUBCELL STATE\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
if (!modp->isTop()) {
puts( scopep->nameDotless()+"."+funcname+"(os);\n");
@ -571,7 +571,7 @@ void EmitCSyms::emitDpiHdr() {
int firstExp = 0;
int firstImp = 0;
for (vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
for (std::vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
AstCFunc* nodep = *it;
if (nodep->dpiExportWrapper()) {
if (!firstExp++) puts("\n// DPI EXPORTS\n");
@ -619,7 +619,7 @@ void EmitCSyms::emitDpiImp() {
puts("#include \""+topClassName()+".h\"\n");
puts("\n");
for (vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
for (std::vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
AstCFunc* nodep = *it;
if (nodep->dpiExportWrapper()) {
puts("#ifndef _VL_DPIDECL_"+nodep->name()+"\n");
@ -629,7 +629,7 @@ void EmitCSyms::emitDpiImp() {
puts("return "+topClassName()+"::"+nodep->name()+"(");
string args;
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
if (const AstVar* portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
if (args != "") args+= ", ";
args += portp->name();

View File

@ -40,11 +40,7 @@ class EmitMkVisitor : public EmitCBaseVisitor {
public:
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void putMakeClassEntry(V3OutMkFile& of, const string& name) {
of.puts("\t"+V3Os::filenameNonDirExt(name)+" \\\n");
@ -98,11 +94,14 @@ public:
putMakeClassEntry(of, "verilated_vcd_sc.cpp");
}
}
if (v3Global.opt.mtasks()) {
putMakeClassEntry(of, "verilated_threads.cpp");
}
}
else if (support==2 && slow) {
}
else {
for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=nodep->nextp()->castCFile()) {
for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=VN_CAST(nodep->nextp(), CFile)) {
if (nodep->source() && nodep->slow()==(slow!=0) && nodep->support()==(support!=0)) {
putMakeClassEntry(of, nodep->name());
}

View File

@ -41,11 +41,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
AstSenTree* m_sensesp;
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
virtual void puts(const string& str) = 0;
virtual void putbs(const string& str) = 0;
@ -63,11 +59,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
// VISITORS
virtual void visit(AstNetlist* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNodeModule* nodep) {
putfs(nodep, nodep->verilogKwd()+" "+modClassName(nodep)+";\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
putqs(nodep, "end"+nodep->verilogKwd()+"\n");
}
virtual void visit(AstNodeFTask* nodep) {
@ -76,7 +72,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
puts(nodep->prettyName());
puts(";\n");
putqs(nodep, "begin\n"); // Only putfs the first time for each visitor; later for same node is putqs
nodep->stmtsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->stmtsp());
putqs(nodep, "end\n");
}
@ -86,64 +82,64 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
} else {
putbs("begin : "+nodep->name()+"\n");
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
puts("end\n");
}
virtual void visit(AstGenerate* nodep) {
putfs(nodep, "generate\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstFinal* nodep) {
putfs(nodep, "final begin\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstInitial* nodep) {
putfs(nodep,"initial begin\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstAlways* nodep) {
putfs(nodep,"always ");
if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active
else nodep->sensesp()->iterateAndNext(*this);
if (m_sensesp) iterateAndNextNull(m_sensesp); // In active
else iterateAndNextNull(nodep->sensesp());
putbs(" begin\n");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
putqs(nodep,"end\n");
}
virtual void visit(AstAlwaysPublic* nodep) {
putfs(nodep,"/*verilator public_flat_rw ");
if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active
else nodep->sensesp()->iterateAndNext(*this);
if (m_sensesp) iterateAndNextNull(m_sensesp); // In active
else iterateAndNextNull(nodep->sensesp());
putqs(nodep," ");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
putqs(nodep,"*/\n");
}
virtual void visit(AstNodeAssign* nodep) {
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
putfs(nodep," "+nodep->verilogKwd()+" ");
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep) {
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
putfs(nodep," <= ");
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
puts(";\n");
}
virtual void visit(AstAssignAlias* nodep) {
putbs("alias ");
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
putfs(nodep," = ");
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignW* nodep) {
putfs(nodep,"assign ");
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
putbs(" = ");
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstBreak* nodep) {
@ -154,7 +150,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
// AstSenItem is called for dumping in isolation by V3Order
putfs(nodep,"@(");
for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) {
expp->accept(*this);
iterate(expp);
if (expp->nextp()) putqs(expp->nextp()," or ");
}
puts(")");
@ -166,40 +162,40 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
putfs(nodep,"");
puts(nodep->edgeType().verilogKwd());
if (nodep->sensp()) puts(" ");
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNodeCase* nodep) {
putfs(nodep,"");
if (AstCase* casep = nodep->castCase()) {
if (const AstCase* casep = VN_CAST(nodep, Case)) {
if (casep->priorityPragma()) puts("priority ");
if (casep->uniquePragma()) puts("unique ");
if (casep->unique0Pragma()) puts("unique0 ");
}
puts(nodep->verilogKwd());
puts(" (");
nodep->exprp()->iterateAndNext(*this);
iterateAndNextNull(nodep->exprp());
puts(")\n");
if (AstCase* casep = nodep->castCase()) {
if (const AstCase* casep = VN_CAST(nodep, Case)) {
if (casep->fullPragma() || casep->parallelPragma()) {
puts(" // synopsys");
if (casep->fullPragma()) puts(" full_case");
if (casep->parallelPragma()) puts(" parallel_case");
}
}
nodep->itemsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->itemsp());
putqs(nodep,"endcase\n");
}
virtual void visit(AstCaseItem* nodep) {
if (nodep->condsp()) {
nodep->condsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->condsp());
} else putbs("default");
putfs(nodep,": begin ");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
putqs(nodep,"end\n");
}
virtual void visit(AstComment* nodep) {
puts((string)"// "+nodep->name()+"\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstContinue* nodep) {
putbs("continue");
@ -212,11 +208,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
if (fileOrStrgp) { fileOrStrgp->iterateAndNext(*this); putbs(","); }
if (fileOrStrgp) { iterateAndNextNull(fileOrStrgp); putbs(","); }
putsQuoted(text);
for (AstNode* expp=exprsp; expp; expp = expp->nextp()) {
puts(",");
expp->iterateAndNext(*this);
iterateAndNextNull(expp);
}
puts(");\n");
}
@ -241,23 +237,23 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
virtual void visit(AstFOpen* nodep) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
if (nodep->filep()) iterateAndNextNull(nodep->filep());
putbs(",");
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
putbs(",");
if (nodep->modep()) nodep->modep()->iterateAndNext(*this);
if (nodep->modep()) iterateAndNextNull(nodep->modep());
puts(");\n");
}
virtual void visit(AstFClose* nodep) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
if (nodep->filep()) iterateAndNextNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstFFlush* nodep) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
if (nodep->filep()) iterateAndNextNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstJumpGo* nodep) {
@ -265,80 +261,80 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
virtual void visit(AstJumpLabel* nodep) {
putbs("begin : "+cvtToStr((void*)(nodep))+"\n");
if (nodep->stmtsp()) nodep->stmtsp()->iterateAndNext(*this);
if (nodep->stmtsp()) iterateAndNextNull(nodep->stmtsp());
puts("end\n");
}
virtual void visit(AstNodeReadWriteMem* nodep) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
putbs(",");
if (nodep->memp()) nodep->memp()->iterateAndNext(*this);
if (nodep->lsbp()) { putbs(","); nodep->lsbp()->iterateAndNext(*this); }
if (nodep->msbp()) { putbs(","); nodep->msbp()->iterateAndNext(*this); }
if (nodep->memp()) iterateAndNextNull(nodep->memp());
if (nodep->lsbp()) { putbs(","); iterateAndNextNull(nodep->lsbp()); }
if (nodep->msbp()) { putbs(","); iterateAndNextNull(nodep->msbp()); }
puts(");\n");
}
virtual void visit(AstSysFuncAsTask* nodep) {
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstSysIgnore* nodep) {
putfs(nodep,nodep->verilogKwd());
putbs(" (");
nodep->exprsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->exprsp());
puts(");\n");
}
virtual void visit(AstNodeFor* nodep) {
putfs(nodep,"for (");
m_suppressSemi = true;
nodep->initsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->initsp());
puts(";");
nodep->condp()->iterateAndNext(*this);
iterateAndNextNull(nodep->condp());
puts(";");
nodep->incsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->incsp());
m_suppressSemi = false;
puts(") begin\n");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
putqs(nodep,"end\n");
}
virtual void visit(AstRepeat* nodep) {
putfs(nodep,"repeat (");
nodep->countp()->iterateAndNext(*this);
iterateAndNextNull(nodep->countp());
puts(") begin\n");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
putfs(nodep,"end\n");
}
virtual void visit(AstWhile* nodep) {
nodep->precondsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->precondsp());
putfs(nodep,"while (");
nodep->condp()->iterateAndNext(*this);
iterateAndNextNull(nodep->condp());
puts(") begin\n");
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->incsp());
iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop
putfs(nodep,"end\n");
}
virtual void visit(AstNodeIf* nodep) {
putfs(nodep,"");
if (AstIf* ifp = nodep->castIf()) {
if (const AstIf* ifp = VN_CAST(nodep, If)) {
if (ifp->priorityPragma()) puts("priority ");
if (ifp->uniquePragma()) puts("unique ");
if (ifp->unique0Pragma()) puts("unique0 ");
}
puts("if (");
nodep->condp()->iterateAndNext(*this);
iterateAndNextNull(nodep->condp());
puts(") begin\n");
nodep->ifsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->ifsp());
if (nodep->elsesp()) {
putqs(nodep,"end\n");
putqs(nodep,"else begin\n");
nodep->elsesp()->iterateAndNext(*this);
iterateAndNextNull(nodep->elsesp());
}
putqs(nodep,"end\n");
}
virtual void visit(AstReturn* nodep) {
putfs(nodep,"return ");
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstStop* nodep) {
@ -354,22 +350,22 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
virtual void visit(AstCStmt* nodep) {
putfs(nodep,"$_CSTMT(");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstCMath* nodep) {
putfs(nodep,"$_CMATH(");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCStmt* nodep) {
putfs(nodep,"$c(");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCFunc* nodep) {
putfs(nodep,"$c(");
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
puts(")");
}
@ -399,22 +395,22 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
case 'k': putbs(""); break;
case 'l': {
if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else lhsp->iterateAndNext(*this);
else iterateAndNextNull(lhsp);
break;
}
case 'r': {
if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else rhsp->iterateAndNext(*this);
else iterateAndNextNull(rhsp);
break;
}
case 't': {
if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else thsp->iterateAndNext(*this);
else iterateAndNextNull(thsp);
break;
}
case 'd': {
if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else nodep->dtypep()->iterateAndNext(*this);
else iterateAndNextNull(nodep->dtypep());
break;
}
default:
@ -439,10 +435,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
virtual void visit(AstAttrOf* nodep) {
putfs(nodep,"$_ATTROF(");
nodep->fromp()->iterateAndNext(*this);
iterateAndNextNull(nodep->fromp());
if (nodep->dimp()) {
putbs(",");
nodep->dimp()->iterateAndNext(*this);
iterateAndNextNull(nodep->dimp());
}
puts(")");
}
@ -453,83 +449,83 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
int index = nodep->posIndex(pos);
puts(cvtToStr(index));
puts(":");
itemp->accept(*this);
iterate(itemp);
if (itemp->nextp()) putbs(",");
}
puts("}");
}
virtual void visit(AstNodeCond* nodep) {
putbs("(");
nodep->condp()->iterateAndNext(*this); putfs(nodep," ? ");
nodep->expr1p()->iterateAndNext(*this); putbs(" : ");
nodep->expr2p()->iterateAndNext(*this); puts(")");
iterateAndNextNull(nodep->condp()); putfs(nodep," ? ");
iterateAndNextNull(nodep->expr1p()); putbs(" : ");
iterateAndNextNull(nodep->expr2p()); puts(")");
}
virtual void visit(AstRange* nodep) {
puts("[");
if (nodep->msbp()->castConst() && nodep->lsbp()->castConst()) {
if (VN_IS(nodep->msbp(), Const) && VN_IS(nodep->lsbp(), Const)) {
// Looks nicer if we print [1:0] rather than [32'sh1:32sh0]
puts(cvtToStr(nodep->leftp()->castConst()->toSInt())); puts(":");
puts(cvtToStr(nodep->rightp()->castConst()->toSInt())); puts("]");
puts(cvtToStr(VN_CAST(nodep->leftp(), Const)->toSInt())); puts(":");
puts(cvtToStr(VN_CAST(nodep->rightp(), Const)->toSInt())); puts("]");
} else {
nodep->leftp()->iterateAndNext(*this); puts(":");
nodep->rightp()->iterateAndNext(*this); puts("]");
iterateAndNextNull(nodep->leftp()); puts(":");
iterateAndNextNull(nodep->rightp()); puts("]");
}
}
virtual void visit(AstSel* nodep) {
nodep->fromp()->iterateAndNext(*this); puts("[");
if (nodep->lsbp()->castConst()) {
iterateAndNextNull(nodep->fromp()); puts("[");
if (VN_IS(nodep->lsbp(), Const)) {
if (nodep->widthp()->isOne()) {
if (nodep->lsbp()->castConst()) {
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()));
if (VN_IS(nodep->lsbp(), Const)) {
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()));
} else {
nodep->lsbp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lsbp());
}
} else {
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()
+nodep->widthp()->castConst()->toSInt()
-1));
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()
+ VN_CAST(nodep->widthp(), Const)->toSInt()
- 1));
puts(":");
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()));
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()));
}
} else {
nodep->lsbp()->iterateAndNext(*this); putfs(nodep,"+:");
nodep->widthp()->iterateAndNext(*this); puts("]");
iterateAndNextNull(nodep->lsbp()); putfs(nodep,"+:");
iterateAndNextNull(nodep->widthp()); puts("]");
}
puts("]");
}
virtual void visit(AstSliceSel* nodep) {
nodep->fromp()->iterateAndNext(*this);
iterateAndNextNull(nodep->fromp());
puts(cvtToStr(nodep->declRange()));
}
virtual void visit(AstTypedef* nodep) {
putfs(nodep,"typedef ");
nodep->dtypep()->iterateAndNext(*this); puts(" ");
iterateAndNextNull(nodep->dtypep()); puts(" ");
puts(nodep->prettyName());
puts(";\n");
}
virtual void visit(AstBasicDType* nodep) {
if (nodep->isSigned()) putfs(nodep,"signed ");
putfs(nodep,nodep->prettyName());
if (nodep->rangep()) { puts(" "); nodep->rangep()->iterateAndNext(*this); puts(" "); }
if (nodep->rangep()) { puts(" "); iterateAndNextNull(nodep->rangep()); puts(" "); }
else if (nodep->isRanged()) { puts(" ["); puts(cvtToStr(nodep->msb())); puts(":0] "); }
}
virtual void visit(AstConstDType* nodep) {
putfs(nodep,"const ");
nodep->subDTypep()->accept(*this);
iterate(nodep->subDTypep());
}
virtual void visit(AstNodeArrayDType* nodep) {
nodep->subDTypep()->accept(*this);
nodep->rangep()->iterateAndNext(*this);
iterate(nodep->subDTypep());
iterateAndNextNull(nodep->rangep());
}
virtual void visit(AstNodeClassDType* nodep) {
puts(nodep->verilogKwd()+" ");
if (nodep->packed()) puts("packed ");
puts("\n");
nodep->membersp()->iterateAndNext(*this);
iterateAndNextNull(nodep->membersp());
puts("}");
}
virtual void visit(AstMemberDType* nodep) {
nodep->subDTypep()->accept(*this);
iterate(nodep->subDTypep());
puts(" ");
puts(nodep->name());
puts("}");
@ -538,11 +534,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
if (nodep->dotted()!="") { putfs(nodep,nodep->dotted()); puts("."); puts(nodep->prettyName()); }
else { putfs(nodep,nodep->prettyName()); }
puts("(");
nodep->pinsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->pinsp());
puts(")");
}
virtual void visit(AstArg* nodep) {
nodep->exprp()->iterateAndNext(*this);
iterateAndNextNull(nodep->exprp());
}
// Terminals
virtual void visit(AstVarRef* nodep) {
@ -564,21 +560,21 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
// Just iterate
virtual void visit(AstTopScope* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstScope* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVar* nodep) {
putfs(nodep,nodep->verilogKwd());
puts(" ");
nodep->dtypep()->iterate(*this); puts(" ");
iterate(nodep->dtypep()); puts(" ");
puts(nodep->prettyName());
puts(";\n");
}
virtual void visit(AstActive* nodep) {
m_sensesp = nodep->sensesp();
nodep->stmtsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->stmtsp());
m_sensesp = NULL;
}
virtual void visit(AstVarScope*) {}
@ -591,7 +587,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
// Default
virtual void visit(AstNode* nodep) {
puts((string)"\n???? // "+nodep->prettyTypeName()+"\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Not v3fatalSrc so we keep processing
nodep->v3error("Internal: Unknown node type reached emitter: "<<nodep->prettyTypeName());
}
@ -620,7 +616,7 @@ class EmitVFileVisitor : public EmitVBaseVisitor {
public:
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp) {
m_ofp = ofp;
nodep->accept(*this);
iterate(nodep);
}
virtual ~EmitVFileVisitor() {}
};
@ -630,7 +626,7 @@ public:
class EmitVStreamVisitor : public EmitVBaseVisitor {
// MEMBERS
ostream& m_os;
std::ostream& m_os;
// METHODS
virtual void putsNoTracking(const string& str) { m_os<<str; }
virtual void puts(const string& str) { putsNoTracking(str); }
@ -638,9 +634,9 @@ class EmitVStreamVisitor : public EmitVBaseVisitor {
virtual void putfs(AstNode*, const string& str) { putbs(str); }
virtual void putqs(AstNode*, const string& str) { putbs(str); }
public:
EmitVStreamVisitor(AstNode* nodep, ostream& os)
EmitVStreamVisitor(AstNode* nodep, std::ostream& os)
: m_os(os) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~EmitVStreamVisitor() {}
};
@ -649,7 +645,7 @@ public:
// Emit to a stream (perhaps stringstream)
class EmitVPrefixedFormatter : public V3OutFormatter {
ostream& m_os;
std::ostream& m_os;
string m_prefix; // What to print at beginning of each line
int m_flWidth; // Padding of fileline
int m_column; // Rough location; need just zero or non-zero
@ -675,7 +671,7 @@ public:
void prefixFl(FileLine* fl) { m_prefixFl = fl; }
FileLine* prefixFl() const { return m_prefixFl; }
int column() const { return m_column; }
EmitVPrefixedFormatter(ostream& os, const string& prefix, int flWidth)
EmitVPrefixedFormatter(std::ostream& os, const string& prefix, int flWidth)
: V3OutFormatter("__STREAM", V3OutFormatter::LA_VERILOG)
, m_os(os), m_prefix(prefix), m_flWidth(flWidth) {
m_column = 0;
@ -707,11 +703,11 @@ class EmitVPrefixedVisitor : public EmitVBaseVisitor {
}
public:
EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
AstSenTree* domainp, bool user3mark)
: EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth) {
if (user3mark) { AstUser3InUse::check(); }
nodep->accept(*this);
iterate(nodep);
}
virtual ~EmitVPrefixedVisitor() {}
};
@ -729,7 +725,7 @@ void V3EmitV::emitv() {
EmitVFileVisitor visitor (v3Global.rootp(), &of);
} else {
// Process each module in turn
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
V3OutVFile of (v3Global.opt.makeDir()
+"/"+EmitCBaseVisitor::modClassName(modp)+"__Vout.v");
of.putsHeader();
@ -738,11 +734,11 @@ void V3EmitV::emitv() {
}
}
void V3EmitV::verilogForTree(AstNode* nodep, ostream& os) {
void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) {
EmitVStreamVisitor(nodep, os);
}
void V3EmitV::verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
AstSenTree* domainp, bool user3mark) {
EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark);
}

View File

@ -30,8 +30,8 @@
class V3EmitV {
public:
static void emitv();
static void verilogForTree(AstNode* nodep, ostream& os=cout);
static void verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
static void verilogForTree(AstNode* nodep, std::ostream& os=std::cout);
static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
AstSenTree* domainp, bool user3percent);
};

View File

@ -46,11 +46,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
uint64_t m_id;
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// Outfile methods
V3OutFile* ofp() const { return m_ofp; }
@ -76,10 +72,10 @@ class EmitXmlFileVisitor : public AstNVisitor {
void outputTag(AstNode* nodep, string tag) {
if (tag=="") tag = VString::downcase(nodep->typeName());
puts("<"+tag+" "+nodep->fileline()->xml());
if (nodep->castNodeDType()) { puts(" id="); outputId(nodep); }
if (VN_IS(nodep, NodeDType)) { puts(" id="); outputId(nodep); }
if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); }
if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); }
if (AstNodeDType* dtp = nodep->castNodeDType()) {
if (AstNodeDType* dtp = VN_CAST(nodep, NodeDType)) {
if (dtp->subDTypep()) { puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp()); }
} else {
if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); }
@ -89,7 +85,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
if (tag=="") tag = VString::downcase(nodep->typeName());
if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) {
puts(">\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
puts("</"+tag+">\n");
} else {
puts("/>\n");
@ -108,7 +104,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
}
virtual void visit(AstNetlist* nodep) {
puts("<netlist>\n");
nodep->iterateChildren(*this);
iterateChildren(nodep);
puts("</netlist>\n");
}
virtual void visit(AstNodeModule* nodep) {
@ -154,7 +150,7 @@ public:
EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) {
m_ofp = ofp;
m_id = 0;
nodep->accept(*this);
iterate(nodep);
}
virtual ~EmitXmlFileVisitor() {}
};

View File

@ -38,7 +38,7 @@ int V3Error::s_debugDefault = 0;
int V3Error::s_errorLimit = V3Error::MAX_ERRORS;
bool V3Error::s_warnFatal = true;
int V3Error::s_tellManual = 0;
ostringstream V3Error::s_errorStr; // Error string being formed
std::ostringstream V3Error::s_errorStr; // Error string being formed
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::EC_FATAL;
bool V3Error::s_errorSuppressed = false;
bool V3Error::s_describedEachWarn[V3ErrorCode::_ENUM_MAX];
@ -81,10 +81,10 @@ void V3Error::init() {
}
string V3Error::lineStr (const char* filename, int lineno) {
ostringstream out;
std::ostringstream out;
const char* fnslashp = strrchr (filename, '/');
if (fnslashp) filename = fnslashp+1;
out<<filename<<":"<<dec<<lineno<<":";
out<<filename<<":"<<std::dec<<lineno<<":";
const char* const spaces = " ";
size_t numsp = out.str().length(); if (numsp>20) numsp = 20;
out<<(spaces + numsp);
@ -101,11 +101,11 @@ void V3Error::incErrors() {
void V3Error::abortIfWarnings() {
bool exwarn = warnFatal() && warnCount();
if (errorCount() && exwarn) {
v3fatal ("Exiting due to "<<dec<<errorCount()<<" error(s), "<<warnCount()<<" warning(s)\n");
v3fatal("Exiting due to "<<std::dec<<errorCount()<<" error(s), "<<warnCount()<<" warning(s)\n");
} else if (errorCount()) {
v3fatal ("Exiting due to "<<dec<<errorCount()<<" error(s)\n");
v3fatal("Exiting due to "<<std::dec<<errorCount()<<" error(s)\n");
} else if (exwarn) {
v3fatal ("Exiting due to "<<dec<<warnCount()<<" warning(s)\n");
v3fatal("Exiting due to "<<std::dec<<warnCount()<<" warning(s)\n");
}
}
@ -139,7 +139,7 @@ string V3Error::msgPrefix() {
void V3Error::vlAbort () {
if (V3Error::debugDefault()) {
cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
std::cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
abort();
} else {
exit(10);
@ -162,7 +162,7 @@ string V3Error::warnMore() {
return msgPrefix();
}
void V3Error::v3errorEnd (ostringstream& sstr) {
void V3Error::v3errorEnd(std::ostringstream& sstr) {
#if defined(__COVERITY__) || defined(__cppcheck__)
if (s_errorCode==V3ErrorCode::EC_FATAL) __coverity_panic__(x);
#endif
@ -176,20 +176,20 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
if (s_messages.find(msg) != s_messages.end()) return;
s_messages.insert(msg);
// Output
cerr<<msg;
std::cerr<<msg;
if (!s_errorSuppressed && !(s_errorCode==V3ErrorCode::EC_INFO
|| s_errorCode==V3ErrorCode::USERINFO)) {
if (!s_describedEachWarn[s_errorCode]
&& !s_pretendError[s_errorCode]) {
s_describedEachWarn[s_errorCode] = true;
if (s_errorCode>=V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) {
cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
std::cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
<<" */\" and lint_on around source to disable this message."<<endl;
s_describedWarnings = true;
}
if (s_errorCode.dangerous()) {
cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
std::cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
std::cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
}
}
// If first warning is not the user's fault (internal/unsupported) then give the website
@ -210,7 +210,7 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
if (!inFatal) {
inFatal = true;
if (s_tellManual==1) {
cerr<<msgPrefix()<<"See the manual and http://www.veripool.org/verilator for more assistance."<<endl;
std::cerr<<msgPrefix()<<"See the manual and http://www.veripool.org/verilator for more assistance."<<endl;
s_tellManual = 2;
}
#ifndef _V3ERROR_NO_GLOBAL_

View File

@ -97,6 +97,7 @@ public:
UNDRIVEN, // No drivers
UNOPT, // Unoptimizable block
UNOPTFLAT, // Unoptimizable block after flattening
UNOPTTHREADS, // Thread partitioner unable to fill all requested threads
UNPACKED, // Unsupported unpacked
UNSIGNED, // Comparison is constant due to unsigned arithmetic
UNUSED, // No receivers
@ -139,7 +140,8 @@ public:
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY",
"REALCVT", "REDEFMACRO",
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNPACKED", "UNSIGNED", "UNUSED",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSED",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
"VARHIDDEN", "WIDTH", "WIDTHCONCAT",
" MAX"
@ -189,14 +191,14 @@ public:
inline bool operator== (V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (V3ErrorCode::en lhs, V3ErrorCode rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, V3ErrorCode rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, V3ErrorCode rhs) { return os<<rhs.ascii(); }
//######################################################################
class V3Error {
// Base class for any object that wants debugging and error reporting
typedef set<string> MessagesSet;
typedef std::set<string> MessagesSet;
typedef void (*ErrorExitCb)(void);
private:
@ -209,7 +211,7 @@ class V3Error {
static int s_errCount; // Error count
static int s_warnCount; // Warning count
static int s_tellManual; // Tell user to see manual, 0=not yet, 1=doit, 2=disable
static ostringstream s_errorStr; // Error string being formed
static std::ostringstream s_errorStr; // Error string being formed
static V3ErrorCode s_errorCode; // Error string being formed will abort
static bool s_errorSuppressed; // Error being formed should be suppressed
static MessagesSet s_messages; // What errors we've outputted
@ -217,7 +219,7 @@ class V3Error {
enum MaxErrors { MAX_ERRORS = 50 }; // Fatal after this may errors
V3Error() { cerr<<("Static class"); abort(); }
V3Error() { std::cerr<<("Static class"); abort(); }
public:
// CONSTRUCTORS
@ -252,15 +254,15 @@ class V3Error {
// Error end takes the string stream to output, be careful to seek() as needed
static void v3errorPrep(V3ErrorCode code) {
s_errorStr.str(""); s_errorCode=code; s_errorSuppressed=false; }
static ostringstream& v3errorStr() { return s_errorStr; }
static std::ostringstream& v3errorStr() { return s_errorStr; }
static void vlAbort();
static void v3errorEnd(ostringstream& sstr); // static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr); // static, but often overridden in classes.
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
inline int debug() { return V3Error::debugDefault(); }
inline void v3errorEnd(ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); assert(0); VL_UNREACHABLE }
inline void v3errorEnd(std::ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
inline void v3errorEndFatal(std::ostringstream& sstr) { V3Error::v3errorEnd(sstr); assert(0); VL_UNREACHABLE }
// Theses allow errors using << operators: v3error("foo"<<"bar");
// Careful, you can't put () around msg, as you would in most macro definitions
@ -276,7 +278,11 @@ inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); as
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
#define v3fatal(msg) v3warnCodeFatal(V3ErrorCode::EC_FATAL, msg)
// Use this instead of fatal() to mention the source code line.
#define v3fatalSrc(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, __FILE__<<":"<<dec<<__LINE__<<": "<<msg)
#define v3fatalSrc(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, __FILE__<<":"<<std::dec<<__LINE__<<": "<<msg)
// Use this when normal v3fatal is called in static method that overrides fileline.
#define v3fatalStatic(msg) \
::v3errorEndFatal((V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), \
(V3Error::v3errorStr()<<msg), V3Error::v3errorStr()));
#define UINFO(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<"- "<<V3Error::lineStr(__FILE__,__LINE__)<<stmsg; }}
#define UINFONL(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<stmsg; } }
@ -289,15 +295,31 @@ inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); as
#define UASSERT(condition,stmsg) { if (VL_UNLIKELY(!(condition))) { v3fatalSrc(stmsg); }}
// For use in V3Ast static functions only
#define UASSERT_STATIC(condition,stmsg) { if (VL_UNLIKELY(!(condition))) { \
cerr<<"Internal Error: "<<__FILE__<<":"<<dec<<__LINE__<<":"<<(stmsg)<<endl; abort(); } }
#define UASSERT_STATIC(condition,stmsg) \
{ if (VL_UNLIKELY(!(condition))) { \
std::cerr<<"Internal Error: "<<__FILE__<<":"<<std::dec<<__LINE__<<":"<<(stmsg)<<std::endl; abort(); } }
// Check self test values for expected value. Safe from side-effects.
// Type argument can be removed when go to C++11 (use auto).
#define UASSERT_SELFTEST(Type,got,exp) \
do { Type g = (got); Type e = (exp); \
UASSERT(g==e, "Self-test failed '" #got "==" #exp "'"" got=" \
<<g<<" expected="<<e); } while(0)
#define V3ERROR_NA { v3error("Internal: Unexpected Call"); v3fatalSrc("Unexpected Call"); }
/// Declare a convenience debug() routine that may be added to any class in
/// Verilator so that --debugi-<srcfile> will work to control UINFOs in
/// that class:
#define VL_DEBUG_FUNC static int debug() { \
static int level = -1; \
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \
return level; \
}
//----------------------------------------------------------------------
template< class T> std::string cvtToStr (const T& t) {
ostringstream os; os<<t; return os.str();
std::ostringstream os; os<<t; return os.str();
}
inline uint32_t cvtToHash(const void* vp) {

View File

@ -53,11 +53,7 @@ private:
AstNode* m_stmtp; // Current statement
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
int longOrQuadWidth (AstNode* nodep) {
// Return 32 or 64...
@ -107,7 +103,7 @@ private:
void fixCloneLvalue (AstNode* nodep) {
// In AstSel transforms, we call clone() on VarRefs that were lvalues,
// but are now being used on the RHS of the assignment
if (nodep->castVarRef()) nodep->castVarRef()->lvalue(false);
if (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->lvalue(false);
// Iterate
if (nodep->op1p()) fixCloneLvalue(nodep->op1p());
if (nodep->op2p()) fixCloneLvalue(nodep->op2p());
@ -178,9 +174,9 @@ private:
AstNode* newSelBitWord(AstNode* lsbp, int wordAdder) {
// Return equation to get the VL_BITWORD of a constant or non-constant
if (lsbp->castConst()) {
if (VN_IS(lsbp, Const)) {
return new AstConst (lsbp->fileline(),
wordAdder + VL_BITWORD_I(lsbp->castConst()->toUInt()));
wordAdder + VL_BITWORD_I(VN_CAST(lsbp, Const)->toUInt()));
} else {
AstNode* shiftp = new AstShiftR (lsbp->fileline(),
lsbp->cloneTree(true),
@ -201,17 +197,17 @@ private:
// If there's a CONDBOUND safety to keep arrays in bounds,
// we're going to AND it to a value that always fits inside a
// word, so we don't need it.
//if (nodep->castCondBound() && nodep->castCondBound()->lhsp()->castLte()) {
// nodep = nodep->castCondBound()->rhsp();
//if (VN_IS(nodep, CondBound) && VN_IS(VN_CAST(nodep, CondBound)->lhsp(), Lte)) {
// nodep = VN_CAST(nodep, CondBound)->rhsp();
//}
return nodep;
}
AstNode* newSelBitBit(AstNode* lsbp) {
// Return equation to get the VL_BITBIT of a constant or non-constant
if (lsbp->castConst()) {
if (VN_IS(lsbp, Const)) {
return new AstConst (lsbp->fileline(),
VL_BITBIT_I(lsbp->castConst()->toUInt()));
VL_BITBIT_I(VN_CAST(lsbp, Const)->toUInt()));
} else {
return new AstAnd (lsbp->fileline(),
new AstConst(lsbp->fileline(), VL_WORDSIZE-1),
@ -243,7 +239,7 @@ private:
}
bool expandWide (AstNodeAssign* nodep, AstArraySel* rhsp) {
UINFO(8," Wordize ASSIGN(ARRAYSEL) "<<nodep<<endl);
if (nodep->dtypep()->skipRefp()->castUnpackArrayDType()) {
if (VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
}
for (int w=0; w<nodep->widthWords(); w++) {
@ -312,7 +308,7 @@ private:
// VISITORS
virtual void visit(AstExtend* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isWide()) {
// See under ASSIGN(EXTEND)
} else {
@ -351,10 +347,10 @@ private:
virtual void visit(AstSel* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
if (nodep->widthMin()!=(int)nodep->widthConst()) nodep->v3fatalSrc("Width mismatch");
if (nodep->backp()->castNodeAssign() && nodep==nodep->backp()->castNodeAssign()->lhsp()) {
if (VN_IS(nodep->backp(), NodeAssign) && nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) {
// Sel is an LHS assignment select
} else if (nodep->isWide()) {
// See under ASSIGN(WIDE)
@ -462,7 +458,7 @@ private:
bool expandWide (AstNodeAssign* nodep, AstSel* rhsp) {
if (nodep->widthMin()!=(int)rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch");
if (rhsp->lsbp()->castConst() && VL_BITBIT_I(rhsp->lsbConst())==0) {
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_I(rhsp->lsbConst())==0) {
int lsb = rhsp->lsbConst();
UINFO(8," Wordize ASSIGN(SEL,align) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
@ -515,7 +511,7 @@ private:
// Yuk.
bool destwide = lhsp->fromp()->isWide();
bool ones = nodep->rhsp()->isAllOnesV();
if (lhsp->lsbp()->castConst()) {
if (VN_IS(lhsp->lsbp(), Const)) {
// The code should work without this constant test, but it won't
// constify as nicely as we'd like.
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
@ -652,7 +648,7 @@ private:
virtual void visit(AstConcat* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
@ -692,7 +688,7 @@ private:
virtual void visit(AstReplicate* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
@ -704,7 +700,7 @@ private:
newp = new AstNegate (nodep->fileline(), lhsp);
} else {
UINFO(8," REPLICATE "<<nodep<<endl);
AstConst* constp = nodep->rhsp()->castConst();
const AstConst* constp = VN_CAST(nodep->rhsp(), Const);
if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->toUInt();
if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
@ -729,7 +725,7 @@ private:
UINFO(8," Wordize ASSIGN(REPLICATE) "<<nodep<<endl);
AstNode* lhsp = rhsp->lhsp();
int lhswidth = lhsp->widthMin();
AstConst* constp = rhsp->rhsp()->castConst();
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->toUInt();
for (int w=0; w<rhsp->widthWords(); w++) {
@ -753,7 +749,7 @@ private:
virtual void visit(AstChangeXor* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
UINFO(8," Wordize ChangeXor "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
AstNode* newp = NULL;
@ -768,7 +764,7 @@ private:
void visitEqNeq(AstNodeBiop* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize EQ/NEQ "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
@ -779,7 +775,7 @@ private:
newAstWordSelClone (nodep->rhsp(), w));
newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp));
}
if (nodep->castNeq()) {
if (VN_IS(nodep, Neq)) {
newp = new AstNeq (nodep->fileline(),
new AstConst (nodep->fileline(), 0), newp);
} else {
@ -794,7 +790,7 @@ private:
virtual void visit(AstRedOr* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDOR "<<nodep<<endl);
// -> (0!={or{for each_word{WORDSEL(lhs,#)}}}
@ -818,7 +814,7 @@ private:
}
virtual void visit(AstRedAnd* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDAND "<<nodep<<endl);
// -> (0!={and{for each_word{WORDSEL(lhs,#)}}}
@ -849,7 +845,7 @@ private:
}
virtual void visit(AstRedXor* nodep) {
if (nodep->user1SetOnce()) return; // Process once
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDXOR "<<nodep<<endl);
// -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}}
@ -869,44 +865,44 @@ private:
virtual void visit(AstNodeStmt* nodep) {
if (nodep->user1SetOnce()) return; // Process once
m_stmtp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_stmtp = NULL;
}
virtual void visit(AstNodeAssign* nodep) {
if (nodep->user1SetOnce()) return; // Process once
m_stmtp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
bool did = false;
if (nodep->isWide() && ((nodep->lhsp()->castVarRef()
|| nodep->lhsp()->castArraySel()))
if (nodep->isWide() && ((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 = nodep->rhsp()->castConst()) {
if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) {
did = expandWide(nodep,rhsp);
} else if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) {
} else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
did = expandWide(nodep,rhsp);
} else if (AstSel* rhsp = nodep->rhsp()->castSel()) {
} else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) {
did = expandWide(nodep,rhsp);
} else if (AstArraySel* rhsp = nodep->rhsp()->castArraySel()) {
} else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) {
did = expandWide(nodep,rhsp);
} else if (AstConcat* rhsp = nodep->rhsp()->castConcat()) {
} else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) {
did = expandWide(nodep,rhsp);
} else if (AstReplicate* rhsp = nodep->rhsp()->castReplicate()) {
} else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) {
did = expandWide(nodep,rhsp);
} else if (AstAnd* rhsp = nodep->rhsp()->castAnd()) {
} else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) {
did = expandWide(nodep,rhsp);
} else if (AstOr* rhsp = nodep->rhsp()->castOr()) {
} else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) {
did = expandWide(nodep,rhsp);
} else if (AstNot* rhsp = nodep->rhsp()->castNot()) {
} else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) {
did = expandWide(nodep,rhsp);
} else if (AstXor* rhsp = nodep->rhsp()->castXor()) {
} else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) {
did = expandWide(nodep,rhsp);
} else if (AstXnor* rhsp = nodep->rhsp()->castXnor()) {
} else if (AstXnor* rhsp = VN_CAST(nodep->rhsp(), Xnor)) {
did = expandWide(nodep,rhsp);
} else if (AstNodeCond* rhsp = nodep->rhsp()->castNodeCond()) {
} else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) {
did = expandWide(nodep,rhsp);
}
} else if (AstSel* lhsp = nodep->lhsp()->castSel()) {
} else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) {
did = expandLhs(nodep,lhsp);
}
// Cleanup common code
@ -920,14 +916,14 @@ private:
// Default: Just iterate
virtual void visit(AstVar*) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit ExpandVisitor(AstNetlist* nodep) {
m_stmtp=NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~ExpandVisitor() {}
};

View File

@ -97,8 +97,8 @@ class V3FileDependImp {
};
// MEMBERS
set<string> m_filenameSet; // Files generated (elim duplicates)
set<DependFile> m_filenameList; // Files sourced/generated
std::set<string> m_filenameSet; // Files generated (elim duplicates)
std::set<DependFile> m_filenameList; // Files sourced/generated
static string stripQuotes(const string& in) {
string pretty = in;
@ -137,7 +137,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
const vl_unique_ptr<std::ofstream> ofp (V3File::new_ofstream(filename));
if (ofp->fail()) v3fatal("Can't write "<<filename);
for (set<DependFile>::iterator iter=m_filenameList.begin();
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
if (iter->target()) {
*ofp<<iter->filename()<<" ";
@ -149,7 +149,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
*ofp<<V3PreShell::dependFiles();
*ofp<<" ";
for (set<DependFile>::iterator iter=m_filenameList.begin();
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
if (!iter->target()) {
*ofp<<iter->filename()<<" ";
@ -160,7 +160,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
if (v3Global.opt.makePhony()) {
*ofp<<endl;
for (set<DependFile>::iterator iter=m_filenameList.begin();
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
if (!iter->target()) {
*ofp<<iter->filename()<<":"<<endl;
@ -177,7 +177,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
*ofp<<"# DESCR"<<"IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will."<<endl;
*ofp<<"C \""<<cmdline<<"\""<<endl;
for (set<DependFile>::iterator iter=m_filenameList.begin();
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
// Read stats of files we create after we're done making them (execpt for this file, of course)
DependFile* dfp = (DependFile*)&(*iter);
@ -188,19 +188,19 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
if (dfp->filename() == filename) { showSize=0; showIno=0; } // We're writing it, so need to ignore it
*ofp<<(iter->target()?"T":"S")<<" ";
*ofp<<" "<<setw(8)<<showSize;
*ofp<<" "<<setw(8)<<showIno;
*ofp<<" "<<setw(11)<<iter->cstime();
*ofp<<" "<<setw(11)<<iter->cnstime();
*ofp<<" "<<setw(11)<<iter->mstime();
*ofp<<" "<<setw(11)<<iter->mnstime();
*ofp<<" "<<std::setw(8)<<showSize;
*ofp<<" "<<std::setw(8)<<showIno;
*ofp<<" "<<std::setw(11)<<iter->cstime();
*ofp<<" "<<std::setw(11)<<iter->cnstime();
*ofp<<" "<<std::setw(11)<<iter->mstime();
*ofp<<" "<<std::setw(11)<<iter->mnstime();
*ofp<<" \""<<iter->filename()<<"\"";
*ofp<<endl;
}
}
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
const vl_unique_ptr<std::ifstream> ifp (V3File::new_ifstream_nodepend(filename));
if (ifp->fail()) {
UINFO(2," --check-times failed: no input "<<filename<<endl);
return false;
@ -299,7 +299,7 @@ void V3File::createMakeDir() {
// V3InFilterImp
class V3InFilterImp {
typedef map<string,string> FileContentsMap;
typedef std::map<string,string> FileContentsMap;
typedef V3InFilter::StrList StrList;
FileContentsMap m_contentsMap; // Cache of file contents
@ -316,11 +316,7 @@ class V3InFilterImp {
private:
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
bool readContents(const string& filename, StrList& outl) {
if (m_pid) return readContentsFilter(filename,outl);

View File

@ -34,23 +34,23 @@
class V3File {
public:
static ifstream* new_ifstream(const string& filename) {
static std::ifstream* new_ifstream(const string& filename) {
addSrcDepend(filename);
return new_ifstream_nodepend (filename);
}
static ifstream* new_ifstream_nodepend(const string& filename) {
return new ifstream(filename.c_str());
static std::ifstream* new_ifstream_nodepend(const string& filename) {
return new std::ifstream(filename.c_str());
}
static ofstream* new_ofstream(const string& filename, bool append=false) {
static std::ofstream* new_ofstream(const string& filename, bool append=false) {
addTgtDepend(filename);
return new_ofstream_nodepend (filename, append);
}
static ofstream* new_ofstream_nodepend(const string& filename, bool append=false) {
static std::ofstream* new_ofstream_nodepend(const string& filename, bool append=false) {
if (filename != VL_DEV_NULL) createMakeDir();
if (append) {
return new ofstream(filename.c_str(), ios::app);
return new std::ofstream(filename.c_str(), std::ios::app);
} else {
return new ofstream(filename.c_str());
return new std::ofstream(filename.c_str());
}
}
static FILE* new_fopen_w(const string& filename) {
@ -78,7 +78,7 @@ class V3InFilterImp;
class V3InFilter {
public:
// TYPES
typedef list<string> StrList;
typedef std::list<string> StrList;
private:
V3InFilterImp* m_impp;
@ -123,7 +123,7 @@ private:
int m_nobreak; // Basic operator or begin paren, don't break next
bool m_prependIndent;
int m_indentLevel; // Current {} indentation
stack<int> m_parenVec; // Stack of columns where last ( was
std::stack<int> m_parenVec; // Stack of columns where last ( was
int endLevels(const char* strg);
const char* indentStr(int levels);

View File

@ -71,7 +71,7 @@ int FileLineSingleton::nameToNumber(const string& filename) {
//! Support XML output
//! Experimental. Updated to also put out the language.
void FileLineSingleton::fileNameNumMapDumpXml(ostream& os) {
void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
os<<"<files>\n";
for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) {
os<<"<file id=\""<<filenameLetters(it->second)
@ -184,8 +184,8 @@ const string FileLine::profileFuncname() const {
string FileLine::ascii() const {
return filename()+":"+cvtToStr(lineno());
}
ostream& operator<<(ostream& os, FileLine* fileline) {
os <<fileline->ascii()<<": "<<hex;
std::ostream& operator<<(std::ostream& os, FileLine* fileline) {
os <<fileline->ascii()<<": "<<std::hex;
return(os);
}
@ -237,9 +237,9 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
}
}
void FileLine::v3errorEnd(ostringstream& str) {
void FileLine::v3errorEnd(std::ostringstream& str) {
if (m_lineno) {
ostringstream nsstr;
std::ostringstream nsstr;
nsstr<<this<<str.str();
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
V3Error::v3errorEnd(nsstr);

View File

@ -43,12 +43,12 @@ class FileLine;
//! source file (each with its own unique filename number).
class FileLineSingleton {
// TYPES
typedef map<string,int> FileNameNumMap;
typedef map<string,V3LangCode> FileLangNumMap;
typedef std::map<string,int> FileNameNumMap;
typedef std::map<string,V3LangCode> FileLangNumMap;
// MEMBERS
FileNameNumMap m_namemap; // filenameno for each filename
deque<string> m_names; // filename text for each filenameno
deque<V3LangCode> m_languages; // language for each filenameno
std::deque<string> m_names; // filename text for each filenameno
std::deque<V3LangCode> m_languages; // language for each filenameno
// COSNTRUCTORS
FileLineSingleton() { }
~FileLineSingleton() { }
@ -60,7 +60,7 @@ protected:
const V3LangCode numberToLang(int filenameno) const { return m_languages[filenameno]; }
void numberToLang(int filenameno, const V3LangCode& l) { m_languages[filenameno] = l; }
void clear() { m_namemap.clear(); m_names.clear(); m_languages.clear(); }
void fileNameNumMapDumpXml(ostream& os);
void fileNameNumMapDumpXml(std::ostream& os);
static const string filenameLetters(int fileno);
};
@ -72,7 +72,7 @@ protected:
class FileLine {
int m_lineno;
int m_filenameno;
bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
std::bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
private:
struct EmptySecret {};
@ -148,7 +148,7 @@ public:
defaultFileLine().warnOff(code, flag); }
static bool globalWarnOff(const string& code, bool flag) {
return defaultFileLine().warnOff(code, flag); }
static void fileNameNumMapDumpXml(ostream& os) {
static void fileNameNumMapDumpXml(std::ostream& os) {
singleton().fileNameNumMapDumpXml(os); }
// METHODS - Called from netlist
@ -160,17 +160,17 @@ public:
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code,flag); }
// OPERATORS
void v3errorEnd(ostringstream& str);
void v3errorEndFatal(ostringstream& str);
void v3errorEnd(std::ostringstream& str);
void v3errorEndFatal(std::ostringstream& str);
string warnMore() const;
inline bool operator==(FileLine rhs) const {
return (m_lineno==rhs.m_lineno && m_filenameno==rhs.m_filenameno && m_warnOn==rhs.m_warnOn);
}
private:
void v3errorEndFatalGuts(ostringstream& str);
void v3errorEndFatalGuts(std::ostringstream& str);
};
ostream& operator<<(ostream& os, FileLine* fileline);
std::ostream& operator<<(std::ostream& os, FileLine* fileline);
inline void FileLine::v3errorEndFatal(ostringstream& str) { v3errorEnd(str); assert(0); }
inline void FileLine::v3errorEndFatal(std::ostringstream& str) { v3errorEnd(str); assert(0); }
#endif // Guard

View File

@ -45,7 +45,7 @@
#include "V3Stats.h"
#include "V3Hashed.h"
typedef list<AstNodeVarRef*> GateVarRefList;
typedef std::list<AstNodeVarRef*> GateVarRefList;
#define GATE_DEDUP_MAX_DEPTH 20
@ -53,11 +53,7 @@ typedef list<AstNodeVarRef*> GateVarRefList;
class GateBaseVisitor : public AstNVisitor {
public:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
@ -176,6 +172,7 @@ public:
// ACCESSORS
virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->prettyName()); }
virtual string dotColor() const { return "yellow"; }
virtual FileLine* fileline() const { return nodep()->fileline(); }
AstNode* nodep() const { return m_nodep; }
AstActive* activep() const { return m_activep; }
bool slow() const { return m_slow; }
@ -207,7 +204,7 @@ private:
// VISITORS
virtual void visit(AstNodeVarRef* nodep) {
++m_ops;
nodep->iterateChildren(*this);
iterateChildren(nodep);
// We only allow a LHS ref for the var being set, and a RHS ref for something else being read.
if (nodep->varScopep()->varp()->isSc()) {
clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's
@ -234,19 +231,19 @@ private:
}
virtual void visit(AstNodeAssign* nodep) {
m_substTreep = nodep->rhsp();
if (!nodep->lhsp()->castNodeVarRef())
if (!VN_IS(nodep->lhsp(), NodeVarRef))
clearSimple("ASSIGN(non-VARREF)");
else nodep->iterateChildren(*this);
else iterateChildren(nodep);
// We don't push logic other then assignments/NOTs into SenItems
// This avoids a mess in computing what exactly a POSEDGE is
// V3Const cleans up any NOTs by flipping the edges for us
if (m_buffersOnly
&& !(nodep->rhsp()->castVarRef()
&& !(VN_IS(nodep->rhsp(), VarRef)
// Avoid making non-clocked logic into clocked,
// as it slows down the verilator_sim_benchmark
|| (nodep->rhsp()->castNot()
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()->varp()->isUsedClock())
|| (VN_IS(nodep->rhsp(), Not)
&& VN_IS(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef)
&& VN_CAST(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef)->varp()->isUsedClock())
)) {
clearSimple("Not a buffer (goes to a clock)");
}
@ -265,7 +262,7 @@ private:
UINFO(5, "Non optimizable type: "<<nodep<<endl);
clearSimple("Non optimizable type");
}
else nodep->iterateChildren(*this);
else iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -277,7 +274,7 @@ public:
m_dedupe = dedupe;
m_ops = 0;
// Iterate
nodep->accept(*this);
iterate(nodep);
// Check results
if (!m_substTreep) {
clearSimple("No assignment found\n");
@ -342,8 +339,8 @@ private:
m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable
}
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
if (nodep->castSenItem()) m_logicVertexp->setConsumed("senItem");
nodep->iterateChildren(*this);
if (VN_IS(nodep, SenItem)) m_logicVertexp->setConsumed("senItem");
iterateChildren(nodep);
m_logicVertexp = NULL;
}
}
@ -384,7 +381,7 @@ private:
// VISITORS
virtual void visit(AstNetlist* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
//if (debug()>6) m_graph.dump();
if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre");
warnSignals(); // Before loss of sync/async pointers
@ -411,14 +408,14 @@ private:
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_activeReducible = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstScope* nodep) {
UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
m_logicVertexp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_scopep = NULL;
}
virtual void visit(AstActive* nodep) {
@ -427,7 +424,7 @@ private:
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
m_activep = nodep;
AstNode::user2ClearTree();
nodep->iterateChildren(*this);
iterateChildren(nodep);
AstNode::user2ClearTree();
m_activep = NULL;
m_activeReducible = true;
@ -475,7 +472,7 @@ private:
// The gating term of a AstSenGate is normal logic
m_inSenItem = true;
if (m_logicVertexp) { // Already under logic; presumably a SenGate
nodep->iterateChildren(*this);
iterateChildren(nodep);
} else { // Standalone item, probably right under a SenTree
iterateNewStmt(nodep, NULL, NULL);
}
@ -508,16 +505,16 @@ private:
m_inSlow = lastslow;
}
virtual void visit(AstConcat* nodep) {
if (nodep->backp()->castNodeAssign() && nodep->backp()->castNodeAssign()->lhsp()==nodep) {
if (VN_IS(nodep->backp(), NodeAssign) && VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep) {
nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it");
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//--------------------
// Default
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter");
}
@ -532,7 +529,7 @@ public:
m_activeReducible = true;
m_inSenItem = false;
m_inSlow = false;
nodep->accept(*this);
iterate(nodep);
}
virtual ~GateVisitor() {
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
@ -702,33 +699,33 @@ void GateVisitor::replaceAssigns() {
// Take the Comments/assigns that were moved to the VarScope and change them to a
// simple value assignment
AstVarScope* vscp = vvertexp->varScp();
if (vscp->valuep() && !vscp->valuep()->castNodeMath()) {
if (vscp->valuep() && !VN_IS(vscp->valuep(), NodeMath)) {
//if (debug()>9) vscp->dumpTree(cout, "-vscPre: ");
while (AstNode* delp=vscp->valuep()->castComment()) {
while (AstNode* delp=VN_CAST(vscp->valuep(), Comment)) {
delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp);
}
if (AstInitial* delp=vscp->valuep()->castInitial()) {
if (AstInitial* delp=VN_CAST(vscp->valuep(), Initial)) {
AstNode* bodyp=delp->bodysp();
bodyp->unlinkFrBackWithNext();
delp->replaceWith(bodyp);
delp->deleteTree(); VL_DANGLING(delp);
}
if (AstAlways* delp=vscp->valuep()->castAlways()) {
if (AstAlways* delp=VN_CAST(vscp->valuep(), Always)) {
AstNode* bodyp=delp->bodysp();
bodyp->unlinkFrBackWithNext();
delp->replaceWith(bodyp);
delp->deleteTree(); VL_DANGLING(delp);
}
if (AstNodeAssign* delp=vscp->valuep()->castNodeAssign()) {
if (AstNodeAssign* delp=VN_CAST(vscp->valuep(), NodeAssign)) {
AstNode* rhsp=delp->rhsp();
rhsp->unlinkFrBack();
delp->replaceWith(rhsp);
delp->deleteTree(); VL_DANGLING(delp);
}
//if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<<endl;}
if (!vscp->valuep()->castNodeMath()
if (!VN_IS(vscp->valuep(), NodeMath)
|| vscp->valuep()->nextp()) {
vscp->dumpTree(cerr, "vscStrange: ");
vscp->dumpTree(std::cerr, "vscStrange: ");
vscp->v3fatalSrc("Value of varscope not mathematical");
}
}
@ -829,8 +826,8 @@ private:
m_didReplace = true;
if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
AstNode* substp = m_replaceTreep->cloneTree(false);
if (nodep->castNodeVarRef()
&& substp->castNodeVarRef()
if (VN_IS(nodep, NodeVarRef)
&& VN_IS(substp, NodeVarRef)
&& nodep->same(substp)) {
// Prevent a infinite loop...
substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
@ -840,15 +837,15 @@ private:
// IE what we're replacing with.
// However a VARREF should point to the original as it's otherwise confusing
// to throw warnings that point to a PIN rather than where the pin us used.
if (substp->castVarRef()) substp->fileline(nodep->fileline());
if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline());
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
if (AstNodeVarRef* varrefp = substp->castNodeVarRef()) varrefp->lvalue(false);
if (AstNodeVarRef* varrefp = VN_CAST(substp, NodeVarRef)) varrefp->lvalue(false);
nodep->replaceWith(substp);
nodep->deleteTree(); VL_DANGLING(nodep);
}
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -857,7 +854,7 @@ public:
m_didReplace = false;
m_elimVarScp = varscp;
m_replaceTreep = replaceTreep;
nodep->accept(*this);
iterate(nodep);
}
bool didReplace() const { return m_didReplace; }
};
@ -929,7 +926,7 @@ public:
// So dupit is either a different, duplicate rhsp, or the end of the hash.
if (dupit != m_hashed.end()) {
m_hashed.erase(inserted);
return m_hashed.iteratorNodep(dupit)->user2p()->castNodeAssign();
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
}
return NULL;
}
@ -971,7 +968,7 @@ private:
if (m_dedupable) {
if (!m_always) {
m_always = true;
alwaysp->bodysp()->iterateAndNext(*this);
iterateAndNextNull(alwaysp->bodysp());
} else {
m_dedupable = false;
}
@ -985,7 +982,7 @@ private:
if (m_dedupable) {
if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else
m_ifCondp = ifp->condp();
ifp->ifsp()->iterateAndNext(*this);
iterateAndNextNull(ifp->ifsp());
} else {
m_dedupable = false;
}
@ -1013,11 +1010,11 @@ public:
m_ifCondp = NULL;
m_always = false;
m_dedupable = true;
nodep->accept(*this);
iterate(nodep);
if (m_dedupable && m_assignp) {
AstNode* lhsp = m_assignp->lhsp();
// Possible todo, handle more complex lhs expressions
if (AstNodeVarRef* lhsVarRefp = lhsp->castNodeVarRef()) {
if (AstNodeVarRef* lhsVarRefp = VN_CAST(lhsp, NodeVarRef)) {
if (lhsVarRefp->varScopep() != consumerVarScopep) consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign");
if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp,activep,m_ifCondp)) {
return (AstNodeVarRef*) dup->lhsp();
@ -1157,13 +1154,13 @@ private:
// assemble two Sel into one if possible
AstSel* merge(AstSel* pre, AstSel* cur) {
AstVarRef* preVarRefp = pre->fromp()->castVarRef();
AstVarRef* curVarRefp = cur->fromp()->castVarRef();
AstVarRef* preVarRefp = VN_CAST(pre->fromp(), VarRef);
AstVarRef* curVarRefp = VN_CAST(cur->fromp(), VarRef);
if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) return NULL; // not the same var
AstConst* pstart = pre->lsbp()->castConst();
AstConst* pwidth = pre->widthp()->castConst();
AstConst* cstart = cur->lsbp()->castConst();
AstConst* cwidth = cur->widthp()->castConst();
const AstConst* pstart = VN_CAST(pre->lsbp(), Const);
const AstConst* pwidth = VN_CAST(pre->widthp(), Const);
const AstConst* cstart = VN_CAST(cur->lsbp(), Const);
const AstConst* cwidth = VN_CAST(cur->widthp(), Const);
if (!pstart || !pwidth || !cstart || !cwidth) return NULL; // too complicated
if (cur->lsbConst()+cur->widthConst() == pre->lsbConst())
return new AstSel(curVarRefp->fileline(), curVarRefp->cloneTree(false), cur->lsbConst(), pre->widthConst()+cur->widthConst());
@ -1175,10 +1172,10 @@ private:
V3GraphEdge* oldedgep = edgep;
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
if (AstNodeAssign* assignp = lvertexp->nodep()->castNodeAssign()) {
//if (lvertexp->outSize1() && assignp->lhsp()->castSel()) {
if (assignp->lhsp()->castSel() && lvertexp->outSize1()) {
UINFO(9, "assing to the nodep["<<assignp->lhsp()->castSel()->lsbConst()<<"]"<<endl);
if (AstNodeAssign* assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
//if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
UINFO(9, "assing to the nodep["<<VN_CAST(assignp->lhsp(), Sel)->lsbConst()<<"]"<<endl);
// first assign with Sel-lhs
if (!m_activep) m_activep = lvertexp->activep();
if (!m_logicvp) m_logicvp = lvertexp;
@ -1192,8 +1189,8 @@ private:
continue;
}
AstSel* preselp = m_assignp->lhsp()->castSel();
AstSel* curselp = assignp->lhsp()->castSel();
AstSel* preselp = VN_CAST(m_assignp->lhsp(), Sel);
AstSel* curselp = VN_CAST(assignp->lhsp(), Sel);
if (!preselp || !curselp) continue;
if (AstSel* newselp = merge(preselp, curselp)) {
@ -1292,13 +1289,13 @@ private:
}
virtual void visit(AstConcat* nodep) {
UINFO(9,"CLK DECOMP Concat search (off = "<<m_offset<<") - "<<nodep<<endl);
nodep->rhsp()->iterate(*this);
nodep->lhsp()->iterate(*this);
iterate(nodep->rhsp());
iterate(nodep->lhsp());
}
//--------------------
// Default
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -1315,7 +1312,7 @@ public:
m_offset = 0;
m_found = false;
// Iterate
concatp->accept(*this);
iterate(concatp);
UINFO(9,"CLK DECOMP Concat Offset (found = "<<m_found<<") ("<<m_found_offset<<") - "<<concatp<<" : "<<vscp<<endl);
offsetr = m_found_offset;
return m_found;
@ -1370,10 +1367,10 @@ private:
virtual VNUser visit(GateLogicVertex* lvertexp, VNUser vu) {
GateClkDecompState* currState = (GateClkDecompState*) vu.c();
int clk_offset = currState->m_offset;
if (AstAssignW* assignp = lvertexp->nodep()->castAssignW()) {
if (const AstAssignW* assignp = VN_CAST(lvertexp->nodep(), AssignW)) {
UINFO(9,"CLK DECOMP Logic (off = "<<clk_offset<<") - "<<lvertexp<<" : "<<m_clk_vsp<<endl);
if (AstSel* rselp = assignp->rhsp()->castSel()) {
if (rselp->lsbp()->castConst() && rselp->widthp()->castConst()) {
if (AstSel* rselp = VN_CAST(assignp->rhsp(), Sel)) {
if (VN_IS(rselp->lsbp(), Const) && VN_IS(rselp->widthp(), Const)) {
if (clk_offset < rselp->lsbConst() || clk_offset > rselp->msbConst()) {
UINFO(9,"CLK DECOMP Sel [ "<<rselp->msbConst()<<" : "<<rselp->lsbConst()<<" ] dropped clock ("<<clk_offset<<")"<<endl);
return VNUser(0);
@ -1382,7 +1379,7 @@ private:
} else {
return VNUser(0);
}
} else if (AstConcat* catp = assignp->rhsp()->castConcat()) {
} else if (AstConcat* catp = VN_CAST(assignp->rhsp(), Concat)) {
UINFO(9,"CLK DECOMP Concat searching - "<<assignp->lhsp()<<endl);
int concat_offset;
if (!m_concat_visitor.concatOffset(catp, currState->m_last_vsp, concat_offset)) {
@ -1390,13 +1387,13 @@ private:
}
clk_offset += concat_offset;
}
if (AstSel* lselp = assignp->lhsp()->castSel()) {
if (lselp->lsbp()->castConst() && lselp->widthp()->castConst()) {
if (const AstSel* lselp = VN_CAST(assignp->lhsp(), Sel)) {
if (VN_IS(lselp->lsbp(), Const) && VN_IS(lselp->widthp(), Const)) {
clk_offset += lselp->lsbConst();
} else {
return VNUser(0);
}
} else if (AstVarRef* vrp = assignp->lhsp()->castVarRef()) {
} else if (const AstVarRef* vrp = VN_CAST(assignp->lhsp(), VarRef)) {
if (vrp->dtypep()->width() == 1 && m_seen_clk_vectors) {
if (clk_offset != 0) {
UINFO(9,"Should only make it here with clk_offset = 0"<<endl);
@ -1468,7 +1465,7 @@ class GateDeassignVisitor : public GateBaseVisitor {
private:
// VISITORS
virtual void visit(AstVarScope* nodep) {
if (AstNodeAssign* assp = nodep->valuep()->castNodeAssign()) {
if (AstNodeAssign* assp = VN_CAST(nodep->valuep(), NodeAssign)) {
UINFO(5," Removeassign "<<assp<<endl);
AstNode* valuep = assp->rhsp();
valuep->unlinkFrBack();
@ -1480,13 +1477,13 @@ private:
virtual void visit(AstVar* nodep) {}
virtual void visit(AstActive* nodep) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit GateDeassignVisitor(AstNode* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~GateDeassignVisitor() {}
};

View File

@ -38,11 +38,7 @@
class GenClkBaseVisitor : public AstNVisitor {
protected:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
@ -65,7 +61,7 @@ private:
// METHODS
AstVarScope* genInpClk(AstVarScope* vscp) {
if (vscp->user2p()) {
return vscp->user2p()->castVarScope();
return VN_CAST(vscp->user2p(), VarScope);
} else {
AstVar* varp = vscp->varp();
string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name();
@ -94,7 +90,7 @@ private:
if (!scopep) nodep->v3fatalSrc("No scope found on top level");
m_scopetopp = scopep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//----
virtual void visit(AstVarRef* nodep) {
@ -115,17 +111,18 @@ private:
}
virtual void visit(AstActive* nodep) {
m_activep = nodep;
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
iterateChildren(nodep->sensesp()); // iterateAndNext?
m_activep = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//-----
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
@ -133,7 +130,7 @@ public:
m_topModp = topModp;
m_scopetopp = NULL;
m_activep = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~GenClkRenameVisitor() {}
};
@ -157,7 +154,7 @@ private:
// VISITORS
virtual void visit(AstTopScope* nodep) {
AstNode::user1ClearTree(); // user1p() used on entire tree
nodep->iterateChildren(*this);
iterateChildren(nodep);
{
// Make the new clock signals and replace any activate references
// See rename, it does some AstNode::userClearTree()'s
@ -168,15 +165,15 @@ private:
// Only track the top scopes, not lower level functions
if (nodep->isTop()) {
m_topModp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstCCall* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->funcp()->entryPoint()) {
// Enter the function and trace it
m_tracingCall = true;
nodep->funcp()->accept(*this);
iterate(nodep->funcp());
}
}
virtual void visit(AstCFunc* nodep) {
@ -187,7 +184,7 @@ private:
return;
}
m_tracingCall = false;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//----
@ -209,21 +206,22 @@ private:
virtual void visit(AstNodeAssign* nodep) {
//UINFO(8,"ASS "<<nodep<<endl);
m_assignp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_assignp = NULL;
}
virtual void visit(AstActive* nodep) {
UINFO(8,"ACTIVE "<<nodep<<endl);
m_activep = nodep;
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
iterateChildren(nodep->sensesp()); // iterateAndNext?
m_activep = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//-----
virtual void visit(AstVar*) {} // Don't want varrefs under it
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
@ -232,7 +230,7 @@ public:
, m_tracingCall(false)
, m_assignp(NULL)
, m_topModp(NULL) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~GenClkReadVisitor() {}
};

View File

@ -33,7 +33,7 @@
#include "V3Graph.h"
int V3Graph::s_debug = 0;
int V3Graph::debug() { return max(V3Error::debugDefault(), s_debug); }
int V3Graph::debug() { return std::max(V3Error::debugDefault(), s_debug); }
//######################################################################
//######################################################################
@ -82,7 +82,7 @@ void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep=iedgep->inNextp()) {
for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) {
new V3GraphEdge (graphp, iedgep->fromp(), oedgep->top(),
min(iedgep->weight(),oedgep->weight()),
std::min(iedgep->weight(),oedgep->weight()),
iedgep->cutable() && oedgep->cutable());
}
}
@ -118,7 +118,41 @@ uint32_t V3GraphVertex::outHash() const {
return hash;
}
ostream& operator<<(ostream& os, V3GraphVertex* vertexp) {
V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way,
const V3GraphVertex* waywardp) {
// O(edges) linear search. Searches search both nodes' edge lists in
// parallel. The lists probably aren't _both_ huge, so this is
// unlikely to blow up even on fairly nasty graphs.
GraphWay inv = way.invert();
V3GraphEdge* aedgep = this->beginp(way);
V3GraphEdge* bedgep = waywardp->beginp(inv);
while (aedgep && bedgep) {
if (aedgep->furtherp(way) == waywardp) return aedgep;
if (bedgep->furtherp(inv) == this) return bedgep;
aedgep = aedgep->nextp(way);
bedgep = bedgep->nextp(inv);
}
return NULL;
}
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
std::ostringstream nsstr;
nsstr<<str.str();
if (debug()) {
nsstr<<endl;
nsstr<<"-vertex: "<<this<<endl;
}
if (!fileline()) {
V3Error::v3errorEnd(nsstr);
} else {
fileline()->v3errorEnd(nsstr);
}
}
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const {
v3errorEnd(str); assert(0);
}
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) {
os<<" VERTEX="<<vertexp->name();
if (vertexp->rank()) os<<" r"<<vertexp->rank();
if (vertexp->fanout()!=0.0) os<<" f"<<vertexp->fanout();
@ -239,12 +273,16 @@ void V3Graph::clearColors() {
//======================================================================
// Dumping
void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
// Needed here as V3GraphVertex<< isn't defined until later in header
cerr<<"-Info-Loop: "<<(void*)(vertexp)<<" "<<vertexp<<endl;
void V3Graph::loopsMessageCb(V3GraphVertex* vertexp) {
vertexp->v3fatalSrc("Loops detected in graph: "<<vertexp);
}
void V3Graph::dump(ostream& os) {
void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
// Needed here as V3GraphVertex<< isn't defined until later in header
std::cerr<<"-Info-Loop: "<<(void*)(vertexp)<<" "<<vertexp<<endl;
}
void V3Graph::dump(std::ostream& os) {
// This generates a file used by graphviz, http://www.graphviz.org
os<<" Graph:\n";
// Print vertices
@ -262,7 +300,7 @@ void V3Graph::dump(ostream& os) {
}
}
void V3Graph::dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
void V3Graph::dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
if (edgep->weight()
&& (edgep->fromp() == vertexp
|| edgep->top() == vertexp)) {
@ -299,7 +337,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
*logp<<"\t\t rankdir="<<dotRankDir()<<"];\n";
// List of all possible subgraphs
typedef multimap<string,V3GraphVertex*> SubgraphMmap;
typedef std::multimap<string,V3GraphVertex*> SubgraphMmap;
SubgraphMmap subgraphs;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
@ -308,7 +346,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
// We use a map here, as we don't want to corrupt anything (userp) in the graph,
// and we don't care if this is slow.
map<V3GraphVertex*,int> numMap;
std::map<V3GraphVertex*,int> numMap;
// Print vertices
int n=0;

View File

@ -27,6 +27,7 @@
#include <vector>
#include <algorithm>
class FileLine;
class V3Graph;
class V3GraphVertex;
class V3GraphEdge;
@ -40,6 +41,38 @@ class OrderLogicVertex;
typedef bool (*V3EdgeFuncP)(const V3GraphEdge* edgep);
//=============================================================================
// When the Graph represents a directional acyclical graph (DAG), following
// the to() edges is forward, and back() is reverse. However, sometimes
// it's useful to have algorithms that can walk in either direction, hence
// some methods take GraphWay to programmatically select the direction.
class GraphWay {
public:
enum en { FORWARD=0,
REVERSE=1,
NUM_WAYS=2 // NUM_WAYS is not an actual way, it's typically
// // an array dimension or loop bound.
};
enum en m_e;
inline GraphWay() : m_e(FORWARD) {}
// cppcheck-suppress noExplicitConstructor
inline GraphWay(en _e) : m_e(_e) {}
explicit inline GraphWay(int _e) : m_e(static_cast<en>(_e)) {}
operator en() const { return m_e; }
const char* ascii() const {
static const char* const names[] = { "FORWARD", "REVERSE" };
return names[m_e];
};
// METHODS unique to this class
GraphWay invert() const { return m_e == FORWARD ? REVERSE : FORWARD; }
bool forward() const { return m_e == FORWARD; }
bool reverse() const { return m_e != FORWARD; }
};
inline bool operator==(GraphWay lhs, GraphWay rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator==(GraphWay lhs, GraphWay::en rhs) { return (lhs.m_e == rhs); }
inline bool operator==(GraphWay::en lhs, GraphWay rhs) { return (lhs == rhs.m_e); }
//============================================================================
class V3Graph {
@ -56,7 +89,7 @@ protected:
void acyclicCut();
void acyclicLoop(V3GraphVertex* vertexp, int depth);
double orderDFSIterate(V3GraphVertex* vertexp);
void dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep);
void dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep);
void verticesUnlink() { m_vertices.reset(); }
// ACCESSORS
static int debug();
@ -101,8 +134,13 @@ public:
/// Order all vertices by rank and fanout, lowest first
/// Sort all vertices by rank and fanout, lowest first
/// Sort all edges by weight, lowest first
/// Side-effect: assigns ranks to every node.
void order();
// Similar to order() but does not assign ranks. Caller must
// ensure that the graph has been ranked ahead of the call.
void orderPreRanked();
/// Make acyclical (into a tree) by breaking a minimal subset of cutable edges.
void acyclic(V3EdgeFuncP edgeFuncp);
@ -118,6 +156,12 @@ public:
/// Remove any redundant edges, weights become SUM of any other weight
void removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp);
/// Remove any transitive edges. E.g. if have edges A->B, B->C, and A->C
/// then A->C is a "transitive" edge; it's implied by the first two
/// (assuming the DAG is a dependency graph.)
/// This algorithm can be expensive.
void removeTransitiveEdges();
/// Call loopsVertexCb on any one loop starting where specified
void reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp);
@ -125,7 +169,7 @@ public:
void subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, V3Graph* loopGraphp);
/// Debugging
void dump(ostream& os=cout);
void dump(std::ostream& os=std::cout);
void dumpDotFile(const string& filename, bool colorAsSubgraph) const;
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false) const;
void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph=false) const;
@ -134,7 +178,7 @@ public:
static void selfTest();
// CALLBACKS
virtual void loopsMessageCb(V3GraphVertex* vertexp) { v3fatalSrc("Loops detected in graph: "<<vertexp); }
virtual void loopsMessageCb(V3GraphVertex* vertexp);
virtual void loopsVertexCb(V3GraphVertex* vertexp);
};
@ -180,6 +224,7 @@ public:
virtual string dotStyle() const { return ""; }
virtual string dotName() const { return ""; }
virtual uint32_t rankAdder() const { return 1; }
virtual FileLine* fileline() const { return NULL; } // NULL for unknown
virtual int sortCmp(const V3GraphVertex* rhsp) const {
// LHS goes first if of lower rank, or lower fanout
if (m_rank < rhsp->m_rank) return -1;
@ -207,11 +252,20 @@ public:
bool outEmpty() const { return outBeginp()==NULL; }
bool outSize1() const;
uint32_t outHash() const;
V3GraphEdge* beginp(GraphWay way) const {
return way.forward() ? outBeginp() : inBeginp(); }
// METHODS
void rerouteEdges(V3Graph* graphp); ///< Edges are routed around this vertex to point from "from" directly to "to"
/// Error reporting
void v3errorEnd(std::ostringstream& str) const;
void v3errorEndFatal(std::ostringstream& str) const;
/// Edges are routed around this vertex to point from "from" directly to "to"
void rerouteEdges(V3Graph* graphp);
/// Find the edge connecting ap and bp, where bp is wayward from ap.
/// If edge is not found returns NULL. O(edges) performance.
V3GraphEdge* findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp);
};
ostream& operator<<(ostream& os, V3GraphVertex* vertexp);
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp);
//============================================================================
@ -275,12 +329,16 @@ public:
uint32_t user() const { return m_user; }
V3GraphVertex* fromp() const { return m_fromp; }
V3GraphVertex* top() const { return m_top; }
V3GraphVertex* closerp(GraphWay way) const { return way.forward() ? fromp() : top(); }
V3GraphVertex* furtherp(GraphWay way) const { return way.forward() ? top() : fromp(); }
// STATIC ACCESSORS
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
// ITERATORS
V3GraphEdge* outNextp() const { return m_outs.nextp(); }
V3GraphEdge* inNextp() const { return m_ins.nextp(); }
V3GraphEdge* nextp(GraphWay way) const {
return way.forward() ? outNextp() : inNextp(); }
};
//============================================================================

View File

@ -56,6 +56,7 @@ public:
bool isDelete() const { return m_deleted; }
virtual string name() const { return m_origVertexp->name(); }
virtual string dotColor() const { return m_origVertexp->dotColor(); }
virtual FileLine* fileline() const { return m_origVertexp->fileline(); }
};
//--------------------------------------------------------------------
@ -63,7 +64,7 @@ public:
class GraphAcycEdge : public V3GraphEdge {
// userp() is always used to point to the head original graph edge
private:
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcyc's decl
typedef std::list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcyc's decl
V3GraphEdge* origEdgep() const {
OrigEdgeList* oEListp = ((OrigEdgeList*)userp());
if (!oEListp) v3fatalSrc("No original edge associated with acyc edge "<<this<<endl);
@ -93,7 +94,7 @@ struct GraphAcycEdgeCmp {
// CLASSES
class GraphAcyc {
private:
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl
typedef std::list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl
// GRAPH USERS
// origGraph
// GraphVertex::user() GraphAycVerted* New graph node
@ -104,7 +105,7 @@ private:
V3Graph* m_origGraphp; // Original graph
V3Graph m_breakGraph; // Graph with only breakable edges represented
V3List<GraphAcycVertex*> m_work; // List of vertices with optimization work left
vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
std::vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph)
uint32_t m_placeStep; // Number that user() must be equal to to indicate processing
@ -189,7 +190,7 @@ public:
m_placeStep = 0;
}
~GraphAcyc() {
for (vector<OrigEdgeList*>::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) {
for (std::vector<OrigEdgeList*>::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) {
delete (*it);
}
m_origEdgeDelp.clear();
@ -453,7 +454,7 @@ void GraphAcyc::place() {
}
UINFO(4, " Cutable edges = "<<numEdges<<endl);
vector<V3GraphEdge*> edges; // List of all edges to be processed
std::vector<V3GraphEdge*> edges; // List of all edges to be processed
edges.reserve(numEdges+1); // Make the vector properly sized right off the bat -- faster than reallocating
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->user(0); // Clear in prep of next step
@ -469,7 +470,7 @@ void GraphAcyc::place() {
// Process each edge in weighted order
m_placeStep = 10;
for (vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
for (std::vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
V3GraphEdge* edgep = (*it);
placeTryEdge(edgep);
}

View File

@ -30,6 +30,7 @@
#include "V3Global.h"
#include "V3GraphAlg.h"
#include "V3GraphPathChecker.h"
//######################################################################
//######################################################################
@ -74,7 +75,7 @@ void V3Graph::deleteCutableOnlyEdges() {
//######################################################################
// Algorithms - weakly connected components
class GraphRemoveRedundant : GraphAlg {
class GraphRemoveRedundant : GraphAlg<> {
bool m_sumWeights; ///< Sum, rather then maximize weights
private:
void main() {
@ -121,7 +122,7 @@ private:
}
public:
GraphRemoveRedundant(V3Graph* graphp, V3EdgeFuncP edgeFuncp, bool sumWeights)
: GraphAlg(graphp, edgeFuncp), m_sumWeights(sumWeights) {
: GraphAlg<>(graphp, edgeFuncp), m_sumWeights(sumWeights) {
main();
}
~GraphRemoveRedundant() {}
@ -134,11 +135,51 @@ void V3Graph::removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp) {
GraphRemoveRedundant (this, edgeFuncp, true);
}
//######################################################################
//######################################################################
// Algorithms - remove transitive
class GraphAlgRemoveTransitiveEdges : GraphAlg<> {
public:
explicit GraphAlgRemoveTransitiveEdges(V3Graph* graphp)
: GraphAlg<>(graphp, NULL) {}
void go() {
GraphPathChecker checker(m_graphp);
for (V3GraphVertex* vxp = m_graphp->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
V3GraphEdge* deletep = NULL;
for (V3GraphEdge* edgep = vxp->outBeginp();
edgep; edgep = edgep->outNextp()) {
if (deletep) {
deletep->unlinkDelete(); deletep = NULL;
}
// It should be safe to modify the graph, despite using
// the GraphPathChecker, as none of the modifications will
// change what can be reached from what, nor should they
// change the rank or CP of any node.
if (checker.isTransitiveEdge(edgep)) {
deletep = edgep;
}
}
if (deletep) {
deletep->unlinkDelete(); VL_DANGLING(deletep);
}
}
}
private:
VL_DEBUG_FUNC; // Declare debug()
VL_UNCOPYABLE(GraphAlgRemoveTransitiveEdges);
};
void V3Graph::removeTransitiveEdges() {
GraphAlgRemoveTransitiveEdges(this).go();
}
//######################################################################
//######################################################################
// Algorithms - weakly connected components
class GraphAlgWeakly : GraphAlg {
class GraphAlgWeakly : GraphAlg<> {
private:
void main() {
// Initialize state
@ -169,7 +210,7 @@ private:
}
public:
GraphAlgWeakly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
main();
}
~GraphAlgWeakly() {}
@ -183,10 +224,10 @@ void V3Graph::weaklyConnected(V3EdgeFuncP edgeFuncp) {
//######################################################################
// Algorithms - strongly connected components
class GraphAlgStrongly : GraphAlg {
class GraphAlgStrongly : GraphAlg<> {
private:
uint32_t m_currentDfs; // DFS count
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
std::vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
void main() {
// Use Tarjan's algorithm to find the strongly connected subgraphs.
@ -254,7 +295,7 @@ private:
}
public:
GraphAlgStrongly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
m_currentDfs = 0;
main();
}
@ -269,7 +310,7 @@ void V3Graph::stronglyConnected(V3EdgeFuncP edgeFuncp) {
//######################################################################
// Algorithms - ranking
class GraphAlgRank : GraphAlg {
class GraphAlgRank : GraphAlg<> {
private:
void main() {
// Rank each vertex, ignoring cutable edges
@ -307,7 +348,7 @@ private:
}
public:
GraphAlgRank(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
main();
}
~GraphAlgRank() {}
@ -325,9 +366,9 @@ void V3Graph::rank(V3EdgeFuncP edgeFuncp) {
//######################################################################
// Algorithms - ranking
class GraphAlgRLoops : GraphAlg {
class GraphAlgRLoops : GraphAlg<> {
private:
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
std::vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
bool m_done; // Exit algorithm
void main(V3GraphVertex* vertexp) {
@ -365,7 +406,7 @@ private:
}
public:
GraphAlgRLoops(V3Graph* graphp, V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
m_done = false;
main(vertexp);
}
@ -381,7 +422,7 @@ void V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) {
//######################################################################
// Algorithms - subtrees
class GraphAlgSubtrees : GraphAlg {
class GraphAlgSubtrees : GraphAlg<> {
private:
V3Graph* m_loopGraphp;
@ -412,7 +453,7 @@ private:
public:
GraphAlgSubtrees(V3Graph* graphp, V3Graph* loopGraphp,
V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
: GraphAlg(graphp, edgeFuncp), m_loopGraphp (loopGraphp) {
: GraphAlg<>(graphp, edgeFuncp), m_loopGraphp (loopGraphp) {
// Vertex::m_userp - New vertex if we have seen this vertex already
// Edge::m_userp - New edge if we have seen this edge already
m_graphp->userClearVertices();
@ -460,20 +501,20 @@ struct GraphSortEdgeCmp {
void V3Graph::sortVertices() {
// Sort list of vertices by rank, then fanout
vector<V3GraphVertex*> vertices;
std::vector<V3GraphVertex*> vertices;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertices.push_back(vertexp);
}
std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp());
this->verticesUnlink();
for (vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
for (std::vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
(*it)->verticesPushBack(this);
}
}
void V3Graph::sortEdges() {
// Sort edges by rank then fanout of node they point to
vector<V3GraphEdge*> edges;
std::vector<V3GraphEdge*> edges;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
// Make a vector
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
@ -486,7 +527,7 @@ void V3Graph::sortEdges() {
// We know the vector contains all of the edges that were
// there originally (didn't delete or add)
vertexp->outUnlink();
for (vector<V3GraphEdge*>::const_iterator it = edges.begin(); it!=edges.end(); ++it) {
for (std::vector<V3GraphEdge*>::const_iterator it = edges.begin(); it!=edges.end(); ++it) {
(*it)->outPushBack();
}
// Prep for next
@ -507,7 +548,10 @@ void V3Graph::order() {
// Compute rankings again
rank(&V3GraphEdge::followAlwaysTrue);
orderPreRanked();
}
void V3Graph::orderPreRanked() {
// Compute fanouts
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
userClearVertices();
@ -529,7 +573,7 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
// Compute fanouts of each node
// If forward edge, don't double count that fanout
if (vertexp->user() == 2) return vertexp->fanout(); // Already processed it
if (vertexp->user() == 1) v3fatalSrc("Loop found, backward edges should be dead");
if (vertexp->user() == 1) vertexp->v3fatalSrc("Loop found, backward edges should be dead");
vertexp->user(1);
double fanout = 0;
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {

View File

@ -30,17 +30,19 @@
// Algorithms - common class
// For internal use, most graph algorithms use this as a base class
template <class T_Graph = V3Graph> // Or sometimes const V3Graph
class GraphAlg {
protected:
V3Graph* m_graphp; // Graph we're operating upon
T_Graph* m_graphp; // Graph we're operating upon
V3EdgeFuncP m_edgeFuncp; // Function that says we follow this edge
// CONSTRUCTORS
GraphAlg(T_Graph* graphp, V3EdgeFuncP edgeFuncp)
: m_graphp(graphp), m_edgeFuncp(edgeFuncp) {}
~GraphAlg() {}
// METHODS
inline bool followEdge(V3GraphEdge* edgep) {
return (edgep->weight() && (m_edgeFuncp)(edgep));
}
GraphAlg(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: m_graphp(graphp), m_edgeFuncp(edgeFuncp) {}
~GraphAlg() {}
};
//============================================================================

View File

@ -40,11 +40,11 @@ DfaVertex* DfaGraph::findStart() {
for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
if (vvertexp->start()) {
if (startp) v3fatalSrc("Multiple start points in NFA graph");
if (startp) vertexp->v3fatalSrc("Multiple start points in NFA graph");
startp = vvertexp;
}
} else {
v3fatalSrc("Non DfaVertex in DfaGraph");
vertexp->v3fatalSrc("Non DfaVertex in DfaGraph");
}
}
if (!startp) v3fatalSrc("No start point in NFA graph");
@ -56,15 +56,15 @@ DfaVertex* DfaGraph::findStart() {
// Algorithms - convert NFA to a DFA
// Uses the Subset Construction Algorithm
class GraphNfaToDfa : GraphAlg {
class GraphNfaToDfa : GraphAlg<> {
// We have two types of nodes in one graph, NFA and DFA nodes.
// Edges from NFA to NFA come from the user, and indicate input or epsilon transitions
// Edges from DFA to NFA indicate the NFA from which that DFA was formed.
// Edges from DFA to DFA indicate a completed input transition
private:
// TYPES
typedef deque<DfaVertex*> DfaStates;
typedef multimap<vluint64_t,DfaVertex*> HashMap;
typedef std::deque<DfaVertex*> DfaStates;
typedef std::multimap<vluint64_t,DfaVertex*> HashMap;
// MEMBERS
uint32_t m_step; // Processing step, so we can avoid clearUser all the time
@ -116,7 +116,9 @@ private:
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
hash ^= hashVertex(nfaStatep);
if (debug()) {
if (nfaStatep->user()==m_step) v3fatalSrc("DFA state points to duplicate NFA state.");
if (nfaStatep->user()==m_step) {
nfaStatep->v3fatalSrc("DFA state points to duplicate NFA state.");
}
nfaStatep->user(m_step);
}
}
@ -174,7 +176,7 @@ private:
// The order of the nodes is not deterministic; the hash thus must not depend on order of edges
uint32_t hash = hashDfaOrigins(nfasWithInput);
pair <HashMap::iterator,HashMap::iterator> eqrange = m_hashMap.equal_range(hash);
std::pair<HashMap::iterator,HashMap::iterator> eqrange = m_hashMap.equal_range(hash);
for (HashMap::iterator it = eqrange.first; it != eqrange.second; ++it) {
DfaVertex* testp = it->second;
if (compareDfaOrigins(nfasWithInput, testp)) {
@ -280,7 +282,7 @@ private:
UINFO(9," On dfaState "<<dfaStatep<<endl);
// From this dfaState, what corresponding nfaStates have what inputs?
set<int> inputs;
std::set<int> inputs;
// Foreach NFA state (this DFA state was formed from)
for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
if (nfaState(dfaEdgep->top())) {
@ -299,7 +301,7 @@ private:
}
// Foreach input state (NFA inputs of this DFA state)
for (set<int>::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) {
for (std::set<int>::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) {
DfaInput input = *inIt;
UINFO(9," ==="<<++i<<"=======================\n");
UINFO(9," On input "<<(void*)(input.toNodep())<<endl);
@ -346,7 +348,7 @@ private:
public:
GraphNfaToDfa(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
m_step = 0;
main();
}
@ -363,7 +365,7 @@ void DfaGraph::nfaToDfa() {
//
// Scan the DFA, cleaning up trailing states.
class DfaGraphReduce : GraphAlg {
class DfaGraphReduce : GraphAlg<> {
private:
// METHODS
static int debug() { return 0; }
@ -402,7 +404,7 @@ private:
m_graphp->userClearVertices();
DfaVertex* startp = graphp()->findStart();
stack<V3GraphVertex*> workps; workps.push(startp);
std::stack<V3GraphVertex*> workps; workps.push(startp);
// Mark all nodes connected to start
while (!workps.empty()) {
@ -437,14 +439,14 @@ private:
m_graphp->userClearVertices();
// Find all dead vertexes
stack<DfaVertex*> workps;
std::stack<DfaVertex*> workps;
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
workps.push(vvertexp);
vertexp->user(1);
} else {
// If ever remove this, need dyn cast below
v3fatalSrc("Non DfaVertex in dfa graph");
vertexp->v3fatalSrc("Non DfaVertex in dfa graph");
}
}
@ -469,7 +471,7 @@ private:
}
public:
DfaGraphReduce(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
: GraphAlg<>(graphp, edgeFuncp) {
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_in");
optimize_accepting_out();
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_acc");
@ -505,7 +507,7 @@ void DfaGraph::dfaReduce() {
// The user's old accept is now the new accept. This is imporant as
// we want the virtual type of it to be intact.
class DfaGraphComplement : GraphAlg {
class DfaGraphComplement : GraphAlg<> {
private:
// MEMBERS
DfaVertex* m_tempNewerReject;
@ -559,7 +561,7 @@ private:
}
public:
DfaGraphComplement(V3Graph* dfagraphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(dfagraphp, edgeFuncp) {
: GraphAlg<>(dfagraphp, edgeFuncp) {
if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_in");
// Vertex::m_user begin: 1 indicates new edge, no more processing

167
src/V3GraphPathChecker.cpp Normal file
View File

@ -0,0 +1,167 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DAG Path Checking
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3GraphStream.h"
#include "V3Global.h"
#include "V3GraphPathChecker.h"
//######################################################################
// GraphPCNode
struct GraphPCNode {
// User data for each node in GraphPathChecker.
//
// Like the LogicMTasks's, store before and after CPs for the nodes in
// the GraphPathChecker graph.
//
// Unlike the LogicMTasks's, we have no cost info for the generic graph
// accepted by GraphPathChecker, so assume each node has unit cost.
vluint32_t m_cp[GraphWay::NUM_WAYS];
// Detect if we've seen this node before in a given recursive
// operation. We'll use this in pathExistsInternal() to avoid checking
// the same node twice, and again in updateHalfCriticalPath() to assert
// there are no cycles.
vluint64_t m_seenAtGeneration;
// CONSTRUCTORS
GraphPCNode() : m_seenAtGeneration(0) {
for (int w = 0; w < GraphWay::NUM_WAYS; w++) m_cp[w] = 0;
}
~GraphPCNode() { }
};
//######################################################################
// GraphPathChecker implementation
GraphPathChecker::GraphPathChecker(const V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg<const V3Graph>(graphp, edgeFuncp)
, m_generation(0) {
for (V3GraphVertex* vxp = graphp->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
// Setup tracking structure for each node. If delete a vertex
// there would be a leak, but ok as accept only const V3Graph*'s.
vxp->userp(new GraphPCNode);
}
// Init critical paths in userp() for each vertex
initHalfCriticalPaths(GraphWay::FORWARD, false);
initHalfCriticalPaths(GraphWay::REVERSE, false);
}
GraphPathChecker::~GraphPathChecker() {
// Free every GraphPCNode
for (V3GraphVertex* vxp = m_graphp->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
GraphPCNode* nodep = static_cast<GraphPCNode*>(vxp->userp());
delete nodep; VL_DANGLING(nodep);
vxp->userp(NULL);
}
}
void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
GraphStreamUnordered order(m_graphp, way);
GraphWay rev = way.invert();
while (const V3GraphVertex* vertexp = order.nextp()) {
unsigned critPathCost = 0;
for (V3GraphEdge* edgep = vertexp->beginp(rev);
edgep; edgep = edgep->nextp(rev)) {
if (!m_edgeFuncp(edgep)) continue;
V3GraphVertex* wrelativep = edgep->furtherp(rev);
GraphPCNode* wrelUserp = static_cast<GraphPCNode*>(wrelativep->userp());
critPathCost = std::max(critPathCost, wrelUserp->m_cp[way] + 1);
}
GraphPCNode* ourUserp = static_cast<GraphPCNode*>(vertexp->userp());
if (checkOnly) {
if (ourUserp->m_cp[way] != critPathCost) {
vertexp->v3fatalSrc("Validation of critical paths failed");
}
} else {
ourUserp->m_cp[way] = critPathCost;
}
}
}
bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap,
const V3GraphVertex* bp,
unsigned* costp) {
GraphPCNode* auserp = static_cast<GraphPCNode*>(ap->userp());
GraphPCNode* buserp = static_cast<GraphPCNode*>(bp->userp());
// If have already searched this node on the current search, don't
// recurse through it again. Since we're still searching, we must not
// have found a path on the first go either.
if (auserp->m_seenAtGeneration == m_generation) {
if (costp) *costp = 0;
return false;
}
auserp->m_seenAtGeneration = m_generation;
if (costp) *costp = 1; // count 'a' toward the search cost
if (ap == bp) return true;
// Rule out an a->b path based on their CPs
if (auserp->m_cp[GraphWay::REVERSE] < buserp->m_cp[GraphWay::REVERSE] + 1) {
return false;
}
if (buserp->m_cp[GraphWay::FORWARD] < auserp->m_cp[GraphWay::FORWARD] + 1) {
return false;
}
// Slow path; visit some extended family
bool foundPath = false;
for (V3GraphEdge* edgep = ap->outBeginp();
edgep && !foundPath; edgep = edgep->outNextp()) {
if (!m_edgeFuncp(edgep)) continue;
unsigned childCost;
if (pathExistsInternal(edgep->top(), bp, &childCost)) {
foundPath = true;
}
if (costp) *costp += childCost;
}
return foundPath;
}
bool GraphPathChecker::pathExistsFrom(const V3GraphVertex* fromp,
const V3GraphVertex* top) {
incGeneration();
return pathExistsInternal(fromp, top);
}
bool GraphPathChecker::isTransitiveEdge(const V3GraphEdge* edgep) {
const V3GraphVertex* fromp = edgep->fromp();
const V3GraphVertex* top = edgep->top();
incGeneration();
for (const V3GraphEdge* fromOutp = fromp->outBeginp();
fromOutp; fromOutp = fromOutp->outNextp()) {
if (fromOutp == edgep) continue;
if (pathExistsInternal(fromOutp->top(), top)) {
return true;
}
}
return false;
}

68
src/V3GraphPathChecker.h Normal file
View File

@ -0,0 +1,68 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DAG Path Checking
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GRAPHPATHCHECKER_H_
#define _V3GRAPHPATHCHECKER_H_
#include "V3Error.h"
#include "V3Graph.h"
#include "V3GraphAlg.h"
//######################################################################
/// Implement pathExistsFrom() with some caching to prune the search.
/// Far more aggressive caching/pruning is possible; for now the use cases
/// don't rely so heavily on this class that it's necessary.
///
/// The graph (or at least, the subset the algorithm sees through
/// edgeFuncp) must not change during the lifetime of the checker.
class GraphPathChecker : GraphAlg<const V3Graph> {
// Count "generations" which increases on operations that scan through
// the graph. Each node is marked with the last generation that scanned
// it, to enable asserting there are no cycles, and to avoid recursing
// through the same node twice while searching for a path.
vluint64_t m_generation;
public:
// CONSTRUCTORS
GraphPathChecker(const V3Graph* graphp,
V3EdgeFuncP edgeFuncp = V3GraphEdge::followAlwaysTrue);
~GraphPathChecker();
// METHODS
bool pathExistsFrom(const V3GraphVertex* fromp, const V3GraphVertex* top);
// If have edges A->B, B->C, and A->C then A->C is considered a
// "transitive" edge (implied by A->B and B->C) and it could be safely
// removed. Detect such an edge.
bool isTransitiveEdge(const V3GraphEdge* edgep);
private:
bool pathExistsInternal(const V3GraphVertex* ap,
const V3GraphVertex* bp,
unsigned* costp = NULL);
void initHalfCriticalPaths(GraphWay w, bool checkOnly);
void incGeneration() { ++m_generation; }
VL_DEBUG_FUNC; // Declare debug()
VL_UNCOPYABLE(GraphPathChecker);
};
#endif // Guard

246
src/V3GraphStream.h Normal file
View File

@ -0,0 +1,246 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Dependency graph iterator. Iterates over nodes
// in any DAG, following dependency order.
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GRAPHSTREAM_H_
#define _V3GRAPHSTREAM_H_
#include "config_build.h"
#include "verilatedos.h"
#include <set>
#include VL_INCLUDE_UNORDERED_MAP
#include "V3Graph.h"
//######################################################################
// GraphStream
//
// Template 'T_Compare' is a tie-breaker for ordering nodes that the DAG
// itself does not order. It must provide an operator() that does a logical
// less-than on two V3GraphVertex*'s, with the same signature as
// std::less<const V3GraphVertex*>::operator(). This does not default to
// std::less<const V3GraphVertex*> because that is nondeterministic, and so
// not generally safe. If you want a raw pointer compare, see
// GraphStreamUnordered below.
template <class T_Compare> class GraphStream {
private:
// TYPES
class VxHolder {
public:
// MEMBERS
const V3GraphVertex* m_vxp; // [mtask] Vertex
uint32_t m_pos; // Sort position
uint32_t m_numBlockingEdges; // Number of blocking edges
// CONSTRUCTORS
VxHolder(const V3GraphVertex* vxp, uint32_t pos, uint32_t numBlockingEdges)
: m_vxp(vxp)
, m_pos(pos)
, m_numBlockingEdges(numBlockingEdges) {}
// METHODS
const V3GraphVertex* vertexp() const { return m_vxp; }
// Decrement blocking edges count, return true if the vertex is
// newly unblocked
bool unblock() {
if (m_numBlockingEdges <= 0) vertexp()->v3fatalSrc("Underflow of blocking edges");
m_numBlockingEdges--;
return (m_numBlockingEdges == 0);
}
};
class VxHolderCmp {
public:
// MEMBERS
T_Compare m_lessThan; // Sorting functor
// CONSTRUCTORS
explicit VxHolderCmp(const T_Compare& lessThan)
: m_lessThan(lessThan) {}
// METHODS
bool operator() (const VxHolder& a, const VxHolder& b) const {
if (m_lessThan.operator()(a.vertexp(), b.vertexp())) return true;
if (m_lessThan.operator()(b.vertexp(), a.vertexp())) return false;
return a.m_pos < b.m_pos;
}
private:
VL_UNCOPYABLE(VxHolderCmp);
};
typedef std::set<VxHolder, VxHolderCmp&> ReadyVertices;
typedef vl_unordered_map<const V3GraphVertex*, VxHolder> WaitingVertices;
// MEMBERS
VxHolderCmp m_vxHolderCmp; // Vertext comparison functor
ReadyVertices m_readyVertices; // List of ready verticies
WaitingVertices m_waitingVertices; // List of wiating verticies
typename ReadyVertices::iterator m_last; // Previously returned element
GraphWay m_way; // FORWARD or REVERSE order of traversal
public:
// CONSTRUCTORS
GraphStream(const V3Graph* graphp,
GraphWay way = GraphWay::FORWARD,
const T_Compare& lessThan = T_Compare())
// NOTE: Perhaps REVERSE way should also reverse the sense of the
// lessThan function? For now the only usage of REVERSE is not
// sensitive to its lessThan at all, so it doesn't matter.
: m_vxHolderCmp(lessThan)
, m_readyVertices(m_vxHolderCmp)
, m_last(m_readyVertices.end())
, m_way(way) {
uint32_t pos = 0;
for (const V3GraphVertex* vxp = graphp->verticesBeginp();
vxp; vxp=vxp->verticesNextp()) {
// Every vertex initially is waiting, or ready.
if (way == GraphWay::FORWARD) {
if (vxp->inEmpty()) {
VxHolder newVx(vxp, pos++, 0);
m_readyVertices.insert(newVx);
} else {
uint32_t depCount = 0;
for (V3GraphEdge* depp = vxp->inBeginp();
depp; depp = depp->inNextp()) {
depCount++;
}
VxHolder newVx(vxp, pos++, depCount);
m_waitingVertices.insert(make_pair(vxp, newVx));
}
} else { // REVERSE
if (vxp->outEmpty()) {
VxHolder newVx(vxp, pos++, 0);
m_readyVertices.insert(newVx);
} else {
uint32_t depCount = 0;
for (V3GraphEdge* depp = vxp->outBeginp();
depp; depp = depp->outNextp()) {
depCount++;
}
VxHolder newVx(vxp, pos++, depCount);
m_waitingVertices.insert(make_pair(vxp, newVx));
}
}
}
}
~GraphStream() {}
// METHODS
// Each call to nextp() returns a unique vertex in the graph, in
// dependency order.
//
// Dependencies alone don't fully specify the order. Usually a graph
// has many "ready" vertices, any of which might return next.
//
// To decide among the "ready" vertices, GraphStream keeps an ordered
// list of ready vertices, sorted first by lessThan and second by
// original graph order.
//
// You might expect that nextp() would return the first item from this
// sorted list -- but that's not what it does! What nextp() actually
// does is to return the next item in the list, following the position
// where the previously-returned item would have been. This maximizes
// locality: given an appropriate lessThan, nextp() will stay on a
// given domain (or domscope, or mtask, or whatever) for as long as
// possible before an unmet dependency forces us to switch to another
// one.
//
// Within a group of vertices that lessThan considers equivalent,
// nextp() returns them in the original graph order (presumably also
// good locality.) V3Order.cpp relies on this to order the logic
// vertices within a given mtask without jumping over domains too much.
const V3GraphVertex* nextp() {
const V3GraphVertex* resultp = NULL;
typename ReadyVertices::iterator curIt;
if (m_last == m_readyVertices.end()) {
// First call to nextp()
curIt = m_readyVertices.begin();
} else {
// Subsequent call to nextp()
curIt = m_last;
++curIt;
// Remove previously-returned element
m_readyVertices.erase(m_last);
// Wrap curIt. Expect to wrap, and make another pass, to find
// newly-ready elements that could have appeared ahead of the
// m_last iterator
if (curIt == m_readyVertices.end()) {
curIt = m_readyVertices.begin();
}
}
if (curIt != m_readyVertices.end()) {
resultp = curIt->vertexp();
unblockDeps(resultp);
} else {
// No ready vertices; waiting should be empty too, otherwise we
// were fed a graph with cycles (which is not supported.)
UASSERT(m_waitingVertices.empty(), "DGS fed non-DAG");
}
m_last = curIt;
return resultp;
}
private:
void unblockDeps(const V3GraphVertex* vertexp) {
if (m_way == GraphWay::FORWARD) {
for (V3GraphEdge* edgep = vertexp->outBeginp();
edgep; edgep=edgep->outNextp()) {
V3GraphVertex* toVertexp = edgep->top();
typename WaitingVertices::iterator it =
m_waitingVertices.find(toVertexp);
if (it == m_waitingVertices.end()) {
toVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
}
if (it->second.unblock()) {
m_readyVertices.insert(it->second);
m_waitingVertices.erase(it);
}
}
} else {
for (V3GraphEdge* edgep = vertexp->inBeginp();
edgep; edgep=edgep->inNextp()) {
V3GraphVertex* fromVertexp = edgep->fromp();
typename WaitingVertices::iterator it =
m_waitingVertices.find(fromVertexp);
if (it == m_waitingVertices.end()) {
fromVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
}
if (it->second.unblock()) {
m_readyVertices.insert(it->second);
m_waitingVertices.erase(it);
}
}
}
}
VL_UNCOPYABLE(GraphStream);
};
//######################################################################
// GraphStreamUnordered is GraphStream using a plain pointer compare to
// break ties in the graph order. This WILL return nodes in
// nondeterministic order.
typedef GraphStream<std::less<const V3GraphVertex*> > GraphStreamUnordered;
#endif // Guard

View File

@ -52,18 +52,16 @@ private:
// STATE
V3Hash m_lowerHash; // Hash of the statement we're building
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
bool m_cacheInUser4; // Use user4 to cache each V3Hash?
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void nodeHashIterate(AstNode* nodep) {
if (!nodep->user4()) {
if (nodep->backp()->castCFunc()
&& !(nodep->castNodeStmt() || nodep->castCFunc())) {
V3Hash thisHash;
if (!m_cacheInUser4 || !nodep->user4()) {
if (VN_IS(nodep->backp(), CFunc)
&& !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))) {
nodep->v3fatalSrc("Node "<<nodep->prettyTypeName()<<" in statement position but not marked stmt (node under function)");
}
V3Hash oldHash = m_lowerHash;
@ -75,15 +73,17 @@ private:
// 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
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Store the hash value
nodep->user4(m_lowerHash.fullValue());
//UINFO(9, " hashnode "<<m_lowerHash<<" "<<nodep<<endl);
}
thisHash = m_lowerHash;
m_lowerHash = oldHash;
}
// Update what will become the above node's hash
m_lowerHash += V3Hashed::nodeHash(nodep);
m_lowerHash += m_cacheInUser4
? V3Hashed::nodeHash(nodep) : thisHash;
}
//--------------------
@ -98,15 +98,26 @@ private:
public:
// CONSTUCTORS
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() {}
};
//######################################################################
// 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.insert(make_pair(nodeHash(nodep), nodep));
@ -144,7 +155,7 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
const vl_unique_ptr<std::ofstream> logp (V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write "<<filename);
map<int,int> dist;
std::map<int,int> dist;
V3Hash lasthash;
int num_in_bucket = 0;
@ -165,8 +176,8 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
}
*logp <<"\n*** STATS:\n"<<endl;
*logp<<" #InBucket Occurrences\n";
for (map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) {
*logp<<" "<<setw(9)<<it->first<<" "<<setw(12)<<it->second<<endl;
for (std::map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) {
*logp<<" "<<std::setw(9)<<it->first<<" "<<std::setw(12)<<it->second<<endl;
}
*logp <<"\n*** Dump:\n"<<endl;
@ -185,7 +196,7 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
UINFO(8," findD "<<nodep<<endl);
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
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 && sameNodes(nodep, node2p)) {
@ -198,7 +209,7 @@ V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp) {
UINFO(8," findD "<<nodep<<endl);
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
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->check(nodep,node2p) && sameNodes(nodep, node2p)) {

View File

@ -36,11 +36,7 @@ public:
~VHashedBase() {}
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
};
//============================================================================
@ -58,7 +54,7 @@ class V3Hashed : public VHashedBase {
AstUser4InUse m_inuser4;
// TYPES
typedef multimap<V3Hash,AstNode*> HashMmap;
typedef std::multimap<V3Hash,AstNode*> HashMmap;
public:
typedef HashMmap::iterator iterator;
private:
@ -87,6 +83,8 @@ public:
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

View File

@ -73,22 +73,18 @@ private:
AstNodeModule* m_modp; // Current module
V3Double0 m_statUnsup; // Statistic tracking
typedef vector<AstNodeModule*> ModVec;
typedef std::vector<AstNodeModule*> ModVec;
ModVec m_allMods; // All modules, in top-down order.
// Within the context of a given module, LocalInstanceMap maps
// from child modules to the count of each child's local instantiations.
typedef map<AstNodeModule*, int> LocalInstanceMap;
typedef std::map<AstNodeModule*, int> LocalInstanceMap;
// We keep a LocalInstanceMap for each module in the design
map<AstNodeModule*, LocalInstanceMap> m_instances;
std::map<AstNodeModule*, LocalInstanceMap> m_instances;
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void cantInline(const char* reason, bool hard) {
if (hard) {
if (m_modp->user2() != CIL_NOTHARD) {
@ -110,20 +106,20 @@ private:
m_allMods.push_back(nodep);
m_modp->user2(CIL_MAYBE);
m_modp->user4(0); // statement count
if (m_modp->castIface()) {
if (VN_IS(m_modp, Iface)) {
// Inlining an interface means we no longer have a cell handle to resolve to.
// If inlining moves post-scope this can perhaps be relaxed.
cantInline("modIface",true);
}
if (m_modp->modPublic()) cantInline("modPublic",false);
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstCell* nodep) {
nodep->modp()->user3Inc(); // Inc refs
m_instances[m_modp][nodep->modp()]++;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstPragma* nodep) {
if (nodep->pragType() == AstPragmaType::INLINE_MODULE) {
@ -143,7 +139,7 @@ private:
}
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell...
} else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstVarXRef* nodep) {
@ -153,23 +149,23 @@ private:
virtual void visit(AstNodeFTaskRef* nodep) {
// Cleanup link until V3LinkDot can correct it
if (!nodep->packagep()) nodep->taskp(NULL);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstAlways* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp->user4Inc(); // statement count
}
virtual void visit(AstNodeAssign* nodep) {
// Don't count assignments, as they'll likely flatten out
// Still need to iterate though to nullify VarXRefs
int oldcnt = m_modp->user4();
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp->user4(oldcnt);
}
virtual void visit(AstNetlist* nodep) {
// Build user2, user3, and user4 for all modules.
// Also build m_allMods and m_instances.
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Iterate through all modules in bottom-up order.
// Make a final inlining decision for each.
@ -201,7 +197,7 @@ private:
|| v3Global.opt.inlineMult() < 1
|| refs*statements < v3Global.opt.inlineMult())));
// Packages aren't really "under" anything so they confuse this algorithm
if (modp->castPackage()) doit = false;
if (VN_IS(modp, Package)) doit = false;
UINFO(4, " Inline="<<doit<<" Possible="<<allowed
<<" Refs="<<refs<<" Stmts="<<statements<<" "<<modp<<endl);
modp->user1(doit);
@ -210,7 +206,7 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_modp) {
m_modp->user4Inc(); // Inc statement count
}
@ -220,7 +216,7 @@ public:
// CONSTUCTORS
explicit InlineMarkVisitor(AstNode* nodep) {
m_modp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~InlineMarkVisitor() {
V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup);
@ -241,11 +237,8 @@ private:
// Output:
// AstCell::user4p() // AstCell* of the created clone
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstCell* nodep) {
@ -255,13 +248,13 @@ private:
virtual void visit(AstNodeStmt* nodep) {}
virtual void visit(AstNodeMath* nodep) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit InlineCollectVisitor(AstNodeModule* nodep) { // passed OLD module, not new one
nodep->accept(*this);
iterate(nodep);
}
virtual ~InlineCollectVisitor() {}
};
@ -282,11 +275,8 @@ private:
AstNodeModule* m_modp; // Current module
AstCell* m_cellp; // Cell being cloned
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstCellInline* nodep) {
@ -298,24 +288,24 @@ private:
nodep->name(name);
UINFO(6, " Inline "<<nodep<<endl);
// Do CellInlines under this, but don't move them
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) {
// Cell under the inline cell, need to rename to avoid conflicts
string name = m_cellp->name() + "__DOT__" + nodep->name();
nodep->name(name);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstModule* nodep) {
m_renamedInterfaces.clear();
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVar* nodep) {
if (nodep->user2p()) {
// Make an assignment, so we'll trace it properly
// user2p is either a const or a var.
AstConst* exprconstp = nodep->user2p()->castConst();
AstVarRef* exprvarrefp = nodep->user2p()->castVarRef();
AstConst* exprconstp = VN_CAST(nodep->user2p(), Const);
AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef);
UINFO(8,"connectto: "<<nodep->user2p()<<endl);
if (!exprconstp && !exprvarrefp) {
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
@ -351,16 +341,16 @@ private:
}
}
// Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module
if (AstIfaceRefDType* ifacerefp = nodep->dtypep()->castIfaceRefDType()) {
if (AstIfaceRefDType* ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
m_renamedInterfaces.insert(nodep->name());
// Each inlined cell that contain an interface variable need to copy the IfaceRefDType and point it to
// the newly cloned interface cell.
AstIfaceRefDType* newdp = ifacerefp->cloneTree(false)->castIfaceRefDType();
AstIfaceRefDType* newdp = VN_CAST(ifacerefp->cloneTree(false), IfaceRefDType);
nodep->dtypep(newdp);
ifacerefp->addNextHere(newdp);
// Relink to point to newly cloned cell
if (newdp->cellp()) {
if (AstCell* newcellp = newdp->cellp()->user4p()->castCell()) {
if (AstCell* newcellp = VN_CAST(newdp->cellp()->user4p(), Cell)) {
newdp->cellp(newcellp);
newdp->cellName(newcellp->name());
// Tag the old ifacerefp to ensure it leaves no stale reference to the inlined cell.
@ -376,24 +366,24 @@ private:
if (!m_cellp->isTrace()) nodep->trace(false);
if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); }
if (debug()>=9 && nodep->valuep()) { nodep->valuep()->dumpTree(cout,"varchangei:"); }
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNodeFTask* nodep) {
// Function under the inline cell, need to rename to avoid conflicts
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstTypedef* nodep) {
// Typedef under the inline cell, need to rename to avoid conflicts
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVarRef* nodep) {
if (nodep->varp()->user2p() // It's being converted to an alias.
&& !nodep->varp()->user3()
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made)
AstConst* exprconstp = nodep->varp()->user2p()->castConst();
AstVarRef* exprvarrefp = nodep->varp()->user2p()->castVarRef();
&& !VN_IS(nodep->backp(), AssignAlias)) { // Don't constant propagate aliases (we just made)
AstConst* exprconstp = VN_CAST(nodep->varp()->user2p(), Const);
AstVarRef* exprvarrefp = VN_CAST(nodep->varp()->user2p(), VarRef);
if (exprconstp) {
nodep->replaceWith(exprconstp->cloneTree(true));
nodep->deleteTree(); VL_DANGLING(nodep);
@ -407,7 +397,7 @@ private:
}
}
nodep->name(nodep->varp()->name());
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVarXRef* nodep) {
// Track what scope it was originally under so V3LinkDot can resolve it
@ -426,7 +416,7 @@ private:
tryname = tryname.substr(0, pos);
}
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNodeFTaskRef* nodep) {
// Track what scope it was originally under so V3LinkDot can resolve it
@ -436,7 +426,7 @@ private:
nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted());
}
UINFO(8," "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// Not needed, as V3LinkDot doesn't care about typedefs
@ -454,15 +444,15 @@ private:
if (afterp) afterp->unlinkFrBackWithNext();
nodep->scopeEntrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
if (afterp) nodep->scopeEntrp(afterp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCoverDecl* nodep) {
// Fix path in coverage statements
nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier()));
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -470,7 +460,7 @@ public:
InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) {
m_modp = oldModp;
m_cellp = cellp;
cloneModp->accept(*this);
iterate(cloneModp);
}
virtual ~InlineRelinkVisitor() {}
};
@ -499,16 +489,13 @@ private:
AstNodeModule* m_modp; // Current module
V3Double0 m_statCells; // Statistic tracking
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstNetlist* nodep) {
// Iterate modules backwards, in bottom-up order. Required!
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
}
virtual void visit(AstIfaceRefDType* nodep) {
if (nodep->user5()) {
@ -520,7 +507,7 @@ private:
}
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) {
if (nodep->modp()->user1()) { // Marked with inline request
@ -532,7 +519,7 @@ private:
// Better off before, as if module has multiple instantiations
// we'll save work, and we can't call pinReconnectSimple in
// this loop as it clone()s itself.
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
if (!pinp->exprp()) continue;
V3Inst::pinReconnectSimple(pinp, nodep, false);
}
@ -551,7 +538,7 @@ private:
nodep->name(), nodep->modp()->origName());
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
// Create assignments to the pins
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
if (!pinp->exprp()) continue;
UINFO(6," Pin change from "<<pinp->modVarp()<<endl);
// Make new signal; even though we'll optimize the interconnect, we
@ -562,17 +549,17 @@ private:
if (!pinNewVarp) pinOldVarp->v3fatalSrc("Cloning failed");
AstNode* connectRefp = pinp->exprp();
if (!connectRefp->castConst() && !connectRefp->castVarRef()) {
if (!VN_IS(connectRefp, Const) && !VN_IS(connectRefp, VarRef)) {
pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
}
if (pinNewVarp->isOutOnly() && connectRefp->castConst()) {
if (pinNewVarp->isOutOnly() && VN_IS(connectRefp, Const)) {
pinp->v3error("Output port is connected to a constant pin, electrical short");
}
// Propagate any attributes across the interconnect
pinNewVarp->propagateAttrFrom(pinOldVarp);
if (connectRefp->castVarRef()) {
connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp);
if (VN_IS(connectRefp, VarRef)) {
VN_CAST(connectRefp, VarRef)->varp()->propagateAttrFrom(pinOldVarp);
}
// One to one interconnect won't make a temporary variable.
@ -605,14 +592,14 @@ private:
virtual void visit(AstNodeMath* nodep) {} // Accelerate
virtual void visit(AstNodeStmt* nodep) {} // Accelerate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit InlineVisitor(AstNode* nodep) {
m_modp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~InlineVisitor() {
V3Stats::addStat("Optimizations, Inlined cells", m_statCells);
@ -638,7 +625,7 @@ void V3Inline::inlineAll(AstNetlist* nodep) {
// idea to avoid dumping the hugely exploded tree.
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = modp->nextp()->castNodeModule();
nextmodp = VN_CAST(modp->nextp(), NodeModule);
if (modp->user1()) { // Was inlined
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
}

View File

@ -51,12 +51,8 @@ private:
// STATE
AstCell* m_cellp; // Current cell
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
//int m_debug; int debug() { return m_debug; }
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstCell* nodep) {
@ -64,7 +60,7 @@ private:
m_cellp = nodep;
//VV***** We reset user1p() on each cell!!!
AstNode::user1ClearTree();
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_cellp = NULL;
}
virtual void visit(AstPin* nodep) {
@ -73,7 +69,7 @@ private:
UINFO(4," PIN "<<nodep<<endl);
if (!nodep->exprp()) return; // No-connect
if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: ");
if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst())
if (nodep->modVarp()->isOutOnly() && VN_IS(nodep->exprp(), Const))
nodep->v3error("Output port is connected to a constant pin, electrical short");
// Use user1p on the PIN to indicate we created an assign for this pin
if (!nodep->user1SetOnce()) {
@ -99,12 +95,12 @@ private:
m_cellp->addNextHere(assp);
if (debug()>=9) assp->dumpTree(cout," _new: ");
} else if (nodep->modVarp()->isIfaceRef()
|| (nodep->modVarp()->subDTypep()->castUnpackArrayDType()
&& nodep->modVarp()->subDTypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType())) {
|| (VN_IS(nodep->modVarp()->subDTypep(), UnpackArrayDType)
&& VN_IS(VN_CAST(nodep->modVarp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType))) {
// Create an AstAssignVarScope for Vars to Cells so we can link with their scope later
AstNode* lhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
AstVarRef* refp = exprp->castVarRef();
AstVarXRef* xrefp = exprp->castVarXRef();
const AstVarRef* refp = VN_CAST(exprp, VarRef);
const AstVarXRef* xrefp = VN_CAST(exprp, VarXRef);
if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef");
AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp);
m_cellp->addNextHere(assp);
@ -132,14 +128,14 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit InstVisitor(AstNetlist* nodep) {
m_cellp=NULL;
//
nodep->accept(*this);
iterate(nodep);
}
virtual ~InstVisitor() {}
};
@ -150,28 +146,24 @@ class InstDeModVarVisitor : public AstNVisitor {
// Expand all module variables, and save names for later reference
private:
// STATE
typedef map<string,AstVar*> VarNameMap;
typedef std::map<string,AstVar*> VarNameMap;
VarNameMap m_modVarNameMap; // Per module, name of cloned variables
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstVar* nodep) {
if (nodep->dtypep()->castIfaceRefDType()) {
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
UINFO(8," dm-1-VAR "<<nodep<<endl);
insert(nodep);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// Save some time
virtual void visit(AstNodeMath*) {}
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// METHODS
@ -198,7 +190,7 @@ public:
void main(AstNodeModule* nodep) {
UINFO(8," dmMODULE "<<nodep<<endl);
m_modVarNameMap.clear();
nodep->accept(*this);
iterate(nodep);
}
virtual ~InstDeModVarVisitor() {}
};
@ -213,26 +205,22 @@ private:
int m_instSelNum; // Current instantiation count 0..N-1
InstDeModVarVisitor m_deModVars; // State of variables for current cell module
typedef map<string,AstVar*> VarNameMap;
typedef std::map<string,AstVar*> VarNameMap;
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstVar* nodep) {
if (nodep->dtypep()->castUnpackArrayDType()
&& nodep->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType()) {
if (VN_IS(nodep->dtypep(), UnpackArrayDType)
&& VN_IS(VN_CAST(nodep->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType)) {
UINFO(8," dv-vec-VAR "<<nodep<<endl);
AstUnpackArrayDType* arrdtype = nodep->dtypep()->castUnpackArrayDType();
AstUnpackArrayDType* arrdtype = VN_CAST(nodep->dtypep(), UnpackArrayDType);
AstNode* prevp = NULL;
for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) {
string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__";
UINFO(8,"VAR name insert "<<varNewName<<" "<<nodep<<endl);
if (!m_deModVars.find(varNewName)) {
AstIfaceRefDType* ifaceRefp = arrdtype->subDTypep()->castIfaceRefDType()->cloneTree(false);
AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false);
arrdtype->addNextHere(ifaceRefp);
ifaceRefp->cellp(NULL);
@ -251,7 +239,7 @@ private:
if (prevp) nodep->addNextHere(prevp);
if (prevp && debug()==9) { prevp->dumpTree(cout, "newintf: "); cout << endl; }
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) {
@ -263,10 +251,10 @@ private:
if (nodep->rangep()) {
m_cellRangep = nodep->rangep();
AstVar* ifaceVarp = nodep->nextp()->castVar();
AstVar* ifaceVarp = VN_CAST(nodep->nextp(), Var);
bool isIface = ifaceVarp
&& ifaceVarp->dtypep()->castUnpackArrayDType()
&& ifaceVarp->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType();
&& VN_IS(ifaceVarp->dtypep(), UnpackArrayDType)
&& VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType);
// Make all of the required clones
for (int i = 0; i < m_cellRangep->elementsConst(); i++) {
@ -287,11 +275,11 @@ private:
// If this AstCell is actually an interface instantiation, also clone the IfaceRef
// within the same parent module as the cell
if (isIface) {
AstUnpackArrayDType* arrdtype = ifaceVarp->dtypep()->castUnpackArrayDType();
AstIfaceRefDType* origIfaceRefp = arrdtype->subDTypep()->castIfaceRefDType();
AstUnpackArrayDType* arrdtype = VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType);
AstIfaceRefDType* origIfaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType);
origIfaceRefp->cellp(NULL);
AstVar* varNewp = ifaceVarp->cloneTree(false);
AstIfaceRefDType* ifaceRefp = arrdtype->subDTypep()->castIfaceRefDType()->cloneTree(false);
AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false);
arrdtype->addNextHere(ifaceRefp);
ifaceRefp->cellp(newp);
ifaceRefp->cellName(newp->name());
@ -302,7 +290,7 @@ private:
if (debug()==9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; }
}
// Fixup pins
newp->pinsp()->iterateAndNext(*this);
iterateAndNextNull(newp->pinsp());
if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<<endl; }
}
@ -314,7 +302,7 @@ private:
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
} else {
m_cellRangep = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
@ -325,8 +313,8 @@ private:
UINFO(4," PIN "<<nodep<<endl);
int pinwidth = nodep->modVarp()->width();
int expwidth = nodep->exprp()->width();
pair<uint32_t,uint32_t> pinDim = nodep->modVarp()->dtypep()->dimensions(false);
pair<uint32_t,uint32_t> expDim = nodep->exprp()->dtypep()->dimensions(false);
std::pair<uint32_t,uint32_t> pinDim = nodep->modVarp()->dtypep()->dimensions(false);
std::pair<uint32_t,uint32_t> expDim = nodep->exprp()->dtypep()->dimensions(false);
UINFO(4," PINVAR "<<nodep->modVarp()<<endl);
UINFO(4," EXP "<<nodep->exprp()<<endl);
UINFO(4," pinwidth ew="<<expwidth<<" pw="<<pinwidth
@ -348,9 +336,9 @@ private:
}
AstNode* exprp = nodep->exprp()->unlinkFrBack();
bool inputPin = nodep->modVarp()->isInput();
if (!inputPin && !exprp->castVarRef()
&& !exprp->castConcat() // V3Const will collapse the SEL with the one we're about to make
&& !exprp->castSel()) { // V3Const will collapse the SEL with the one we're about to make
if (!inputPin && !VN_IS(exprp, VarRef)
&& !VN_IS(exprp, Concat) // V3Const will collapse the SEL with the one we're about to make
&& !VN_IS(exprp, Sel)) { // V3Const will collapse the SEL with the one we're about to make
nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires.");
// Note spec allows more complicated matches such as slices and such
}
@ -361,19 +349,19 @@ private:
} else {
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
}
} else if (AstArraySel* arrselp = nodep->exprp()->castArraySel()) {
if (AstUnpackArrayDType* arrp = arrselp->lhsp()->dtypep()->castUnpackArrayDType()) {
if (!arrp->subDTypep()->castIfaceRefDType())
} else if (AstArraySel* arrselp = VN_CAST(nodep->exprp(), ArraySel)) {
if (AstUnpackArrayDType* arrp = VN_CAST(arrselp->lhsp()->dtypep(), UnpackArrayDType)) {
if (!VN_IS(arrp->subDTypep(), IfaceRefDType))
return;
V3Const::constifyParamsEdit(arrselp->rhsp());
AstConst* constp = arrselp->rhsp()->castConst();
const AstConst* constp = VN_CAST(arrselp->rhsp(), Const);
if (!constp) {
nodep->v3error("Unsupported: Non-constant index when passing interface to module");
return;
}
string index = AstNode::encodeNumber(constp->toSInt());
AstVarRef* varrefp = arrselp->lhsp()->castVarRef();
AstVarRef* varrefp = VN_CAST(arrselp->lhsp(), VarRef);
AstVarXRef* newp = new AstVarXRef(nodep->fileline(), varrefp->name()+"__BRA__"+index+"__KET__", "", true);
newp->dtypep(nodep->modVarp()->dtypep());
newp->packagep(varrefp->packagep());
@ -382,8 +370,8 @@ private:
}
} else {
AstVar* pinVarp = nodep->modVarp();
AstUnpackArrayDType* pinArrp = pinVarp->dtypep()->castUnpackArrayDType();
if (!pinArrp || !pinArrp->subDTypep()->castIfaceRefDType())
AstUnpackArrayDType* pinArrp = VN_CAST(pinVarp->dtypep(), UnpackArrayDType);
if (!pinArrp || !VN_IS(pinArrp->subDTypep(), IfaceRefDType))
return;
AstNode* prevp = NULL;
AstNode* prevPinp = NULL;
@ -397,7 +385,7 @@ private:
if (!pinVarp->backp()) {
varNewp = m_deModVars.find(varNewName);
} else {
AstIfaceRefDType* ifaceRefp = pinArrp->subDTypep()->castIfaceRefDType();
AstIfaceRefDType* ifaceRefp = VN_CAST(pinArrp->subDTypep(), IfaceRefDType);
ifaceRefp->cellp(NULL);
varNewp = pinVarp->cloneTree(false);
varNewp->name(varNewName);
@ -421,7 +409,7 @@ private:
newp->modVarp(varNewp);
newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__");
// And replace exprp with a new varxref
AstVarRef* varrefp = newp->exprp()->castVarRef();
const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef);
string newname = varrefp->name() + "__BRA__" + cvtToStr(i) + "__KET__";
AstVarXRef* newVarXRefp = new AstVarXRef (nodep->fileline(), newname, "", true);
newVarXRefp->varp(newp->modVarp());
@ -448,7 +436,7 @@ private:
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -456,7 +444,7 @@ public:
m_cellRangep=NULL;
m_instSelNum=0;
//
nodep->accept(*this);
iterate(nodep);
}
virtual ~InstDeVisitor() {}
};
@ -466,11 +454,7 @@ public:
class InstStatic {
private:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
InstStatic() {} // Static class
static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) {
@ -496,12 +480,12 @@ public:
// Note this module calles cloneTree() via new AstVar
AstVar* pinVarp = pinp->modVarp();
AstVarRef* connectRefp = pinp->exprp()->castVarRef();
AstVarXRef* connectXRefp = pinp->exprp()->castVarXRef();
AstBasicDType* pinBasicp = pinVarp->dtypep()->castBasicDType(); // Maybe NULL
AstVarRef* connectRefp = VN_CAST(pinp->exprp(), VarRef);
AstVarXRef* connectXRefp = VN_CAST(pinp->exprp(), VarXRef);
AstBasicDType* pinBasicp = VN_CAST(pinVarp->dtypep(), BasicDType); // Maybe NULL
AstBasicDType* connBasicp = NULL;
AstAssignW* assignp = NULL;
if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->castBasicDType();
if (connectRefp) connBasicp = VN_CAST(connectRefp->varp()->dtypep(), BasicDType);
//
if (!alwaysCvt
&& connectRefp
@ -524,7 +508,7 @@ public:
&& !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types
&& connBasicp->width() == pinVarp->width()) {
// Done. One to one interconnect won't need a temporary variable.
} else if (!alwaysCvt && !forTristate && pinp->exprp()->castConst()) {
} else if (!alwaysCvt && !forTristate && VN_IS(pinp->exprp(), Const)) {
// Done. Constant.
} else {
// Make a new temp wire

315
src/V3InstrCount.cpp Normal file
View File

@ -0,0 +1,315 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Estimate instruction count to run the logic
// we would generate for any given AST subtree.
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <iomanip>
#include "V3Ast.h"
#include "V3InstrCount.h"
/// Estimate the instruction cost for executing all logic within and below
/// a given AST node. Note this estimates the number of instructions we'll
/// execute, not the number we'll generate. That is, for conditionals,
/// we'll count instructions from either the 'if' or the 'else' branch,
/// whichever is larger. We know we won't run both.
class InstrCountVisitor : public AstNVisitor {
private:
// NODE STATE
// AstNode::user4() -> int. Path cost + 1, 0 means don't dump
// AstNode::user5() -> bool. Processed if assertNoDups
AstUser4InUse m_inuser4;
// MEMBERS
uint32_t m_instrCount; // Running count of instructions
const AstNode* m_startNodep; // Start node of count
bool m_tracingCall; // Iterating into a CCall to a CFunc
bool m_inCFunc; // Inside AstCFunc
bool m_assertNoDups; // Check for duplicates
std::ostream* m_osp; // Dump file
// TYPES
// Little class to cleanly call startVisitBase/endVisitBase
class VisitBase {
private:
// MEMBERS
uint32_t m_savedCount;
AstNode* m_nodep;
InstrCountVisitor* m_visitor;
public:
// CONSTRUCTORS
VisitBase(InstrCountVisitor* visitor, AstNode* nodep)
: m_nodep(nodep), m_visitor(visitor) {
m_savedCount = m_visitor->startVisitBase(nodep);
}
~VisitBase() {
m_visitor->endVisitBase(m_savedCount, m_nodep);
}
private:
VL_UNCOPYABLE(VisitBase);
};
public:
// CONSTRUCTORS
InstrCountVisitor(AstNode* nodep, bool assertNoDups, std::ostream* osp)
: m_instrCount(0),
m_startNodep(nodep),
m_tracingCall(false),
m_inCFunc(false),
m_assertNoDups(assertNoDups),
m_osp(osp)
{
if (nodep) iterate(nodep);
}
virtual ~InstrCountVisitor() {}
// METHODS
uint32_t instrCount() const { return m_instrCount; }
private:
uint32_t startVisitBase(AstNode* nodep) {
if (m_assertNoDups && !m_inCFunc) {
// Ensure we don't count the same node twice
//
// We only enable this assert for the initial LogicMTask counts
// in V3Order. We can't enable it for the 2nd pass in V3EmitC,
// as we expect mtasks to contain common logic after V3Combine,
// so this would fail.
//
// Also, we expect some collisions within calls to CFuncs
// (which at the V3Order stage represent verilog tasks, not to
// the CFuncs that V3Order will generate.) So don't check for
// collisions in CFuncs.
if (nodep->user5p()) {
nodep->v3fatalSrc("Node originally inserted below logic vertex "
<<static_cast<AstNode*>(nodep->user5p()));
}
nodep->user5p(const_cast<void*>(reinterpret_cast<const void*>(m_startNodep)));
}
// Save the count, and add it back in during ~VisitBase This allows
// debug prints to show local cost of each subtree, so we can see a
// hierarchical view of the cost when in debug mode.
uint32_t savedCount = m_instrCount;
m_instrCount = nodep->instrCount();
return savedCount;
}
void endVisitBase(uint32_t savedCount, AstNode* nodep) {
UINFO(8, "cost "<<std::setw(6)<<std::left<<m_instrCount
<<" "<<nodep<<endl);
markCost(nodep);
m_instrCount += savedCount;
}
void markCost(AstNode* nodep) {
if (m_osp) nodep->user4(m_instrCount+1); // Else don't mark to avoid writeback
}
// VISITORS
virtual void visit(AstNodeSel* nodep) {
// This covers both AstArraySel and AstWordSel
//
// If some vector is a bazillion dwords long, and we're selecting 1
// dword to read or write from it, our cost should be small.
//
// Hence, exclude the child of the AstWordSel from the computation,
// whose cost scales with the size of the entire (maybe large) vector.
VisitBase vb(this, nodep);
iterateAndNextNull(nodep->bitp());
}
virtual void visit(AstSel* nodep) {
// Similar to AstNodeSel above, a small select into a large vector
// is not expensive. Count the cost of the AstSel itself (scales with
// its width) and the cost of the lsbp() and widthp() nodes, but not
// the fromp() node which could be disproportionately large.
VisitBase vb(this, nodep);
iterateAndNextNull(nodep->lsbp());
iterateAndNextNull(nodep->widthp());
}
virtual void visit(AstSliceSel* nodep) {
nodep->v3fatalSrc("AstSliceSel unhandled");
}
virtual void visit(AstMemberSel* nodep) {
nodep->v3fatalSrc("AstMemberSel unhandled");
}
virtual void visit(AstConcat* nodep) {
// Nop.
//
// Ignore concat. The problem with counting concat is that when we
// have many things concatted together, it's not a single
// operation, but this:
//
// concat(a, concat(b, concat(c, concat(d, ... ))))
//
// Then if we account a cost to each 'concat' that scales with its
// width, this whole operation ends up with a cost accounting that
// scales with N^2. Of course, the real operation isn't that
// expensive: we won't copy each element over and over, we'll just
// copy it once from its origin into its destination, so the actual
// cost is linear with the size of the data. We don't need to count
// the concat at all to reflect a linear cost; it's already there
// in the width of the destination (which we count) and the sum of
// the widths of the operands (ignored here).
markCost(nodep);
}
virtual void visit(AstNodeIf* nodep) {
VisitBase vb(this, nodep);
iterateAndNextNull(nodep->condp());
uint32_t savedCount = m_instrCount;
UINFO(8, "ifsp:\n");
m_instrCount = 0;
iterateAndNextNull(nodep->ifsp());
uint32_t ifCount = m_instrCount;
if (nodep->branchPred() == AstBranchPred::BP_UNLIKELY) ifCount = 0;
UINFO(8, "elsesp:\n");
m_instrCount = 0;
iterateAndNextNull(nodep->elsesp());
uint32_t elseCount = m_instrCount;
if (nodep->branchPred() == AstBranchPred::BP_LIKELY) elseCount = 0;
if (ifCount >= elseCount) {
m_instrCount = savedCount + ifCount;
if (nodep->elsesp()) nodep->elsesp()->user4(0); // Don't dump it
} else {
m_instrCount = savedCount + elseCount;
if (nodep->ifsp()) nodep->ifsp()->user4(0); // Don't dump it
}
}
virtual void visit(AstNodeCond* nodep) {
// Just like if/else above, the ternary operator only evaluates
// one of the two expressions, so only count the max.
VisitBase vb(this, nodep);
iterateAndNextNull(nodep->condp());
uint32_t savedCount = m_instrCount;
UINFO(8, "?\n");
m_instrCount = 0;
iterateAndNextNull(nodep->expr1p());
uint32_t ifCount = m_instrCount;
UINFO(8, ":\n");
m_instrCount = 0;
iterateAndNextNull(nodep->expr2p());
uint32_t elseCount = m_instrCount;
if (ifCount < elseCount) {
m_instrCount = savedCount + elseCount;
if (nodep->expr1p()) nodep->expr1p()->user4(0); // Don't dump it
} else {
m_instrCount = savedCount + ifCount;
if (nodep->expr2p()) nodep->expr2p()->user4(0); // Don't dump it
}
}
virtual void visit(AstActive* nodep) {
// You'd think that the OrderLogicVertex's would be disjoint trees
// of stuff in the AST, but it isn't so: V3Order makes an
// OrderLogicVertex for each ACTIVE, and then also makes an
// OrderLogicVertex for each statement within the ACTIVE.
//
// To avoid double-counting costs, stop recursing and short-circuit
// the computation for each ACTIVE.
//
// Our intent is that this only stops at the root node of the
// search; there should be no actives beneath the root, as there
// are no actives-under-actives. In any case, check that we're at
// root:
markCost(nodep);
if (nodep != m_startNodep) {
nodep->v3fatalSrc("Multiple actives, or not start node");
}
}
virtual void visit(AstCCall* nodep) {
VisitBase vb(this, nodep);
iterateChildren(nodep);
m_tracingCall = true;
iterate(nodep->funcp());
if (m_tracingCall) {
nodep->v3fatalSrc("visit(AstCFunc) should have cleared m_tracingCall.");
}
}
virtual void visit(AstCFunc* nodep) {
// Don't count a CFunc other than by tracing a call or counting it
// from the root
if (!m_tracingCall && (nodep != m_startNodep)) {
nodep->v3fatalSrc("AstCFunc not under AstCCall, or not start node");
}
m_tracingCall = false;
bool saved_inCFunc = m_inCFunc;
m_inCFunc = true;
{
VisitBase vb(this, nodep);
iterateChildren(nodep);
}
m_inCFunc = saved_inCFunc;
}
virtual void visit(AstNode* nodep) {
VisitBase vb(this, nodep);
iterateChildren(nodep);
}
VL_DEBUG_FUNC; // Declare debug()
VL_UNCOPYABLE(InstrCountVisitor);
};
// Iterate the graph printing the critical path marked by previous visitation
class InstrCountDumpVisitor : public AstNVisitor {
private:
// NODE STATE
// AstNode::user4() -> int. Path cost, 0 means don't dump
// MEMBERS
std::ostream* m_osp; // Dump file
unsigned m_depth; // Current tree depth for printing indent
public:
// CONSTRUCTORS
InstrCountDumpVisitor(AstNode* nodep, std::ostream* osp)
: m_osp(osp), m_depth(0) {
// No check for NULL output, so...
if (!osp) nodep->v3fatalSrc("Don't call if not dumping");
if (nodep) iterate(nodep);
}
virtual ~InstrCountDumpVisitor() {}
private:
// METHODS
string indent() { return string(m_depth, ':')+" "; }
virtual void visit(AstNode* nodep) {
++m_depth;
if (unsigned costPlus1 = nodep->user4()) {
*m_osp <<" "<<indent()
<<"cost "<<std::setw(6)<<std::left<<(costPlus1-1)
<<" "<<nodep<<endl;
iterateChildren(nodep);
}
--m_depth;
}
VL_DEBUG_FUNC; // Declare debug()
VL_UNCOPYABLE(InstrCountDumpVisitor);
};
uint32_t V3InstrCount::count(AstNode* nodep, bool assertNoDups, std::ostream* osp) {
InstrCountVisitor visitor(nodep, assertNoDups, osp);
if (osp) InstrCountDumpVisitor dumper(nodep, osp);
return visitor.instrCount();
}

43
src/V3InstrCount.h Normal file
View File

@ -0,0 +1,43 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Estimate instruction count to run the logic
// we would generate for any given AST subtree.
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2018 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
class AstNode;
class V3InstrCount {
public:
// Return the estimate count of instructions we'd incur while running
// code in and under nodep.
//
// This is a rough estimate; we don't know what path we'll take through
// conditionals in nodep, so we assume we take the longest path.
//
// If nodep is an AstActive, returns 0.
// If nodep contains nested AstActives, raises an error.
//
// If assertNoDups is true, marks user5 on each AstNode scanned. Then
// if we see the same node twice (across more than one call to count,
// potentially) raises an error.
// Optional osp is stream to dump critical path to.
static uint32_t count(AstNode* nodep, bool assertNoDups, std::ostream* osp = NULL);
};

View File

@ -30,14 +30,15 @@ class V3LanguageWords {
// List of common reserved keywords
private:
map<string,string> m_kwdMap; // List of keywords, and what language applies
typedef std::map<string,string> KeywordMap;
KeywordMap m_kwdMap; // List of keywords, and what language applies
void addKwd(const string& kwd, const string& why) {
m_kwdMap.insert(make_pair(kwd,why));
}
public:
string isKeyword(const string& kwd) {
map<string,string>::iterator it = m_kwdMap.find(kwd);
KeywordMap::iterator it = m_kwdMap.find(kwd);
if (it == m_kwdMap.end()) return "";
return it->second;
}
@ -107,8 +108,8 @@ class V3LanguageWords {
addKwd("long", "C++ keyword");
addKwd("map", "C++ common word");
addKwd("module", "C++ modules TS keyword");
addKwd("multimap", "C++ common word");
addKwd("multiset", "C++ common word");
addKwd("std::multimap", "C++ common word");
addKwd("std::multiset", "C++ common word");
addKwd("mutable", "C++ keyword");
addKwd("namespace", "C++ keyword");
addKwd("near", "C++ common word");

View File

@ -53,7 +53,7 @@ class LifeState {
public:
V3Double0 m_statAssnDel; // Statistic tracking
V3Double0 m_statAssnCon; // Statistic tracking
vector<AstNode*> m_unlinkps;
std::vector<AstNode*> m_unlinkps;
public:
// CONSTRUCTORS
@ -61,7 +61,7 @@ public:
~LifeState() {
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
for (vector<AstNode*>::iterator it = m_unlinkps.begin(); it != m_unlinkps.end(); ++it) {
for (std::vector<AstNode*>::iterator it = m_unlinkps.begin(); it != m_unlinkps.end(); ++it) {
(*it)->unlinkFrBack();
(*it)->deleteTree();
}
@ -104,7 +104,7 @@ public:
m_assignp = assp;
m_constp = NULL;
m_everSet = true;
if (assp->rhsp()->castConst()) m_constp = assp->rhsp()->castConst();
if (VN_IS(assp->rhsp(), Const)) m_constp = VN_CAST(assp->rhsp(), Const);
}
inline void complexAssign() { // A[x]=... or some complicated assignment
m_assignp = NULL;
@ -135,11 +135,7 @@ class LifeBlock {
LifeBlock* m_aboveLifep; // Upper life, or NULL
LifeState* m_statep; // Current global state
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
public:
LifeBlock(LifeBlock* aboveLifep, LifeState* statep) {
@ -297,11 +293,7 @@ private:
LifeBlock* m_lifep; // Current active lifetime map for current scope
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstVarRef* nodep) {
@ -322,42 +314,42 @@ private:
// Similar code in V3Dead
vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
m_sideEffect = false;
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
if (lastEdit != AstNode::editCountGbl()) {
// We changed something, try to constant propagate, but don't delete the
// assignment as we still need nodep to remain.
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
}
// Has to be direct assignment without any EXTRACTing.
if (nodep->lhsp()->castVarRef() && !m_sideEffect && !m_noopt) {
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) {
AstVarScope* vscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
m_lifep->simpleAssign(vscp, nodep);
} else {
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
}
}
virtual void visit(AstAssignDly* nodep) {
// Don't treat as normal assign; V3Life doesn't understand time sense
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//---- Track control flow changes
virtual void visit(AstNodeIf* nodep) {
UINFO(4," IF "<<nodep<<endl);
// Condition is part of PREVIOUS block
nodep->condp()->iterateAndNext(*this);
iterateAndNextNull(nodep->condp());
LifeBlock* prevLifep = m_lifep;
LifeBlock* ifLifep = new LifeBlock (prevLifep, m_statep);
LifeBlock* elseLifep = new LifeBlock (prevLifep, m_statep);
{
m_lifep = ifLifep;
nodep->ifsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->ifsp());
m_lifep = prevLifep;
}
{
m_lifep = elseLifep;
nodep->elsesp()->iterateAndNext(*this);
iterateAndNextNull(nodep->elsesp());
m_lifep = prevLifep;
}
UINFO(4," join "<<endl);
@ -383,14 +375,14 @@ private:
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
{
m_lifep = condLifep;
nodep->precondsp()->iterateAndNext(*this);
nodep->condp()->iterateAndNext(*this);
iterateAndNextNull(nodep->precondsp());
iterateAndNextNull(nodep->condp());
m_lifep = prevLifep;
}
{
m_lifep = bodyLifep;
nodep->bodysp()->iterateAndNext(*this);
nodep->incsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->incsp());
m_lifep = prevLifep;
}
UINFO(4," joinfor"<<endl);
@ -410,7 +402,7 @@ private:
{
m_lifep = bodyLifep;
m_noopt = true;
nodep->stmtsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->stmtsp());
m_lifep = prevLifep;
m_noopt = prev_noopt;
}
@ -421,11 +413,11 @@ private:
}
virtual void visit(AstCCall* nodep) {
//UINFO(4," CCALL "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Enter the function and trace it
if (!nodep->funcp()->entryPoint()) { // else is non-inline or public function we optimize separately
m_tracingCall = true;
nodep->funcp()->accept(*this);
iterate(nodep->funcp());
}
}
virtual void visit(AstCFunc* nodep) {
@ -435,20 +427,20 @@ private:
if (nodep->dpiImport() && !nodep->pure()) {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstUCFunc* nodep) {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCMath* nodep) {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVar*) {} // Don't want varrefs under it
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -461,7 +453,7 @@ public:
m_tracingCall = false;
{
m_lifep = new LifeBlock (NULL, m_statep);
nodep->accept(*this);
iterate(nodep);
if (m_lifep) { delete m_lifep; m_lifep=NULL; }
}
}
@ -502,13 +494,13 @@ private:
virtual void visit(AstNodeStmt*) {} // Accelerate
virtual void visit(AstNodeMath*) {} // Accelerate
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
LifeTopVisitor(AstNetlist* nodep, LifeState* statep) {
m_statep = statep;
nodep->accept(*this);
iterate(nodep);
}
virtual ~LifeTopVisitor() {}
};

View File

@ -32,29 +32,21 @@
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <memory> // for vl_unique_ptr -> auto_ptr or unique_ptr
#include <unistd.h>
#include VL_INCLUDE_UNORDERED_MAP
#include "V3Global.h"
#include "V3PartitionGraph.h"
#include "V3GraphPathChecker.h"
#include "V3LifePost.h"
#include "V3Stats.h"
#include "V3Ast.h"
//######################################################################
// LifePost state, as a visitor of each AstNode
class LifePostBaseVisitor : public AstNVisitor {
protected:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
};
//######################################################################
// LifePost class functions
class LifePostElimVisitor : public LifePostBaseVisitor {
class LifePostElimVisitor : public AstNVisitor {
private:
bool m_tracingCall; // Iterating into a CCall to a CFunc
@ -62,6 +54,9 @@ private:
// INPUT:
// AstVarScope::user4p() -> AstVarScope*, If set, replace this varscope with specified new one
// STATE
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstVarRef* nodep) {
AstVarScope* vscp = nodep->varScopep();
@ -75,132 +70,296 @@ private:
}
virtual void visit(AstNodeModule* nodep) {
// Only track the top scopes, not lower level functions
if (nodep->isTop()) nodep->iterateChildren(*this);
if (nodep->isTop()) iterateChildren(nodep);
}
virtual void visit(AstCCall* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->funcp()->entryPoint()) {
// Enter the function and trace it
m_tracingCall = true;
nodep->funcp()->accept(*this);
iterate(nodep->funcp());
}
}
virtual void visit(AstExecGraph* nodep) {
// Can just iterate across the MTask bodies in any order. Order
// isn't important for LifePostElimVisitor's simple substitution.
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) {
if (!m_tracingCall && !nodep->entryPoint()) return;
m_tracingCall = false;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstVar*) {} // Don't want varrefs under it
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit LifePostElimVisitor(AstTopScope* nodep)
: m_tracingCall(false) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~LifePostElimVisitor() {}
};
//######################################################################
// Location within the execution graph, identified by an mtask
// and a sequence number within the mtask:
struct LifeLocation {
const ExecMTask* mtaskp;
uint32_t sequence;
public:
LifeLocation() : mtaskp(NULL), sequence(0) {}
LifeLocation(const ExecMTask* mtaskp_, uint32_t sequence_)
: mtaskp(mtaskp_), sequence(sequence_) {}
bool operator< (const LifeLocation& b) const {
unsigned a_id = mtaskp ? mtaskp->id() : 0;
unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0;
if (a_id < b_id) { return true; }
if (b_id < a_id) { return false; }
return sequence < b.sequence;
}
};
struct LifePostLocation {
LifeLocation loc;
AstAssignPost* nodep;
LifePostLocation() : nodep(NULL) {}
LifePostLocation(LifeLocation loc_, AstAssignPost* nodep_)
: loc(loc_), nodep(nodep_) {}
};
//######################################################################
// LifePost delay elimination
class LifePostDlyVisitor : public LifePostBaseVisitor {
class LifePostDlyVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared on entire tree
// AstVarScope::user() -> Sequence # of first vertex setting this var.
// AstVarScope::user2() -> Sequence # of last consumption of this var
// AstVarScope::user4() -> AstVarScope*: Passed to LifePostElim to substitute this var
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
AstUser4InUse m_inuser4;
// STATE
uint32_t m_sequence; // Sequence number of assignments/varrefs
uint32_t m_sequence; // Sequence number of assigns/varrefs,
// // local to the current MTask.
const ExecMTask* m_execMTaskp; // Current ExecMTask being processed,
// // or NULL for serial code.
V3Double0 m_statAssnDel; // Statistic tracking
bool m_tracingCall; // Tracing a CCall to a CFunc
bool m_tracingCall; // Currently tracing a CCall to a CFunc
// Map each varscope to one or more locations where it's accessed.
// These maps will not include any ASSIGNPOST accesses:
typedef vl_unordered_map<const AstVarScope*, std::set<LifeLocation> > LocMap;
LocMap m_reads; // VarScope read locations
LocMap m_writes; // VarScope write locations
// Map each dly var to its AstAssignPost* node and the location thereof
typedef vl_unordered_map<const AstVarScope*, LifePostLocation> PostLocMap;
PostLocMap m_assignposts; // AssignPost dly var locations
const V3Graph* m_mtasksGraphp; // Mtask tracking graph
vl_unique_ptr<GraphPathChecker> m_checker;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool before(const LifeLocation& a, const LifeLocation& b) {
if (a.mtaskp == b.mtaskp) return a.sequence < b.sequence;
return m_checker->pathExistsFrom(a.mtaskp, b.mtaskp);
}
bool outsideCriticalArea(LifeLocation loc,
const std::set<LifeLocation>& dlyVarAssigns,
LifeLocation assignPostLoc) {
// If loc is before all of dlyVarAssigns, return true.
// ("Before" means certain to be ordered before them at execution time.)
// If assignPostLoc is before loc, return true.
//
// Otherwise, loc could fall in the "critical" area where the
// substitution affects the result of the operation at loc, so
// return false.
if (!loc.mtaskp && assignPostLoc.mtaskp) {
// This is threaded mode; 'loc' is something that happens at
// initial/settle time, or perhaps in _eval() but outside of
// the mtask graph.
// In either case, it's not in the critical area.
return true;
}
if (before(assignPostLoc, loc)) return true;
for (std::set<LifeLocation>::iterator it = dlyVarAssigns.begin();
it != dlyVarAssigns.end(); ++it) {
if (!before(loc, *it)) return false;
}
return true;
}
void squashAssignposts() {
for (PostLocMap::iterator it = m_assignposts.begin();
it != m_assignposts.end(); ++it) {
LifePostLocation* app = &it->second;
AstVarRef* lhsp = VN_CAST(app->nodep->lhsp(), VarRef); // original var
AstVarRef* rhsp = VN_CAST(app->nodep->rhsp(), VarRef); // dly var
AstVarScope* dlyVarp = rhsp->varScopep();
AstVarScope* origVarp = lhsp->varScopep();
// Scrunch these:
// X1: __Vdly__q = __PVT__clk_clocks;
// ... {no reads or writes of __PVT__q after the first write to __Vdly__q}
// ... {no reads of __Vdly__q after the first write to __Vdly__q}
// X2: __PVT__q = __Vdly__q;
//
// Into just this:
// X1: __PVT__q = __PVT__clk_clocks;
// X2: (nothing)
// More formally, with the non-sequential mtasks graph, we must
// prove all of these before doing the scrunch:
// 1) No reads of the dly var anywhere except for the ASSIGNPOST
// 2) Every read of the original var either falls before each of
// the dly var's assignments, or after the ASSIGNPOST.
// 3) Every write of the original var either falls before each of
// the dly var's assignments, or after the ASSIGNPOST.
const std::set<LifeLocation>& dlyVarAssigns = m_writes[dlyVarp];
// Proof (1)
const std::set<LifeLocation>& dlyVarReads = m_reads[dlyVarp];
if (dlyVarReads.size() > 0) {
continue; // do not scrunch, go to next LifePostLocation
}
// Proof (2)
bool canScrunch = true;
const std::set<LifeLocation>& origVarReads = m_reads[origVarp];
for (std::set<LifeLocation>::iterator rdLocIt = origVarReads.begin();
rdLocIt != origVarReads.end(); ++rdLocIt) {
if (!outsideCriticalArea(*rdLocIt, dlyVarAssigns, app->loc)) {
canScrunch = false;
break;
}
}
if (!canScrunch) continue;
// Proof (3)
const std::set<LifeLocation>& origVarWrites = m_writes[origVarp];
for (std::set<LifeLocation>::iterator wrLocIt = origVarWrites.begin();
wrLocIt != origVarWrites.end(); ++wrLocIt) {
if (!outsideCriticalArea(*wrLocIt, dlyVarAssigns, app->loc)) {
canScrunch = false;
break;
}
}
if (!canScrunch) continue;
// Delete and mark so LifePostElimVisitor will get it
UINFO(4," DELETE "<<app->nodep<<endl);
dlyVarp->user4p(origVarp);
app->nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(app->nodep);
++m_statAssnDel;
}
}
// VISITORS
virtual void visit(AstTopScope* nodep) {
AstNode::user1ClearTree(); // user1p() used on entire tree
AstNode::user2ClearTree(); // user2p() used on entire tree
AstNode::user4ClearTree(); // user4p() used on entire tree
m_sequence = 0;
nodep->iterateChildren(*this);
// First, build maps of every location (mtask and sequence
// within the mtask) where each varscope is read, and written.
iterateChildren(nodep);
if (v3Global.opt.mtasks()) {
if (!m_mtasksGraphp) {
nodep->v3fatalSrc("Should have initted m_mtasksGraphp by now");
}
m_checker.reset(new GraphPathChecker(m_mtasksGraphp));
} else {
if (m_mtasksGraphp) {
nodep->v3fatalSrc("Did not expect any m_mtasksGraphp in serial mode");
}
}
// Find all assignposts. Determine which ones can be
// eliminated. Remove those, and mark their dly vars' user4 field
// to indicate we should replace these dly vars with their original
// variables.
squashAssignposts();
// Replace any node4p varscopes with the new scope
LifePostElimVisitor visitor (nodep);
}
virtual void visit(AstVarRef* nodep) {
// Consumption/generation of a variable,
AstVarScope* vscp = nodep->varScopep();
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
m_sequence++;
LifeLocation loc(m_execMTaskp, ++m_sequence);
if (nodep->lvalue()) {
// First generator
if (!vscp->user1()) vscp->user1(m_sequence);
m_writes[vscp].insert(loc);
} else {
// Last consumer
vscp->user2(m_sequence);
m_reads[vscp].insert(loc);
}
}
virtual void visit(AstAssignPre* nodep) {
// Do not record varrefs within assign pre.
//
// The pre-assignment into the dly var should not count as its
// first write; we only want to consider reads and writes that
// would still happen if the dly var were eliminated.
}
virtual void visit(AstAssignPost* nodep) {
if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) {
if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) {
// Scrunch these:
// __Vdly__q = __PVT__clk_clocks;
// ... {no reads or writes of __PVT__q after the first write to __Vdly__q}
// ... {no reads of __Vdly__q after the first write to __Vdly__q}
// __PVT__q = __Vdly__q;
UINFO(9," POST "<<nodep<<endl);
UINFO(9," lhs "<<lhsp<<endl);
UINFO(9," rhs "<<rhsp<<endl);
uint32_t firstWrite = rhsp->varScopep()->user1();
uint32_t lastRead = rhsp->varScopep()->user2();
uint32_t lastRead2 = lhsp->varScopep()->user2();
UINFO(9," first "<<firstWrite<<" last "<<lastRead<<" "<<lastRead2<<endl);
if (lastRead < firstWrite
&& lastRead2 < firstWrite) {
UINFO(4," DELETE "<<nodep<<endl);
// Mark so LifePostElimVisitor will get it
rhsp->varScopep()->user4p(lhsp->varScopep());
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
++m_statAssnDel;
}
// Don't record ASSIGNPOST in the read/write maps, record them in a
// separate map
if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
// rhsp is the dly var
AstVarScope* dlyVarp = rhsp->varScopep();
if (m_assignposts.find(dlyVarp) != m_assignposts.end()) {
nodep->v3fatalSrc("LifePostLocation attempted duplicate dlyvar map addition");
}
LifeLocation loc(m_execMTaskp, ++m_sequence);
m_assignposts[dlyVarp] = LifePostLocation(loc, nodep);
}
}
virtual void visit(AstNodeModule* nodep) {
// Only track the top scopes, not lower level functions
if (nodep->isTop()) nodep->iterateChildren(*this);
if (nodep->isTop()) iterateChildren(nodep);
}
virtual void visit(AstCCall* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->funcp()->entryPoint()) {
// Enter the function and trace it
m_tracingCall = true;
nodep->funcp()->accept(*this);
iterate(nodep->funcp());
}
}
virtual void visit(AstExecGraph* nodep) {
// Treat the ExecGraph like a call to each mtask body
m_mtasksGraphp = nodep->depGraphp();
for (V3GraphVertex* mtaskVxp = m_mtasksGraphp->verticesBeginp();
mtaskVxp; mtaskVxp = mtaskVxp->verticesNextp()) {
ExecMTask* mtaskp = dynamic_cast<ExecMTask*>(mtaskVxp);
m_execMTaskp = mtaskp;
m_sequence = 0;
iterate(mtaskp->bodyp());
}
m_execMTaskp = NULL;
}
virtual void visit(AstCFunc* nodep) {
if (!m_tracingCall && !nodep->entryPoint()) return;
m_tracingCall = false;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
//-----
virtual void visit(AstVar*) {} // Don't want varrefs under it
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit LifePostDlyVisitor(AstNetlist* nodep)
: m_tracingCall(false) {
nodep->accept(*this);
: m_sequence(0)
, m_execMTaskp(NULL)
, m_tracingCall(false)
, m_mtasksGraphp(NULL) {
iterate(nodep);
}
virtual ~LifePostDlyVisitor() {
V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel);
@ -214,7 +373,7 @@ void V3LifePost::lifepostAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
// Mark redundant AssignPost
{
LifePostDlyVisitor visitor (nodep);
LifePostDlyVisitor visitor(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("life_post", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -62,6 +62,7 @@ public:
virtual ~LinkCellsVertex() {}
AstNodeModule* modp() const { return m_modp; }
virtual string name() const { return modp()->name(); }
virtual FileLine* fileline() const { return modp()->fileline(); }
// Recursive modules get space for maximum recursion
virtual uint32_t rankAdder() const { return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; }
};
@ -113,11 +114,7 @@ private:
V3GraphVertex* m_topVertexp; // Vertex of top module
vl_unordered_set<string> m_declfnWarned; // Files we issued DECLFILENAME on
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// METHODS
V3GraphVertex* vertex(AstNodeModule* nodep) {
@ -131,7 +128,7 @@ private:
AstNodeModule* findModuleSym(const string& modName) {
VSymEnt* foundp = m_mods.rootp()->findIdFallback(modName);
if (!foundp) return NULL;
else return foundp->nodep()->castNodeModule();
else return VN_CAST(foundp->nodep(), NodeModule);
}
AstNodeModule* resolveModule(AstNode* nodep, const string& modName) {
@ -160,7 +157,7 @@ private:
virtual void visit(AstNetlist* nodep) {
AstNode::user1ClearTree();
readModNames();
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Find levels in graph
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
m_graph.dumpDotFilePrefixed("linkcells");
@ -203,7 +200,7 @@ private:
<<"' does not match "<<nodep->typeName()<<" name: "<<nodep->prettyName());
}
}
if (nodep->castIface() || nodep->castPackage()) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
bool topMatch = (v3Global.opt.topModule()==nodep->prettyName());
if (topMatch) {
m_topVertexp = vertex(nodep);
@ -219,7 +216,7 @@ private:
new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false);
}
// Note AstBind also has iteration on cells
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->checkTree();
m_modp = NULL;
}
@ -231,11 +228,11 @@ private:
// but we might support modules-under-modules someday.
AstNodeModule* modp = resolveModule(nodep, nodep->ifaceName());
if (modp) {
if (modp->castIface()) {
if (VN_IS(modp, Iface)) {
// Track module depths, so can sort list from parent down to children
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
if (!nodep->cellp()) nodep->ifacep(modp->castIface());
} else if (modp->castNotFoundModule()) { // Will error out later
if (!nodep->cellp()) nodep->ifacep(VN_CAST(modp, Iface));
} else if (VN_IS(modp, NotFoundModule)) { // Will error out later
} else {
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
}
@ -245,7 +242,7 @@ private:
virtual void visit(AstPackageImport* nodep) {
// Package Import: We need to do the package before the use of a package
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->packagep()) nodep->v3fatalSrc("Unlinked package"); // Parser should set packagep
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false);
}
@ -264,7 +261,7 @@ private:
{
m_modp = modp;
modp->addStmtp(cellsp); // Important that this adds to end, as next iterate assumes does all cells
cellsp->iterateAndNext(*this);
iterateAndNextNull(cellsp);
}
m_modp = oldModp;
}
@ -298,7 +295,7 @@ private:
// This lets us link the XREFs between the (uncloned) children so
// they don't point to the same module which would
// break parameter resolution.
AstNodeModule* otherModp = cellmodp->user2p()->castNodeModule();
AstNodeModule* otherModp = VN_CAST(cellmodp->user2p(), NodeModule);
if (!otherModp) {
otherModp = cellmodp->cloneTree(false);
otherModp->name(otherModp->name()+"__Vrcm");
@ -342,7 +339,7 @@ private:
// Convert .* to list of pins
bool pinStar = false;
for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) {
nextp = pinp->nextp()->castPin();
nextp = VN_CAST(pinp->nextp(), Pin);
if (pinp->dotStar()) {
if (pinStar) pinp->v3error("Duplicate .* in a cell");
pinStar = true;
@ -351,10 +348,10 @@ private:
}
}
// Convert unnamed pins to pin number based assignments
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum()));
}
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
pinp->param(true);
if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum()));
}
@ -362,7 +359,7 @@ private:
nodep->modName(nodep->modp()->name());
// Note what pins exist
vl_unordered_set<string> ports; // Symbol table of all connected port names
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells");
if (!pinp->exprp()) {
if (pinp->name().substr(0, 11) == "__pinNumber") {
@ -378,7 +375,7 @@ private:
// We search ports, rather than in/out declarations as they aren't resolved yet,
// and it's easier to do it now than in V3LinkDot when we'd need to repeat steps.
for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep; portnodep=portnodep->nextp()) {
if (AstPort* portp = portnodep->castPort()) {
if (const AstPort* portp = VN_CAST(portnodep, Port)) {
if (ports.find(portp->name()) == ports.end()
&& ports.find("__pinNumber"+cvtToStr(portp->pinNum())) == ports.end()) {
if (pinStar) {
@ -398,7 +395,7 @@ private:
}
}
}
if (nodep->modp()->castIface()) {
if (VN_IS(nodep->modp(), Iface)) {
// Cell really is the parent's instantiation of an interface, not a normal module
// Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
// in the same way that a child does. Rename though to avoid conflict with cell.
@ -428,7 +425,7 @@ private:
}
}
if (nodep->modp()) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
UINFO(4," Link Cell done: "<<nodep<<endl);
}
@ -438,14 +435,14 @@ private:
virtual void visit(AstNodeMath* nodep) {}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// METHODS
void readModNames() {
// Look at all modules, and store pointers to all module names
for (AstNodeModule* nextp,* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nextp) {
nextp = nodep->nextp()->castNodeModule();
nextp = VN_CAST(nodep->nextp(), NodeModule);
AstNodeModule* foundp = findModuleSym(nodep->name());
if (foundp && foundp != nodep) {
if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) {
@ -470,7 +467,7 @@ public:
m_modp = NULL;
m_libVertexp = NULL;
m_topVertexp = NULL;
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkCellsVisitor() {}
};

View File

@ -106,11 +106,11 @@ public:
private:
// TYPES
typedef multimap<string,VSymEnt*> NameScopeSymMap;
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
typedef set<pair<AstNodeModule*,string> > ImplicitNameSet;
typedef vector<VSymEnt*> IfaceVarSyms;
typedef vector<pair<AstIface*,VSymEnt*> > IfaceModSyms;
typedef std::multimap<string,VSymEnt*> NameScopeSymMap;
typedef std::map<VSymEnt*,VSymEnt*> ScopeAliasMap;
typedef std::set<std::pair<AstNodeModule*,string> > ImplicitNameSet;
typedef std::vector<VSymEnt*> IfaceVarSyms;
typedef std::vector<std::pair<AstIface*,VSymEnt*> > IfaceModSyms;
static LinkDotState* s_errorThisp; // Last self, for error reporting only
@ -128,17 +128,14 @@ private:
public:
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void dump(const string& nameComment="linkdot", bool force=false) {
if (debug()>=6 || force) {
string filename = v3Global.debugFilename(nameComment)+".txt";
const vl_unique_ptr<std::ofstream> logp (V3File::new_ofstream(filename));
if (logp->fail()) v3fatal("Can't write "<<filename);
ostream& os = *logp;
std::ostream& os = *logp;
m_syms.dump(os);
bool first = true;
for (int samn=0; samn<SAMN__MAX; ++samn) {
@ -191,13 +188,13 @@ public:
// METHODS
static string nodeTextType(AstNode* nodep) {
if (nodep->castVar()) return "variable";
else if (nodep->castCell()) return "cell";
else if (nodep->castTask()) return "task";
else if (nodep->castFunc()) return "function";
else if (nodep->castBegin()) return "block";
else if (nodep->castIface()) return "interface";
else if (nodep->castParamTypeDType()) return "parameter type";
if (VN_IS(nodep, Var)) return "variable";
else if (VN_IS(nodep, Cell)) return "cell";
else if (VN_IS(nodep, Task)) return "task";
else if (VN_IS(nodep, Func)) return "function";
else if (VN_IS(nodep, Begin)) return "block";
else if (VN_IS(nodep, Iface)) return "interface";
else if (VN_IS(nodep, ParamTypeDType)) return "parameter type";
else return nodep->prettyTypeName();
}
@ -218,8 +215,8 @@ public:
// Good.
} else if (foundp->imported()) { // From package
// We don't throw VARHIDDEN as if the import is later the symbol table's import wouldn't warn
} else if (nodep->castBegin() && fnodep->castBegin()
&& nodep->castBegin()->generate()) {
} else if (VN_IS(nodep, Begin) && VN_IS(fnodep, Begin)
&& VN_CAST(nodep, Begin)->generate()) {
// Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks
// See t_gen_forif.v for an example.
} else {
@ -373,10 +370,10 @@ public:
}
// Iface for a raw or arrayed iface
static AstIfaceRefDType* ifaceRefFromArray(AstNodeDType* nodep) {
AstIfaceRefDType* ifacerefp = nodep->castIfaceRefDType();
AstIfaceRefDType* ifacerefp = VN_CAST(nodep, IfaceRefDType);
if (!ifacerefp) {
if (AstUnpackArrayDType* arrp = nodep->castUnpackArrayDType()) {
ifacerefp = arrp->subDTypep()->castIfaceRefDType();
if (AstUnpackArrayDType* arrp = VN_CAST(nodep, UnpackArrayDType)) {
ifacerefp = VN_CAST(arrp->subDTypep(), IfaceRefDType);
}
}
return ifacerefp;
@ -384,7 +381,7 @@ public:
void computeIfaceVarSyms() {
for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) {
VSymEnt* varSymp = *it;
AstVar* varp = varSymp ? varSymp->nodep()->castVar() : NULL;
AstVar* varp = varSymp ? VN_CAST(varSymp->nodep(), Var) : NULL;
UINFO(9, " insAllIface se"<<(void*)varSymp<<" "<<varp<<endl);
AstIfaceRefDType* ifacerefp = ifaceRefFromArray(varp->subDTypep());
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
@ -406,7 +403,7 @@ public:
VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
bool ok = false;
if (foundp) {
if (AstModport* modportp = foundp->nodep()->castModport()) {
if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) {
UINFO(4,"Link Modport: "<<modportp<<endl);
ifacerefp->modportp(modportp);
ifOrPortSymp = foundp;
@ -427,8 +424,8 @@ public:
// Track and later insert scope aliases; an interface referenced by a child cell connecting to that interface
// Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
UINFO(9," insertScopeAlias se"<<(void*)lhsp<<" se"<<(void*)rhsp<<endl);
if (rhsp->nodep()->castCell()
&& !rhsp->nodep()->castCell()->modp()->castIface()) {
if (VN_IS(rhsp->nodep(), Cell)
&& !VN_IS(VN_CAST(rhsp->nodep(), Cell)->modp(), Iface)) {
rhsp->nodep()->v3fatalSrc("Got a non-IFACE alias RHS");
}
m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp));
@ -450,7 +447,7 @@ public:
// srcp should be an interface reference pointing to the interface we want to import
lhsp->importFromIface(symsp(), srcp);
// Allow access to objects not permissible to be listed in a modport
if (srcp->nodep()->castModport()) {
if (VN_IS(srcp->nodep(), Modport)) {
lhsp->importFromIface(symsp(), srcp->parentp(), true);
}
}
@ -502,8 +499,8 @@ public:
// then look up (inst name or modname)
if (firstId) {
// Check this module - subcellnames
AstCell* cellp = lookupSymp ? lookupSymp->nodep()->castCell() : NULL; // Replicated below
AstCellInline* inlinep = lookupSymp ? lookupSymp->nodep()->castCellInline() : NULL; // Replicated below
AstCell* cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell) : NULL; // Replicated below
AstCellInline* inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated below
if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
}
@ -515,8 +512,8 @@ public:
bool crossedCell = false; // Crossed a cell boundary
while (lookupSymp) {
lookupSymp = lookupSymp->parentp();
cellp = lookupSymp ? lookupSymp->nodep()->castCell() : NULL; // Replicated above
inlinep = lookupSymp ? lookupSymp->nodep()->castCellInline() : NULL; // Replicated above
cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell) : NULL; // Replicated above
inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated above
if (lookupSymp) {
UINFO(9,"\t\tUp to "<<lookupSymp<<endl);
if (cellp || inlinep) {
@ -528,7 +525,7 @@ public:
}
else if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
if (crossedCell && lookupSymp->nodep()->castVar()) {
if (crossedCell && VN_IS(lookupSymp->nodep(), Var)) {
UINFO(9,"\t\tNot found but matches var name in parent "<<lookupSymp<<endl);
return NULL; // Not found (but happens to be var name in parent)
}
@ -647,13 +644,13 @@ class LinkDotFindVisitor : public AstNVisitor {
virtual void visit(AstNetlist* nodep) {
// Process $unit or other packages
// Not needed - dotted references not allowed from inside packages
//for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
// if (nodep->castPackage()) {}}
//for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
// if (VN_IS(nodep, Package)) {}}
m_statep->insertDUnit(nodep);
// First back iterate, to find all packages. Backward as must do base packages before using packages
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
// The first module in the list is always the top module (sorted before this is called).
// This may not be the module with isTop() set, as early in the steps,
@ -666,7 +663,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_scope = "TOP";
m_curSymp = m_modSymp = m_statep->insertTopCell(topmodp, m_scope);
{
topmodp->accept(*this);
iterate(topmodp);
}
m_scope = "";
m_curSymp = m_modSymp = NULL;
@ -679,7 +676,7 @@ class LinkDotFindVisitor : public AstNVisitor {
UINFO(8," "<<nodep<<endl);
// m_curSymp/m_modSymp maybe NULL for packages and non-top modules
// Packages will be under top after the initial phases, but until then need separate handling
bool standalonePkg = !m_modSymp && (m_statep->forPrearray() && nodep->castPackage());
bool standalonePkg = !m_modSymp && (m_statep->forPrearray() && VN_IS(nodep, Package));
bool doit = (m_modSymp || standalonePkg);
string oldscope = m_scope;
VSymEnt* oldModSymp = m_modSymp;
@ -694,7 +691,7 @@ class LinkDotFindVisitor : public AstNVisitor {
UINFO(4," Link Module: "<<nodep<<endl);
if (nodep->dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?");
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
m_packagep = nodep->castPackage();
m_packagep = VN_CAST(nodep, Package);
if (standalonePkg) {
if (m_packagep->isDollarUnit()) {
m_curSymp = m_modSymp = m_statep->dunitEntp();
@ -712,11 +709,11 @@ class LinkDotFindVisitor : public AstNVisitor {
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
nodep->user2(true);
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->user2(false);
nodep->user4(true);
// Interfaces need another pass when signals are resolved
if (AstIface* ifacep = nodep->castIface()) {
if (AstIface* ifacep = VN_CAST(nodep, Iface)) {
m_statep->insertIfaceModSym(ifacep, m_curSymp);
}
} else { //!doit
@ -741,7 +738,7 @@ class LinkDotFindVisitor : public AstNVisitor {
UINFO(5," CELL under "<<m_scope<<" is "<<nodep<<endl);
// Process XREFs/etc inside pins
if (nodep->recursive() && m_inRecursion) return;
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Recurse in, preserving state
string oldscope = m_scope;
AstBegin* oldbeginp = m_beginp;
@ -769,7 +766,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_beginp = NULL;
m_inRecursion = nodep->recursive();
// We don't report NotFoundModule, as may be a unused module in a generate
if (nodep->modp()) nodep->modp()->accept(*this);
if (nodep->modp()) iterate(nodep->modp());
}
m_scope = oldscope;
m_beginp = oldbeginp;
@ -800,7 +797,7 @@ class LinkDotFindVisitor : public AstNVisitor {
}
virtual void visit(AstDefParam* nodep) {
nodep->user1p(m_curSymp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstGenerate* nodep) {
// Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks
@ -808,7 +805,7 @@ class LinkDotFindVisitor : public AstNVisitor {
bool lastInGen = m_inGenerate;
{
m_inGenerate = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_inGenerate = lastInGen;
}
@ -832,7 +829,7 @@ class LinkDotFindVisitor : public AstNVisitor {
// places such as tasks, where "task ...; begin ... end"
// are common.
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
if (stmtp->castVar()) {
if (VN_IS(stmtp, Var)) {
++m_modBeginNum;
nodep->name("unnamedblk"+cvtToStr(m_modBeginNum));
break;
@ -848,7 +845,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep);
m_curSymp->fallbackp(oldCurSymp);
// Iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_curSymp = oldCurSymp;
m_beginp = oldbegin;
@ -868,8 +865,8 @@ class LinkDotFindVisitor : public AstNVisitor {
// This should probably be done in the Parser instead, as then we could
// just attact normal signal attributes to it.
if (nodep->fvarp()
&& !nodep->fvarp()->castVar()) {
AstNodeDType* dtypep = nodep->fvarp()->castNodeDType();
&& !VN_IS(nodep->fvarp(), Var)) {
AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType);
// If unspecified, function returns one bit; however when we support NEW() it could
// also return the class reference.
if (dtypep) dtypep->unlinkFrBack();
@ -884,7 +881,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, NULL/*packagep*/);
}
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
}
m_curSymp = oldCurSymp;
@ -892,12 +889,12 @@ class LinkDotFindVisitor : public AstNVisitor {
virtual void visit(AstVar* nodep) {
// Var: Remember its name for later resolution
if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Var not under module?");
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!m_statep->forScopeCreation()) {
// Find under either a task or the module's vars
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname?
AstVar* findvarp = foundp ? foundp->nodep()->castVar() : NULL;
AstVar* findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL;
bool ins=false;
if (!foundp) {
ins=true;
@ -913,7 +910,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|| (findvarp->isSignal() && nodep->isIO())) {
findvarp->combineType(nodep);
nodep->fileline()->modifyStateInherit(nodep->fileline());
AstBasicDType* bdtypep = findvarp->childDTypep()->castBasicDType();
AstBasicDType* bdtypep = VN_CAST(findvarp->childDTypep(), BasicDType);
if (bdtypep && bdtypep->implicit()) {
// Then have "input foo" and "real foo" so the dtype comes from the other side.
AstNodeDType* newdtypep = nodep->subDTypep();
@ -976,12 +973,12 @@ class LinkDotFindVisitor : public AstNVisitor {
virtual void visit(AstTypedef* nodep) {
// Remember its name for later resolution
if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module?");
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
}
virtual void visit(AstParamTypeDType* nodep) {
if (!m_curSymp) nodep->v3fatalSrc("Parameter type not under module?");
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
}
virtual void visit(AstCFunc* nodep) {
@ -990,11 +987,11 @@ class LinkDotFindVisitor : public AstNVisitor {
}
virtual void visit(AstEnumItem* nodep) {
// EnumItem: Remember its name for later resolution
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Find under either a task or the module's vars
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname?
AstEnumItem* findvarp = foundp ? foundp->nodep()->castEnumItem() : NULL;
AstEnumItem* findvarp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : NULL;
bool ins=false;
if (!foundp) {
ins=true;
@ -1052,7 +1049,7 @@ class LinkDotFindVisitor : public AstNVisitor {
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -1070,7 +1067,7 @@ public:
m_beginNum = 0;
m_modBeginNum = 0;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkDotFindVisitor() {}
};
@ -1094,10 +1091,10 @@ private:
void pinImplicitExprRecurse(AstNode* nodep) {
// Under a pin, Check interconnect expression for a pin reference or a concat.
// Create implicit variable as needed
if (nodep->castDot()) { // Not creating a simple implied type,
if (VN_IS(nodep, Dot)) { // Not creating a simple implied type,
// and implying something else would just confuse later errors
}
else if (nodep->castVarRef() || nodep->castParseRef()) {
else if (VN_IS(nodep, VarRef) || VN_IS(nodep, ParseRef)) {
// To prevent user errors, we should only do single bit
// implicit vars, however some netlists (MIPS) expect single
// bit implicit wires to get created with range 0:0 etc.
@ -1127,7 +1124,7 @@ private:
nodep->dead(true);
} else {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
}
@ -1140,10 +1137,10 @@ private:
}
}
virtual void visit(AstDefParam* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<<nodep->prettyName()<<"(...etc...))");
VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path());
AstCell* cellp = foundp ? foundp->nodep()->castCell() : NULL;
AstCell* cellp = foundp ? VN_CAST(foundp->nodep(), Cell) : NULL;
if (!cellp) {
nodep->v3error("In defparam, cell "<<nodep->path()<<" never declared");
} else {
@ -1165,7 +1162,7 @@ private:
// Need to set pin numbers after varnames are created
// But before we do the final resolution based on names
VSymEnt* foundp = m_statep->getNodeSym(m_modp)->findIdFlat(nodep->name());
AstVar* refp = foundp ? foundp->nodep()->castVar() : NULL;
AstVar* refp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL;
if (!refp) {
nodep->v3error("Input/output/inout declaration not found for port: "<<nodep->prettyName());
} else if (!refp->isIO() && !refp->isIfaceRef()) {
@ -1184,19 +1181,19 @@ private:
// We used to nodep->allowImplicit() here, but it turns out
// normal "assigns" can also make implicit wires. Yuk.
pinImplicitExprRecurse(nodep->lhsp());
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstAssignAlias* nodep) {
// tran gates need implicit creation
// As VarRefs don't exist in forPrimary, sanity check
if (m_statep->forPrimary()) nodep->v3fatalSrc("Assign aliases unexpected pre-dot");
if (AstVarRef* forrefp = nodep->lhsp()->castVarRef()) {
if (AstVarRef* forrefp = VN_CAST(nodep->lhsp(), VarRef)) {
pinImplicitExprRecurse(forrefp);
}
if (AstVarRef* forrefp = nodep->rhsp()->castVarRef()) {
if (AstVarRef* forrefp = VN_CAST(nodep->rhsp(), VarRef)) {
pinImplicitExprRecurse(forrefp);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstImplicit* nodep) {
// Unsupported gates need implicit creation
@ -1206,7 +1203,7 @@ private:
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -1216,7 +1213,7 @@ public:
m_statep = statep;
m_modp = NULL;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkDotParamVisitor() {}
};
@ -1236,7 +1233,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
// VISITs
virtual void visit(AstNetlist* nodep) {
// Recurse..., backward as must do packages before using packages
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
}
virtual void visit(AstScope* nodep) {
UINFO(8," SCOPE "<<nodep<<endl);
@ -1245,7 +1242,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
// up with the hierarchy created by the CELL names.
m_modSymp = m_statep->getScopeSym(nodep);
m_scopep = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modSymp = NULL;
m_scopep = NULL;
}
@ -1285,19 +1282,19 @@ class LinkDotScopeVisitor : public AstNVisitor {
// Track aliases created by V3Inline; if we get a VARXREF(aliased_from)
// we'll need to replace it with a VARXREF(aliased_to)
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: ");
AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep();
AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep();
AstVarScope* fromVscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
AstVarScope* toVscp = VN_CAST(nodep->rhsp(), VarRef)->varScopep();
if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes");
fromVscp->user2p(toVscp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstAssignVarScope* nodep) {
UINFO(5,"ASSIGNVARSCOPE "<<nodep<<endl);
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\tavs: ");
VSymEnt* rhsSymp;
{
AstVarRef* refp = nodep->rhsp()->castVarRef();
AstVarXRef* xrefp = nodep->rhsp()->castVarXRef();
AstVarRef* refp = VN_CAST(nodep->rhsp(), VarRef);
AstVarXRef* xrefp = VN_CAST(nodep->rhsp(), VarXRef);
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
string inl = (xrefp && xrefp->inlinedDots().size()) ? (xrefp->inlinedDots() + "__DOT__") : "";
VSymEnt* symp = NULL;
@ -1317,8 +1314,8 @@ class LinkDotScopeVisitor : public AstNVisitor {
}
VSymEnt* lhsSymp;
{
AstVarXRef* xrefp = nodep->lhsp()->castVarXRef();
AstVarRef* refp = nodep->lhsp()->castVarRef();
const AstVarXRef* xrefp = VN_CAST(nodep->lhsp(), VarXRef);
const AstVarRef* refp = VN_CAST(nodep->lhsp(), VarRef);
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
string scopename = refp ? refp->varp()->name() : xrefp->dotted()+"."+xrefp->name();
@ -1341,7 +1338,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
virtual void visit(AstNodeMath*) {}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -1352,7 +1349,7 @@ public:
m_scopep = NULL;
m_statep = statep;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkDotScopeVisitor() {}
};
@ -1377,18 +1374,18 @@ class LinkDotIfaceVisitor : public AstNVisitor {
// Create symbol table for the vars
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, NULL);
m_curSymp->fallbackp(oldCurSymp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_curSymp = oldCurSymp;
}
virtual void visit(AstModportFTaskRef* nodep) {
UINFO(5," fif: "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isExport()) nodep->v3error("Unsupported: modport export");
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
if (!symp) {
nodep->v3error("Modport item not found: "<<nodep->prettyName());
} else if (AstNodeFTask* ftaskp = symp->nodep()->castNodeFTask()) {
} else if (AstNodeFTask* ftaskp = VN_CAST(symp->nodep(), NodeFTask)) {
// Make symbol under modport that points at the _interface_'s var, not the modport.
nodep->ftaskp(ftaskp);
VSymEnt* subSymp = m_statep->insertSym(m_curSymp, nodep->name(), ftaskp, NULL/*package*/);
@ -1404,16 +1401,16 @@ class LinkDotIfaceVisitor : public AstNVisitor {
}
virtual void visit(AstModportVarRef* nodep) {
UINFO(5," fiv: "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
if (!symp) {
nodep->v3error("Modport item not found: "<<nodep->prettyName());
} else if (AstVar* varp = symp->nodep()->castVar()) {
} else if (AstVar* varp = VN_CAST(symp->nodep(), Var)) {
// Make symbol under modport that points at the _interface_'s var via the modport.
// (Need modport still to test input/output markings)
nodep->varp(varp);
m_statep->insertSym(m_curSymp, nodep->name(), nodep, NULL/*package*/);
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
} else if (AstVarScope* vscp = VN_CAST(symp->nodep(), VarScope)) {
// Make symbol under modport that points at the _interface_'s var, not the modport.
nodep->varp(vscp->varp());
m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/);
@ -1428,7 +1425,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -1437,7 +1434,7 @@ public:
UINFO(4,__FUNCTION__<<": "<<endl);
m_curSymp = curSymp;
m_statep = statep;
nodep->accept(*this);
iterate(nodep);
}
virtual ~LinkDotIfaceVisitor() {}
};
@ -1499,7 +1496,7 @@ private:
}
string ascii() const {
static const char* const names[] = { "NONE","PACKAGE","SCOPE","FINAL","MEMBER" };
ostringstream sstr;
std::ostringstream sstr;
sstr<<"ds="<<names[m_dotPos];
sstr<<" dse"<<(void*)m_dotSymp;
sstr<<" txt="<<m_dotText;
@ -1534,10 +1531,10 @@ private:
// Return a variable if possible, auto converting a modport to variable
if (!symp) {
return NULL;
} else if (symp->nodep()->castVar()) {
return symp->nodep()->castVar();
} else if (symp->nodep()->castModportVarRef()) {
AstModportVarRef* snodep = symp->nodep()->castModportVarRef();
} else if (VN_IS(symp->nodep(), Var)) {
return VN_CAST(symp->nodep(), Var);
} else if (VN_IS(symp->nodep(), ModportVarRef)) {
AstModportVarRef* snodep = VN_CAST(symp->nodep(), ModportVarRef);
AstVar* varp = snodep->varp();
if (lvalue && snodep->isInput()) {
nodep->v3error("Attempt to drive input-only modport: "<<nodep->prettyName());
@ -1548,8 +1545,8 @@ private:
}
}
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
if (nodep->taskp() && nodep->taskp()->castTask()
&& nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<<nodep->prettyName());
if (nodep->taskp() && VN_IS(nodep->taskp(), Task)
&& VN_IS(nodep, FuncRef)) nodep->v3error("Illegal call of a task as a function: "<<nodep->prettyName());
}
inline void checkNoDot(AstNode* nodep) {
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
@ -1582,7 +1579,7 @@ private:
// VISITs
virtual void visit(AstNetlist* nodep) {
// Recurse..., backward as must do packages before using packages
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
}
virtual void visit(AstTypeTable* nodep) {}
virtual void visit(AstNodeModule* nodep) {
@ -1594,7 +1591,7 @@ private:
m_cellp = NULL;
m_modp = nodep;
m_modportNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
}
@ -1604,7 +1601,7 @@ private:
VSymEnt* oldCurSymp = m_curSymp;
checkNoDot(nodep);
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getScopeSym(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
m_modSymp = oldModSymp;
m_curSymp = oldCurSymp;
@ -1624,7 +1621,7 @@ private:
nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out
}
else {
if (nodep->modp()->castNotFoundModule()) {
if (VN_IS(nodep->modp(), NotFoundModule)) {
// Prevent warnings about missing pin connects
if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree();
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
@ -1636,7 +1633,7 @@ private:
UINFO(4,"(Backto) Link Cell: "<<nodep<<endl);
//if (debug()) { nodep->dumpTree(cout,"linkcell:"); }
//if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); }
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_pinSymp = NULL;
}
}
@ -1647,20 +1644,20 @@ private:
virtual void visit(AstPin* nodep) {
// Pin: Link to submodule's port
checkNoDot(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->modVarp()) {
if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?");
VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name());
const char* whatp = nodep->param() ? "parameter pin" : "pin";
if (!foundp) {
if (nodep->name() == "__paramNumber1" && m_cellp->modp()->castPrimitive()) {
if (nodep->name() == "__paramNumber1" && VN_IS(m_cellp->modp(), Primitive)) {
// Primitive parameter is really a delay we can just ignore
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
return;
}
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyName());
}
else if (AstVar* refp = foundp->nodep()->castVar()) {
else if (AstVar* refp = VN_CAST(foundp->nodep(), Var)) {
if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: "<<nodep->prettyName());
} else {
@ -1668,7 +1665,7 @@ private:
markAndCheckPinDup(nodep, refp, whatp);
}
}
else if (AstParamTypeDType* refp = foundp->nodep()->castParamTypeDType()) {
else if (AstParamTypeDType* refp = VN_CAST(foundp->nodep(), ParamTypeDType)) {
nodep->modPTypep(refp);
markAndCheckPinDup(nodep, refp, whatp);
}
@ -1696,20 +1693,20 @@ private:
m_ds.m_dotPos = DP_SCOPE;
// m_ds.m_dotText communicates the cell prefix between stages
if (nodep->lhsp()->castPackageRef()) {
if (VN_IS(nodep->lhsp(), PackageRef)) {
//if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in dotted reference"); m_ds.m_dotErr=true; }
m_ds.m_dotPos = DP_PACKAGE;
} else {
m_ds.m_dotPos = DP_SCOPE;
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
//if (debug()>=9) nodep->dumpTree("-dot-lho: ");
}
if (m_ds.m_unresolved && (nodep->lhsp()->castCellRef() || nodep->lhsp()->castCellArrayRef())) {
if (m_ds.m_unresolved && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) {
m_ds.m_unlinkedScope = nodep->lhsp();
}
if (!m_ds.m_dotErr) { // Once something wrong, give up
if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
//if (debug()>=9) nodep->dumpTree("-dot-rho: ");
}
if (start) {
@ -1770,8 +1767,8 @@ private:
expectWhat = "scope/variable";
allowScope = true;
allowVar = true;
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
packagep = m_ds.m_dotp->lhsp()->castPackageRef()->packagep();
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
packagep = VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep();
if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
m_ds.m_dotSymp = m_statep->getNodeSym(packagep);
m_ds.m_dotPos = DP_SCOPE;
@ -1803,9 +1800,9 @@ private:
// What fell out?
bool ok = false;
if (!foundp) {
} else if (foundp->nodep()->castCell()
|| foundp->nodep()->castBegin()
|| foundp->nodep()->castModule()) { // if top
} else if (VN_IS(foundp->nodep(), Cell)
|| VN_IS(foundp->nodep(), Begin)
|| VN_IS(foundp->nodep(), Module)) { // if top
if (allowScope) {
ok = true;
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
@ -1813,17 +1810,17 @@ private:
m_ds.m_dotPos = DP_SCOPE;
// Upper AstDot visitor will handle it from here
}
else if (foundp->nodep()->castCell()
else if (VN_IS(foundp->nodep(), Cell)
&& allowVar && m_cellp) {
AstCell* cellp = foundp->nodep()->castCell();
if (cellp->modp()->castIface()) {
AstCell* cellp = VN_CAST(foundp->nodep(), Cell);
if (VN_IS(cellp->modp(), Iface)) {
// Interfaces can be referenced like a variable for interconnect
VSymEnt* cellEntp = m_statep->getNodeSym(cellp);
if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
string findName = nodep->name()+"__Viftop";
VSymEnt* ifaceSymp = parentEntp->findIdFallback(findName);
AstVar* ifaceRefVarp = ifaceSymp ? ifaceSymp->nodep()->castVar() : NULL;
AstVar* ifaceRefVarp = ifaceSymp ? VN_CAST(ifaceSymp->nodep(), Var) : NULL;
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<<findName);
//
ok = true;
@ -1833,7 +1830,7 @@ private:
UINFO(9," cell -> iface varref "<<foundp->nodep()<<endl);
AstNode* newp = new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, false);
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
} else if (cellp->modp()->castNotFoundModule()) {
} else if (VN_IS(cellp->modp(), NotFoundModule)) {
cellp->v3error("Cannot find file containing interface: " << AstNode::prettyName(cellp->modp()->name()));
}
}
@ -1859,7 +1856,7 @@ private:
refp->varp(varp);
m_ds.m_dotText = "";
if (m_ds.m_unresolved && m_ds.m_unlinkedScope) {
newp = new AstUnlinkedRef(nodep->fileline(), refp->castVarXRef(),
newp = new AstUnlinkedRef(nodep->fileline(), VN_CAST(refp, VarXRef),
refp->name(), m_ds.m_unlinkedScope->unlinkFrBack());
m_ds.m_unlinkedScope = NULL;
m_ds.m_unresolved = false;
@ -1877,23 +1874,23 @@ private:
ok = true;
}
}
else if (AstModport* modportp = foundp->nodep()->castModport()) {
else if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) {
// A scope reference into an interface's modport (not necessarily at a pin connection)
UINFO(9,"cell-ref-to-modport "<<m_ds.m_dotText<<" "<<nodep<<endl);
UINFO(9,"dotSymp "<<m_ds.m_dotSymp<<" "<<m_ds.m_dotSymp->nodep()<<endl);
// Iface was the previously dotted component
if (!m_ds.m_dotSymp
|| !m_ds.m_dotSymp->nodep()->castCell()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|| !VN_IS(m_ds.m_dotSymp->nodep(), Cell)
|| !VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
nodep->v3error("Modport not referenced as <interface>."<<modportp->prettyName());
} else if (!m_ds.m_dotSymp->nodep()->castCell()->modp()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
} else if (!VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
nodep->v3error("Modport not referenced from underneath an interface: "<<modportp->prettyName());
} else {
AstCell* cellp = m_ds.m_dotSymp->nodep()->castCell();
AstCell* cellp = VN_CAST(m_ds.m_dotSymp->nodep(), Cell);
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
AstIface* ifacep = cellp->modp()->castIface();
AstIface* ifacep = VN_CAST(cellp->modp(), Iface);
//string cellName = m_ds.m_dotText; // Use cellp->name
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
m_ds.m_dotSymp = m_statep->getNodeSym(modportp);
@ -1904,7 +1901,7 @@ private:
nodep->replaceWith(refp); pushDeletep(nodep); VL_DANGLING(nodep);
}
}
else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) {
else if (AstEnumItem* valuep = VN_CAST(foundp->nodep(), EnumItem)) {
if (allowVar) {
AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep());
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
@ -1915,7 +1912,7 @@ private:
//
if (!ok) {
// Cells/interfaces can't be implicit
bool isCell = foundp ? foundp->nodep()->castCell() != NULL : false;
bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText=="" && !isCell);
bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
if (err) {
@ -1952,7 +1949,7 @@ private:
// ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find
// errors here now that we have a VarRef.
// No checkNoDot; created and iterated from a parseRef
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->varp()) {
UINFO(9," linkVarRef se"<<(void*)m_curSymp<<" n="<<nodep<<endl);
if (!m_curSymp) nodep->v3fatalSrc("NULL lookup symbol table");
@ -2001,7 +1998,7 @@ private:
// V3Inst may have expanded arrays of interfaces to AstVarXRef's even though they are in the same module
// detect this and convert to normal VarRefs
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
if (nodep->dtypep()->castIfaceRefDType()) {
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), nodep->varp(), nodep->lvalue());
nodep->replaceWith(newrefp);
nodep->deleteTree(); VL_DANGLING(nodep);
@ -2010,7 +2007,7 @@ private:
} else {
string baddot;
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
AstVarScope* vscp = foundp ? foundp->nodep()->castVarScope() : NULL;
AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : NULL;
if (!vscp) {
nodep->v3error("Can't find varpin scope of '"<<baddot
<<"' in dotted signal: "<<nodep->dotted()+"."+nodep->prettyName());
@ -2018,7 +2015,7 @@ private:
} else {
while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal
UINFO(7," Resolved pre-alias "<<vscp<<endl); // Also prints taskp
vscp = vscp->user2p()->castVarScope();
vscp = VN_CAST(vscp->user2p(), VarScope);
}
// Convert the VarXRef to a VarRef, so we don't need later optimizations to deal with VarXRef.
nodep->varp(vscp->varp());
@ -2034,20 +2031,20 @@ private:
}
virtual void visit(AstEnumItemRef* nodep) {
// EnumItemRef may be under a dot. Should already be resolved.
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstMethodSel* nodep) {
// Created here so should already be resolved.
DotStates lastStates = m_ds;
{
m_ds.init(m_curSymp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_ds = lastStates;
}
virtual void visit(AstVar* nodep) {
checkNoDot(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_statep->forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) {
nodep->v3error("Input/output/inout does not appear in port list: "<<nodep->prettyName());
}
@ -2056,9 +2053,9 @@ private:
if (nodep->user3SetOnce()) return;
UINFO(8," "<<nodep<<endl);
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep());
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
m_ds.m_dotPos = DP_SCOPE;
m_ds.m_dotp = NULL;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
@ -2113,7 +2110,7 @@ private:
dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL
}
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
AstNodeFTask* taskp = foundp ? foundp->nodep()->castNodeFTask() : NULL; // Maybe NULL
AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL; // Maybe NULL
if (taskp) {
nodep->taskp(taskp);
nodep->packagep(foundp->packagep());
@ -2138,13 +2135,13 @@ private:
DotStates lastStates = m_ds;
{
m_ds.init(m_curSymp);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_ds = lastStates;
}
virtual void visit(AstSelBit* nodep) {
if (nodep->user3SetOnce()) return;
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
UINFO(9," deferring until after a V3Param pass: "<<nodep<<endl);
m_ds.m_dotText += "__BRA__??__KET__";
@ -2152,12 +2149,12 @@ private:
// And pass up m_ds.m_dotText
}
// Pass dot state down to fromp()
nodep->fromp()->iterateAndNext(*this);
iterateAndNextNull(nodep->fromp());
DotStates lastStates = m_ds;
{
m_ds.init(m_curSymp);
nodep->bitp()->iterateAndNext(*this);
nodep->attrp()->iterateAndNext(*this);
iterateAndNextNull(nodep->bitp());
iterateAndNextNull(nodep->attrp());
}
m_ds = lastStates;
if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) {
@ -2174,19 +2171,19 @@ private:
m_ds.m_dotErr = true;
return;
}
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
DotStates lastStates = m_ds;
{
m_ds.init(m_curSymp);
nodep->rhsp()->iterateAndNext(*this);
nodep->thsp()->iterateAndNext(*this);
nodep->attrp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
iterateAndNextNull(nodep->thsp());
iterateAndNextNull(nodep->attrp());
}
m_ds = lastStates;
}
virtual void visit(AstMemberSel* nodep) {
// checkNoDot not appropriate, can be under a dot
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstBegin* nodep) {
UINFO(5," "<<nodep<<endl);
@ -2195,7 +2192,7 @@ private:
{
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
UINFO(5," cur=se"<<(void*)m_curSymp<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
UINFO(5," cur=se"<<(void*)m_curSymp<<endl);
@ -2207,7 +2204,7 @@ private:
{
m_ftaskp = nodep;
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
m_ftaskp = NULL;
@ -2216,9 +2213,9 @@ private:
// Resolve its reference
if (nodep->user3SetOnce()) return;
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep());
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
m_ds.m_dotPos = DP_SCOPE;
m_ds.m_dotp = NULL;
} else {
@ -2231,11 +2228,11 @@ private:
} else {
foundp = m_curSymp->findIdFallback(nodep->name());
}
if (AstTypedef* defp = foundp ? foundp->nodep()->castTypedef() : NULL) {
if (AstTypedef* defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : NULL) {
nodep->refDTypep(defp->subDTypep());
nodep->packagep(foundp->packagep());
}
else if (AstParamTypeDType* defp = foundp ? foundp->nodep()->castParamTypeDType() : NULL) {
else if (AstParamTypeDType* defp = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : NULL) {
nodep->refDTypep(defp);
nodep->packagep(foundp->packagep());
}
@ -2243,14 +2240,14 @@ private:
nodep->v3error("Can't find typedef: "<<nodep->prettyName());
}
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstDpiExport* nodep) {
// AstDpiExport: Make sure the function referenced exists, then dump it
nodep->iterateChildren(*this);
iterateChildren(nodep);
checkNoDot(nodep);
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
AstNodeFTask* taskp = foundp ? foundp->nodep()->castNodeFTask() : NULL;
AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL;
if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "<<nodep->prettyName()); }
else if (taskp->dpiExport()) {
nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<<nodep->prettyName());
@ -2277,7 +2274,7 @@ private:
}
virtual void visit(AstCellRef* nodep) {
UINFO(5," AstCellRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstCellArrayRef* nodep) {
UINFO(5," AstCellArrayRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
@ -2291,7 +2288,7 @@ private:
virtual void visit(AstNode* nodep) {
// Default: Just iterate
checkNoDot(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -2306,7 +2303,7 @@ public:
m_ftaskp = NULL;
m_modportNum = 0;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkDotResolveVisitor() {}
};

View File

@ -43,7 +43,7 @@
class LinkJumpVisitor : public AstNVisitor {
private:
// TYPES
typedef vector<AstBegin*> BeginStack;
typedef std::vector<AstBegin*> BeginStack;
// STATE
AstNodeModule* m_modp; // Current module
@ -54,25 +54,21 @@ private:
BeginStack m_beginStack; // All begin blocks above current node
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
// Put label under given node, and if WHILE optionally at end of iteration
UINFO(4,"Create label for "<<nodep<<endl);
if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done
if (VN_IS(nodep, JumpLabel)) return VN_CAST(nodep, JumpLabel); // Done
AstNode* underp = NULL;
bool under_and_next = true;
if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp();
else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp();
else if (nodep->castWhile()) {
if (VN_IS(nodep, Begin)) underp = VN_CAST(nodep, Begin)->stmtsp();
else if (VN_IS(nodep, NodeFTask)) underp = VN_CAST(nodep, NodeFTask)->stmtsp();
else if (VN_IS(nodep, While)) {
if (endOfIter) {
// Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip
underp = nodep->castWhile()->bodysp();
underp = VN_CAST(nodep, While)->bodysp();
} else {
underp = nodep; under_and_next=false; // IE we skip the entire while
}
@ -84,14 +80,14 @@ private:
// Skip over variables as we'll just move them in a momement
// Also this would otherwise prevent us from using a label twice
// see t_func_return test.
while (underp && underp->castVar()) underp = underp->nextp();
while (underp && VN_IS(underp, Var)) underp = underp->nextp();
if (underp) UINFO(5," Underpoint is "<<underp<<endl);
if (!underp) {
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
return NULL;
} else if (underp->castJumpLabel()) {
return underp->castJumpLabel();
} else if (VN_IS(underp, JumpLabel)) {
return VN_CAST(underp, JumpLabel);
} else { // Move underp stuff to be under a new label
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
@ -104,7 +100,7 @@ private:
// Keep any AstVars under the function not under the new JumpLabel
for (AstNode* nextp, *varp=underp; varp; varp = nextp) {
nextp = varp->nextp();
if (varp->castVar()) {
if (VN_IS(varp, Var)) {
labelp->addPrev(varp->unlinkFrBack());
}
}
@ -117,18 +113,18 @@ private:
if (nodep->dead()) return;
m_modp = nodep;
m_repeatNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstNodeFTask* nodep) {
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
}
virtual void visit(AstBegin* nodep) {
UINFO(8," "<<nodep<<endl);
m_beginStack.push_back(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_beginStack.pop_back();
}
virtual void visit(AstRepeat* nodep) {
@ -167,17 +163,17 @@ private:
bool lastInc = m_loopInc;
m_loopp = nodep;
m_loopInc = false;
nodep->precondsp()->iterateAndNext(*this);
nodep->condp()->iterateAndNext(*this);
nodep->bodysp()->iterateAndNext(*this);
iterateAndNextNull(nodep->precondsp());
iterateAndNextNull(nodep->condp());
iterateAndNextNull(nodep->bodysp());
m_loopInc = true;
nodep->incsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->incsp());
m_loopInc = lastInc;
m_loopp = lastLoopp;
}
virtual void visit(AstReturn* nodep) {
nodep->iterateChildren(*this);
AstFunc* funcp = m_ftaskp->castFunc();
iterateChildren(nodep);
AstFunc* funcp = VN_CAST(m_ftaskp, Func);
if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); }
else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); }
else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); }
@ -185,7 +181,7 @@ private:
if (funcp && nodep->lhsp()) {
// Set output variable to return value
nodep->addPrev(new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), funcp->fvarp()->castVar(), true),
new AstVarRef(nodep->fileline(), VN_CAST(funcp->fvarp(), Var), true),
nodep->lhsp()->unlinkFrBackWithNext()));
}
// Jump to the end of the function call
@ -195,7 +191,7 @@ private:
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstBreak* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); }
else {
// Jump to the end of the loop
@ -205,7 +201,7 @@ private:
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstContinue* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); }
else {
// Jump to the end of this iteration
@ -217,7 +213,7 @@ private:
}
virtual void visit(AstDisable* nodep) {
UINFO(8," DISABLE "<<nodep<<endl);
nodep->iterateChildren(*this);
iterateChildren(nodep);
AstBegin* beginp = NULL;
for (BeginStack::reverse_iterator it = m_beginStack.rbegin(); it != m_beginStack.rend(); ++it) {
UINFO(9," UNDERBLK "<<*it<<endl);
@ -242,7 +238,7 @@ private:
virtual void visit(AstConst* nodep) {}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
@ -252,7 +248,7 @@ public:
m_loopp = NULL;
m_loopInc = false;
m_repeatNum = 0;
nodep->accept(*this);
iterate(nodep);
}
virtual ~LinkJumpVisitor() {}
};

View File

@ -47,11 +47,7 @@ private:
AstNodeFTask* m_ftaskp; // Function or task we're inside
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITs
// Result handing
@ -67,7 +63,7 @@ private:
}
}
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
// Nodes that start propagating down lvalues
@ -76,19 +72,19 @@ private:
// When the varref's were created, we didn't know the I/O state
// Now that we do, and it's from a output, we know it's a lvalue
m_setRefLvalue = true;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_setRefLvalue = false;
} else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstNodeAssign* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
m_setRefLvalue = false;
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -96,10 +92,10 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
m_setRefLvalue = false;
nodep->filenamep()->iterateAndNext(*this);
nodep->modep()->iterateAndNext(*this);
iterateAndNextNull(nodep->filenamep());
iterateAndNextNull(nodep->modep());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -107,7 +103,7 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -115,7 +111,7 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -123,7 +119,7 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -131,8 +127,8 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
nodep->strgp()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
iterateAndNextNull(nodep->strgp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -140,8 +136,8 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->filep()->iterateAndNext(*this);
nodep->exprsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->filep());
iterateAndNextNull(nodep->exprsp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -149,25 +145,25 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->exprsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->exprsp());
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstSysIgnore* nodep) {
// Can't know if lvalue or not; presume so as stricter
bool last_setRefLvalue = m_setRefLvalue;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstReadMem* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->memp()->iterateAndNext(*this);
iterateAndNextNull(nodep->memp());
m_setRefLvalue = false;
nodep->filenamep()->iterateAndNext(*this);
nodep->lsbp()->iterateAndNext(*this);
nodep->msbp()->iterateAndNext(*this);
iterateAndNextNull(nodep->filenamep());
iterateAndNextNull(nodep->lsbp());
iterateAndNextNull(nodep->msbp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -175,9 +171,9 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = false;
nodep->searchp()->iterateAndNext(*this);
iterateAndNextNull(nodep->searchp());
m_setRefLvalue = true;
nodep->outp()->iterateAndNext(*this);
iterateAndNextNull(nodep->outp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -185,9 +181,9 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
m_setRefLvalue = false;
nodep->fmtp()->iterateAndNext(*this);
iterateAndNextNull(nodep->fmtp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -196,20 +192,20 @@ private:
virtual void visit(AstSel* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
// Only set lvalues on the from
m_setRefLvalue = false;
nodep->rhsp()->iterateAndNext(*this);
nodep->thsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
iterateAndNextNull(nodep->thsp());
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstNodeSel* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{ // Only set lvalues on the from
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
m_setRefLvalue = false;
nodep->rhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
}
m_setRefLvalue = last_setRefLvalue;
}
@ -217,23 +213,23 @@ private:
bool last_setRefLvalue = m_setRefLvalue;
{ // selp is not an lvalue
m_setRefLvalue = false;
nodep->selp()->iterateAndNext(*this);
iterateAndNextNull(nodep->selp());
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstNodePreSel* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{ // Only set lvalues on the from
nodep->lhsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->lhsp());
m_setRefLvalue = false;
nodep->rhsp()->iterateAndNext(*this);
nodep->thsp()->iterateAndNext(*this);
iterateAndNextNull(nodep->rhsp());
iterateAndNextNull(nodep->thsp());
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstNodeFTask* nodep) {
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
}
virtual void visit(AstNodeFTaskRef* nodep) {
@ -242,13 +238,13 @@ private:
// We'll deal with mismatching pins later
if (!taskp) return;
for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
if (const AstVar* portp = VN_CAST(stmtp, Var)) {
if (portp->isIO()) {
if (portp->isInput()) {
pinp->iterate(*this);
iterate(pinp);
} else { // Output or Inout
m_setRefLvalue = true;
pinp->iterate(*this);
iterate(pinp);
m_setRefLvalue = false;
}
// Advance pin
@ -260,7 +256,7 @@ private:
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -268,7 +264,7 @@ public:
LinkLValueVisitor(AstNode* nodep, bool start) {
m_setRefLvalue = start;
m_ftaskp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~LinkLValueVisitor() {}
};

View File

@ -52,11 +52,11 @@ void V3LinkLevel::modSortByLevel() {
// level() was computed for us in V3LinkCells
typedef vector<AstNodeModule*> ModVec;
typedef std::vector<AstNodeModule*> ModVec;
ModVec vec;
AstNodeModule* topp = NULL;
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
if (nodep->level()<=2) {
if (topp) {
nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: "
@ -108,7 +108,7 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) {
void V3LinkLevel::wrapTopCell(AstNetlist* netlistp) {
AstNodeModule* newmodp = netlistp->modulesp();
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
AstNodeModule* oldmodp = newmodp->nextp()->castNodeModule();
AstNodeModule* oldmodp = VN_CAST(newmodp->nextp(), NodeModule);
if (!oldmodp) netlistp->v3fatalSrc("No module found to process");
// Add instance
@ -121,7 +121,7 @@ void V3LinkLevel::wrapTopCell(AstNetlist* netlistp) {
// Add pins
for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* oldvarp=subnodep->castVar()) {
if (AstVar* oldvarp=VN_CAST(subnodep, Var)) {
UINFO(8,"VARWRAP "<<oldvarp<<endl);
if (oldvarp->isIO()) {
AstVar* varp = oldvarp->cloneTree(false);
@ -154,8 +154,8 @@ void V3LinkLevel::wrapTopPackages(AstNetlist* netlistp) {
// This way all later SCOPE based optimizations can ignore packages
AstNodeModule* newmodp = netlistp->modulesp();
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
for (AstNodeModule* modp = netlistp->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
if (modp->castPackage()) {
for (AstNodeModule* modp = netlistp->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
if (VN_IS(modp, Package)) {
AstCell* cellp = new AstCell(modp->fileline(),
// Could add __03a__03a="::" to prevent conflict
// with module names/"v"

View File

@ -49,8 +49,8 @@ private:
AstUser2InUse m_inuser2;
// TYPES
typedef map <pair<void*,string>,AstTypedef*> ImplTypedefMap;
typedef set <FileLine*> FileLineSet;
typedef std::map<std::pair<void*,string>,AstTypedef*> ImplTypedefMap;
typedef std::set<FileLine*> FileLineSet;
// STATE
AstVar* m_varp; // Variable we're under
@ -65,11 +65,7 @@ private:
AstNodeDType* m_dtypep; // Current data type
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void cleanFileline(AstNode* nodep) {
if (!nodep->user2SetOnce()) { // Process once
@ -91,7 +87,7 @@ private:
if (!nodep->user1SetOnce()) { // Process only once.
cleanFileline(nodep);
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
}
}
@ -101,7 +97,7 @@ private:
UINFO(5," "<<nodep<<endl);
AstNodeModule* upperValueModp = m_valueModp;
m_valueModp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_valueModp = upperValueModp;
}
}
@ -110,17 +106,17 @@ private:
cleanFileline(nodep);
AstNodeDType* upperDtypep = m_dtypep;
m_dtypep = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_dtypep = upperDtypep;
}
}
virtual void visit(AstEnumItem* nodep) {
// Expand ranges
cleanFileline(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->rangep()) {
if (!nodep->rangep()->msbp()->castConst()
|| !nodep->rangep()->lsbp()->castConst()) nodep->v3error("Enum ranges must be integral, per spec");
if (!VN_IS(nodep->rangep()->msbp(), Const)
|| !VN_IS(nodep->rangep()->lsbp(), Const)) nodep->v3error("Enum ranges must be integral, per spec");
int msb = nodep->rangep()->msbConst();
int lsb = nodep->rangep()->lsbConst();
int increment = (msb > lsb) ? -1 : 1;
@ -142,9 +138,9 @@ private:
virtual void visit(AstVar* nodep) {
cleanFileline(nodep);
if (nodep->subDTypep()->castParseTypeDType()) {
if (VN_IS(nodep->subDTypep(), ParseTypeDType)) {
// It's a parameter type. Use a different node type for this.
AstNodeDType* dtypep = nodep->valuep()->castNodeDType();
AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType);
if (!dtypep) {
nodep->v3error("Parameter type's initial value isn't a type: "<<nodep->prettyName());
nodep->unlinkFrBack();
@ -166,7 +162,7 @@ private:
nodep->trace(false);
}
m_varp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_varp = NULL;
// temporaries under an always aren't expected to be blocking
if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
@ -202,9 +198,9 @@ private:
virtual void visit(AstAttrOf* nodep) {
cleanFileline(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
AstTypedef* typep = nodep->backp()->castTypedef();
AstTypedef* typep = VN_CAST(nodep->backp(), Typedef);
if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef");
typep->attrPublic(true);
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
@ -270,7 +266,7 @@ private:
// AlwaysPublic was attached under a var, but it's a statement that should be
// at the same level as the var
cleanFileline(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_varp) {
nodep->unlinkFrBack();
m_varp->addNext(nodep);
@ -296,13 +292,13 @@ private:
// AstVar, AstTypedef, AstNodeFTask are common containers
AstNode* backp = nodep->backp();
for (; backp; backp=backp->backp()) {
if (backp->castVar()) break;
else if (backp->castTypedef()) break;
else if (backp->castNodeFTask()) break;
if (VN_IS(backp, Var)) break;
else if (VN_IS(backp, Typedef)) break;
else if (VN_IS(backp, NodeFTask)) break;
}
if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type");
AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack();
if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing
if (VN_IS(backp, Typedef)) { // A typedef doesn't need us to make yet another level of typedefing
// For typedefs just remove the AstRefDType level of abstraction
nodep->replaceWith(dtypep);
nodep->deleteTree(); VL_DANGLING(nodep);
@ -383,7 +379,7 @@ private:
//
m_modp = nodep;
m_valueModp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
m_valueModp = NULL;
}
@ -393,7 +389,7 @@ private:
//
AstNodeModule* upperValueModp = m_valueModp;
m_valueModp = NULL;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_valueModp = upperValueModp;
}
virtual void visit(AstInitial* nodep) {
@ -414,7 +410,7 @@ private:
virtual void visit(AstNode* nodep) {
// Default: Just iterate
cleanFileline(nodep);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -428,7 +424,7 @@ public:
m_inGenerate = false;
m_needStart = false;
m_valueModp = NULL;
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkParseVisitor() {}
};

View File

@ -60,11 +60,7 @@ private:
int m_senitemCvtNum; // Temporary signal counter
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITs
// TODO: Most of these visitors are here for historical reasons.
@ -76,11 +72,11 @@ private:
if (nodep->dead()) return;
m_modp = nodep;
m_senitemCvtNum = 0;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstInitial* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Initial assignments under function/tasks can just be simple assignments without the initial
if (m_ftaskp) {
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep);
@ -89,12 +85,12 @@ private:
virtual void visit(AstVAssert* nodep) {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
m_assertp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_assertp = NULL;
}
virtual void visit(AstVar* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (m_ftaskp) nodep->funcLocal(true);
if (nodep->isSigModPublic()) {
nodep->sigModPublic(false); // We're done with this attribute
@ -107,21 +103,21 @@ private:
if (nodep->varp()) {
nodep->varp()->usedParam(true);
}
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNodeFTask* nodep) {
// NodeTask: Remember its name for later resolution
// Remember the existing symbol table scope
m_ftaskp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_ftaskp = NULL;
if (nodep->dpiExport()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstNodeFTaskRef* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
@ -129,15 +125,15 @@ private:
virtual void visit(AstSenItem* nodep) {
// Remove bit selects, and bark if it's not a simple variable
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (nodep->isClocked()) {
// If it's not a simple variable wrap in a temporary
// This is a bit unfortunate as we haven't done width resolution
// and any width errors will look a bit odd, but it works.
AstNode* sensp = nodep->sensp();
if (sensp
&& !sensp->castNodeVarRef()
&& !sensp->castConst()) {
&& !VN_IS(sensp, NodeVarRef)
&& !VN_IS(sensp, Const)) {
// Make a new temp wire
string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum);
AstVar* newvarp = new AstVar (sensp->fileline(), AstVarType::MODULETEMP, newvarname,
@ -145,11 +141,11 @@ private:
// We can't just add under the module, because we may be inside a generate, begin, etc.
// We know a SenItem should be under a SenTree/Always etc, we we'll just hunt upwards
AstNode* addwherep = nodep; // Add to this element's next
while (addwherep->castSenItem()
|| addwherep->castSenTree()) {
while (VN_IS(addwherep, SenItem)
|| VN_IS(addwherep, SenTree)) {
addwherep = addwherep->backp();
}
if (!addwherep->castAlways()) { // Assertion perhaps?
if (!VN_IS(addwherep, Always)) { // Assertion perhaps?
sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block");
addwherep = m_modp;
}
@ -166,26 +162,26 @@ private:
bool did=1;
while (did) {
did=0;
if (AstNodeSel* selp = nodep->sensp()->castNodeSel()) {
if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did=1;
}
// NodeSel doesn't include AstSel....
if (AstSel* selp = nodep->sensp()->castSel()) {
if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did=1;
}
if (AstNodePreSel* selp = nodep->sensp()->castNodePreSel()) {
if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) {
AstNode* fromp = selp->lhsp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did=1;
}
}
}
if (!nodep->sensp()->castNodeVarRef()
&& !nodep->sensp()->castEnumItemRef() // V3Const will cleanup
if (!VN_IS(nodep->sensp(), NodeVarRef)
&& !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup
&& !nodep->isIllegal()) {
if (debug()) nodep->dumpTree(cout,"-tree: ");
nodep->v3error("Unsupported: Complex statement in sensitivity list");
@ -197,22 +193,22 @@ private:
virtual void visit(AstNodePreSel* nodep) {
if (!nodep->attrp()) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Constification may change the fromp() to a constant, which will lose the
// variable we're extracting from (to determine MSB/LSB/endianness/etc.)
// So we replicate it in another node
// Note that V3Param knows not to replace AstVarRef's under AstAttrOf's
AstNode* basefromp = AstArraySel::baseFromp(nodep);
if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { // Maybe varxref - so need to clone
if (AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
varrefp->cloneTree(false)));
} else if (AstUnlinkedRef* uvxrp = basefromp->castUnlinkedRef()) { // Maybe unlinked - so need to clone
} else if (AstUnlinkedRef* uvxrp = VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
uvxrp->cloneTree(false)));
} else if (AstMemberSel* fromp = basefromp->castMemberSel()) {
} else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) {
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE,
fromp->cloneTree(false)));
} else if (AstEnumItemRef* fromp = basefromp->castEnumItemRef()) {
} else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) {
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE,
fromp->cloneTree(false)));
} else {
@ -225,7 +221,7 @@ private:
virtual void visit(AstCaseItem* nodep) {
// Move default caseItems to the bottom of the list
// That saves us from having to search each case list twice, for non-defaults and defaults
nodep->iterateChildren(*this);
iterateChildren(nodep);
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
nodep->user2(true);
AstNode* nextp = nodep->nextp();
@ -252,7 +248,7 @@ private:
}
}
else {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
@ -307,7 +303,7 @@ private:
skipCount--;
continue;
}
AstConst *constp = argp->castConst();
AstConst *constp = VN_CAST(argp, Const);
bool isFromString = (constp) ? constp->num().isFromString() : false;
if (isFromString) {
int numchars = argp->dtypep()->width()/8;
@ -356,33 +352,33 @@ private:
}
virtual void visit(AstFOpen* nodep) {
nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFClose* nodep) {
nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFEof* nodep) {
nodep->iterateChildren(*this);
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFScanF* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
}
virtual void visit(AstSScanF* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
}
virtual void visit(AstSFormatF* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
// Cleanup old-school displays without format arguments
if (!nodep->hasFormat()) {
if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format");
if (nodep->exprsp()->castConst()
&& nodep->exprsp()->castConst()->num().isFromString()) {
AstConst* fmtp = nodep->exprsp()->unlinkFrBack()->castConst();
if (VN_IS(nodep->exprsp(), Const)
&& VN_CAST(nodep->exprsp(), Const)->num().isFromString()) {
AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const);
nodep->text(fmtp->num().toString());
pushDeletep(fmtp); VL_DANGLING(fmtp);
}
@ -390,13 +386,14 @@ private:
}
string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
nodep->text(newFormat);
if ((nodep->backp()->castDisplay() && nodep->backp()->castDisplay()->displayType().needScopeTracking())
if ((VN_IS(nodep->backp(), Display)
&& VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking())
|| nodep->formatScopeTracking()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstDisplay* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstUdpTable* nodep) {
@ -408,7 +405,7 @@ private:
// Massive hack, just tie off all outputs so our analysis can proceed
AstVar* varoutp = NULL;
for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* varp = stmtp->castVar()) {
if (AstVar* varp = VN_CAST(stmtp, Var)) {
if (varp->isInput()) {
} else if (varp->isOutput()) {
if (varoutp) { varp->v3error("Multiple outputs not allowed in udp modules"); }
@ -429,22 +426,22 @@ private:
virtual void visit(AstScCtor* nodep) {
// Constructor info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstScDtor* nodep) {
// Destructor info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstScInt* nodep) {
// Special class info means the module must remain public
m_modp->modPublic(true);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
@ -455,7 +452,7 @@ public:
m_assertp = NULL;
m_senitemCvtNum = 0;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkResolveVisitor() {}
};
@ -471,20 +468,16 @@ private:
AstNodeModule* m_modp; // Current module
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// VISITs
virtual void visit(AstNetlist* nodep) {
// Iterate modules backwards, in bottom-up order.
nodep->iterateChildrenBackwards(*this);
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstCell* nodep) {
@ -497,14 +490,14 @@ private:
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit LinkBotupVisitor(AstNetlist* rootp) {
m_modp = NULL;
//
rootp->accept(*this);
iterate(rootp);
}
virtual ~LinkBotupVisitor() {}
};

View File

@ -50,11 +50,7 @@ protected:
// AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
// TYPES
union VarFlags {
@ -90,12 +86,12 @@ private:
}
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit LocalizeDehierVisitor(AstNetlist* nodep) {
nodep->accept(*this);
iterate(nodep);
}
virtual ~LocalizeDehierVisitor() {}
};
@ -114,7 +110,7 @@ private:
// STATE
V3Double0 m_statLocVars; // Statistic tracking
AstCFunc* m_cfuncp; // Current active function
vector<AstVar*> m_varps; // List of variables to consider for deletion
std::vector<AstVar*> m_varps; // List of variables to consider for deletion
// METHODS
void clearOptimizable(AstVar* nodep, const char* reason) {
@ -130,7 +126,7 @@ private:
flags.setNodeFlags(nodep);
}
void moveVars() {
for (vector<AstVar*>::iterator it = m_varps.begin(); it != m_varps.end(); ++it) {
for (std::vector<AstVar*>::iterator it = m_varps.begin(); it != m_varps.end(); ++it) {
AstVar* nodep = *it;
if (nodep->valuep()) clearOptimizable(nodep,"HasInitValue");
if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep,"NoStdAssign");
@ -142,7 +138,7 @@ private:
// We don't need to test for tracing; it would be in the tracefunc if it was needed
UINFO(4," ModVar->BlkVar "<<nodep<<endl);
++m_statLocVars;
AstCFunc* newfuncp = nodep->user1p()->castCFunc();
AstCFunc* newfuncp = VN_CAST(nodep->user1p(), CFunc);
nodep->unlinkFrBack();
newfuncp->addInitsp(nodep);
// Done
@ -157,7 +153,7 @@ private:
// VISITORS
virtual void visit(AstNetlist* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
moveVars();
}
virtual void visit(AstCFunc* nodep) {
@ -167,7 +163,7 @@ private:
searchFuncStmts(nodep->initsp());
searchFuncStmts(nodep->stmtsp());
searchFuncStmts(nodep->finalsp());
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_cfuncp = NULL;
}
void searchFuncStmts(AstNode* nodep) {
@ -176,8 +172,8 @@ private:
// This could be more complicated; allow always-set under both branches of a IF.
// If so, check for ArrayRef's and such, as they aren't acceptable.
for (; nodep; nodep=nodep->nextp()) {
if (nodep->castNodeAssign()) {
if (AstVarRef* varrefp = nodep->castNodeAssign()->lhsp()->castVarRef()) {
if (VN_IS(nodep, NodeAssign)) {
if (AstVarRef* varrefp = VN_CAST(VN_CAST(nodep, NodeAssign)->lhsp(), VarRef)) {
if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue");
if (!varrefp->varp()->user4p()) {
UINFO(4," FuncAsn "<<varrefp<<endl);
@ -230,13 +226,13 @@ private:
// No iterate; Don't want varrefs under it
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit LocalizeVisitor(AstNetlist* nodep) {
m_cfuncp = NULL;
nodep->accept(*this);
iterate(nodep);
}
virtual ~LocalizeVisitor() {
V3Stats::addStat("Optimizations, Vars localized", m_statLocVars);

View File

@ -52,11 +52,7 @@ private:
V3LanguageWords m_words; // Reserved word detector
// METHODS
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
VL_DEBUG_FUNC; // Declare debug()
void rename(AstNode* nodep, bool addPvt) {
if (!nodep->user1()) { // Not already done
@ -78,7 +74,7 @@ private:
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
iterateChildren(nodep);
m_modp = NULL;
}
// Add __PVT__ to names of local signals
@ -91,54 +87,55 @@ private:
}
virtual void visit(AstCFunc* nodep) {
if (!nodep->user1()) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
rename(nodep, false);
}
}
virtual void visit(AstVarRef* nodep) {
if (nodep->varp()) {
nodep->varp()->iterate(*this);
iterate(nodep->varp());
nodep->name(nodep->varp()->name());
}
}
virtual void visit(AstCell* nodep) {
if (!nodep->user1()) {
rename(nodep, !nodep->modp()->modPublic());
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstMemberDType* nodep) {
if (!nodep->user1()) {
rename(nodep, false);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstMemberSel* nodep) {
if (!nodep->user1()) {
rename(nodep, false);
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
virtual void visit(AstScope* nodep) {
if (!nodep->user1SetOnce()) {
if (nodep->aboveScopep()) nodep->aboveScopep()->iterate(*this);
if (nodep->aboveCellp()) nodep->aboveCellp()->iterate(*this);
if (nodep->aboveScopep()) iterate(nodep->aboveScopep());
if (nodep->aboveCellp()) iterate(nodep->aboveCellp());
// Always recompute name (as many level above scope may have changed)
// Same formula as V3Scope
nodep->name(nodep->isTop() ? "TOP"
: (nodep->aboveScopep()->name()+"."+nodep->aboveCellp()->name()));
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
}
//--------------------
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit NameVisitor(AstNetlist* nodep) {
nodep->accept(*this);
m_modp = NULL;
iterate(nodep);
}
virtual ~NameVisitor() {}
};

View File

@ -364,7 +364,7 @@ V3Number& V3Number::setMask(int nbits) {
// ACCESSORS - as strings
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
ostringstream out;
std::ostringstream out;
if (isDouble()) {
out.precision(17);
@ -781,7 +781,7 @@ bool V3Number::isUnknown() const {
return false;
}
bool V3Number::isLt(const V3Number& rhs) const {
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; }
if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; }
if (this->bitIsXZ(bit)) { return 0; }
@ -791,7 +791,7 @@ bool V3Number::isLt(const V3Number& rhs) const {
}
bool V3Number::isLtXZ(const V3Number& rhs) const {
// Include X/Z in comparisons for sort ordering
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; }
if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; }
if (this->bitIsXZ(bit)) { return 1; }
@ -1068,9 +1068,9 @@ V3Number& V3Number::opStreamL (const V3Number& lhs, const V3Number& rhs) {
m_fileline->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in streams.");
}
// Slice size should never exceed the lhs width
int ssize=min(rhs.toUInt(), (unsigned)lhs.width());
int ssize = std::min(rhs.toUInt(), (unsigned)lhs.width());
for (int istart=0; istart<lhs.width(); istart+=ssize) {
int ostart=max(0, lhs.width()-ssize-istart);
int ostart = std::max(0, lhs.width()-ssize-istart);
for (int bit=0; bit<ssize && bit<lhs.width()-istart; bit++) {
setBit(ostart+bit, lhs.bitIs(istart+bit));
}
@ -1126,7 +1126,7 @@ V3Number& V3Number::opLogIff (const V3Number& lhs, const V3Number& rhs) {
V3Number& V3Number::opEq (const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
char outc = 1;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=0; goto last; }
if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { outc=0; goto last; }
if (lhs.bitIsXZ(bit)) { outc='x'; }
@ -1139,7 +1139,7 @@ last:
V3Number& V3Number::opNeq (const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
char outc = 0;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; goto last; }
if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { outc=1; goto last; }
if (lhs.bitIsXZ(bit)) { outc='x'; }
@ -1152,7 +1152,7 @@ last:
bool V3Number::isCaseEq (const V3Number& rhs) const {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
if (this->width() != rhs.width()) return false;
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; }
}
return true;
@ -1165,7 +1165,7 @@ V3Number& V3Number::opCaseEq (const V3Number& lhs, const V3Number& rhs) {
V3Number& V3Number::opCaseNeq (const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
char outc = 0;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; }
}
last:
@ -1174,7 +1174,7 @@ last:
V3Number& V3Number::opWildEq (const V3Number& lhs, const V3Number& rhs) {
char outc = 1;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (!rhs.bitIsXZ(bit)
&& lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=0; goto last; }
if (lhs.bitIsXZ(bit)) outc='x';
@ -1185,7 +1185,7 @@ last:
V3Number& V3Number::opWildNeq (const V3Number& lhs, const V3Number& rhs) {
char outc = 0;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (!rhs.bitIsXZ(bit)
&& lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; }
if (lhs.bitIsXZ(bit)) outc='x';
@ -1197,7 +1197,7 @@ last:
V3Number& V3Number::opGt (const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
char outc = 0;
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; }
if (rhs.bitIs1(bit) && lhs.bitIs0(bit)) { outc=0; }
if (lhs.bitIsXZ(bit)) { outc='x'; }
@ -1210,14 +1210,14 @@ V3Number& V3Number::opGtS (const V3Number& lhs, const V3Number& rhs) {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
char outc = 0;
{
int mbit=max(lhs.width()-1,rhs.width()-1);
int mbit = std::max(lhs.width()-1,rhs.width()-1);
if (lhs.bitIsXZ(mbit)) { outc='x'; }
else if (rhs.bitIsXZ(mbit)) { outc='x'; }
else if (lhs.bitIs0(mbit) && rhs.bitIs1Extend(mbit)) { outc=1; } // + > -
else if (lhs.bitIs1Extend(mbit) && rhs.bitIs0(mbit)) { outc=0; } // - !> +
else {
// both positive or negative, normal >
for (int bit=0; bit<max(lhs.width()-1,rhs.width()-1); bit++) {
for (int bit=0; bit < std::max(lhs.width()-1,rhs.width()-1); bit++) {
if (lhs.bitIs1Extend(bit) && rhs.bitIs0(bit)) { outc=1; }
if (rhs.bitIs1Extend(bit) && lhs.bitIs0(bit)) { outc=0; }
if (lhs.bitIsXZ(bit)) { outc='x'; }
@ -1497,7 +1497,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
m_value[j] = unw64 / (vluint64_t)(rhs.m_value[0]);
k = unw64 - (vluint64_t)(m_value[j])*(vluint64_t)(rhs.m_value[0]);
}
UINFO(9, " opmoddiv-1w "<<lhs<<" "<<rhs<<" q="<<*this<<" rem=0x"<<hex<<k<<dec<<endl);
UINFO(9, " opmoddiv-1w "<<lhs<<" "<<rhs<<" q="<<*this<<" rem=0x"<<std::hex<<k<<std::dec<<endl);
if (is_modulus) { setZero(); m_value[0] = k; }
opCleanThis();
return *this;

View File

@ -39,8 +39,8 @@ class V3Number {
bool m_fromString:1; // True if from string literal
bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width
FileLine* m_fileline;
vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
std::vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
std::vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
string m_stringVal; // If isString, the value of the string
// METHODS
V3Number& setSingleBits(char value);
@ -330,6 +330,6 @@ public:
V3Number& opLtN (const V3Number& lhs, const V3Number& rhs);
V3Number& opLteN (const V3Number& lhs, const V3Number& rhs);
};
inline ostream& operator<<(ostream& os, const V3Number& rhs) { return os<<rhs.ascii(); }
inline std::ostream& operator<<(std::ostream& os, const V3Number& rhs) { return os<<rhs.ascii(); }
#endif // Guard

View File

@ -50,17 +50,17 @@
class V3OptionsImp {
public:
// TYPES
typedef std::map<string,set<string> > DirMap; // Directory listing
typedef std::map<string,std::set<string> > DirMap; // Directory listing
// STATE
list<string> m_allArgs; // List of every argument encountered
list<string> m_incDirUsers; // Include directories (ordered)
set<string> m_incDirUserSet; // Include directories (for removing duplicates)
list<string> m_incDirFallbacks; // Include directories (ordered)
set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
map<string,V3LangCode> m_langExts; // Language extension map
list<string> m_libExtVs; // Library extensions (ordered)
set<string> m_libExtVSet; // Library extensions (for removing duplicates)
std::list<string> m_allArgs; // List of every argument encountered
std::list<string> m_incDirUsers; // Include directories (ordered)
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
std::list<string> m_incDirFallbacks; // Include directories (ordered)
std::set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
std::map<string,V3LangCode> m_langExts; // Language extension map
std::list<string> m_libExtVs; // Library extensions (ordered)
std::set<string> m_libExtVSet; // Library extensions (for removing duplicates)
DirMap m_dirMap; // Directory listing
// ACCESSOR METHODS
@ -167,9 +167,9 @@ string V3Options::parameter(const string& name) {
void V3Options::checkParameters() {
if (!m_parameters.empty()) {
stringstream msg;
std::stringstream msg;
msg << "Parameters from the command line were not found in the design:";
for (map<string,string>::iterator it = m_parameters.begin();
for (std::map<string,string>::iterator it = m_parameters.begin();
it != m_parameters.end(); ++it) {
msg << " " << it->first;
}
@ -235,7 +235,7 @@ void V3Options::addArg(const string& arg) {
string V3Options::allArgsString() {
string out;
for (list<string>::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) {
for (std::list<string>::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) {
if (out != "") out += " ";
out += *it;
}
@ -297,10 +297,10 @@ string V3Options::fileExists (const string& filename) {
V3OptionsImp::DirMap::iterator diriter = m_impp->m_dirMap.find(dir);
if (diriter == m_impp->m_dirMap.end()) {
// Read the listing
m_impp->m_dirMap.insert(make_pair(dir, set<string>() ));
m_impp->m_dirMap.insert(std::make_pair(dir, std::set<string>() ));
diriter = m_impp->m_dirMap.find(dir);
set<string>* setp = &(diriter->second);
std::set<string>* setp = &(diriter->second);
if (DIR* dirp = opendir(dir.c_str())) {
while (struct dirent* direntp = readdir(dirp)) {
@ -311,8 +311,8 @@ string V3Options::fileExists (const string& filename) {
}
}
// Find it
set<string>* filesetp = &(diriter->second);
set<string>::iterator fileiter = filesetp->find(basename);
std::set<string>* filesetp = &(diriter->second);
std::set<string>::iterator fileiter = filesetp->find(basename);
if (fileiter == filesetp->end()) {
return ""; // Not found
}
@ -323,7 +323,7 @@ string V3Options::fileExists (const string& filename) {
}
string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) {
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
string fn = V3Os::filenameFromDirBase(dirname, modname+*extIter);
string exists = fileExists(fn);
if (exists!="") {
@ -340,12 +340,12 @@ string V3Options::filePath (FileLine* fl, const string& modname, const string& l
// Find a filename to read the specified module name,
// using the incdir and libext's.
// Return "" if not found.
for (list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
for (std::list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) {
string exists = filePathCheckOneDir(modname, *dirIter);
if (exists!="") return exists;
}
for (list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
for (std::list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) {
string exists = filePathCheckOneDir(modname, *dirIter);
if (exists!="") return exists;
@ -372,16 +372,16 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
fl->v3error("This may be because there's no search path specified with -I<dir>."<<endl);
}
fl->v3error("Looked in:"<<endl);
for (list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
for (std::list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) {
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter);
fl->v3error(" "<<fn<<endl);
}
}
for (list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
for (std::list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) {
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter);
fl->v3error(" "<<fn<<endl);
}
@ -398,7 +398,7 @@ V3LangCode V3Options::fileLanguage(const string &filename) {
string::size_type pos;
if ((pos = ext.rfind(".")) != string::npos) {
ext.erase(0, pos + 1);
map<string,V3LangCode>::iterator it = m_impp->m_langExts.find(ext);
std::map<string,V3LangCode>::iterator it = m_impp->m_langExts.find(ext);
if (it != m_impp->m_langExts.end()) {
return it->second;
}
@ -661,6 +661,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
else if ( !strcmp (sw, "-debug-abort") ) { abort(); } // Undocumented, see also --debug-sigsegv
else if ( onoff (sw, "-debug-check", flag/*ref*/) ){ m_debugCheck = flag; }
else if ( onoff (sw, "-debug-leak", flag/*ref*/) ){ m_debugLeak = flag; }
else if ( onoff (sw, "-debug-nondeterminism", flag/*ref*/) ){ m_debugNondeterminism = flag; }
else if ( onoff (sw, "-debug-partition", flag/*ref*/) ){ m_debugPartition = flag; } // Undocumented
else if ( onoff (sw, "-debug-self-test", flag/*ref*/) ){ m_debugSelfTest = flag; } // Undocumented
else if ( !strcmp (sw, "-debug-sigsegv") ) { throwSigsegv(); } // Undocumented, see also --debug-abort
else if ( !strcmp (sw, "-debug-fatalsrc") ) { v3fatalSrc("--debug-fatal-src"); } // Undocumented, see also --debug-abort
else if ( onoff (sw, "-decoration", flag/*ref*/) ) { m_decoration = flag; }
@ -678,6 +681,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
else if ( !strcmp (sw, "-private") ) { m_public = false; }
else if ( onoff (sw, "-prof-cfuncs", flag/*ref*/) ) { m_profCFuncs = flag; }
else if ( onoff (sw, "-profile-cfuncs", flag/*ref*/) ) { m_profCFuncs = flag; } // Undocumented, for backward compat
else if ( onoff (sw, "-prof-threads", flag/*ref*/) ) { m_profThreads = flag; }
else if ( onoff (sw, "-public", flag/*ref*/) ) { m_public = flag; }
else if ( !strncmp(sw, "-pvalue+", strlen("-pvalue+"))) { addParameter(string(sw+strlen("-pvalue+")), false); }
else if ( onoff (sw, "-relative-cfuncs", flag/*ref*/) ) { m_relativeCFuncs = flag; }
@ -689,6 +693,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
else if ( onoff (sw, "-stats", flag/*ref*/) ) { m_stats = flag; }
else if ( onoff (sw, "-stats-vars", flag/*ref*/) ) { m_statsVars = flag; m_stats |= flag; }
else if ( !strcmp (sw, "-sv") ) { m_defaultLanguage = V3LangCode::L1800_2005; }
else if ( onoff (sw, "-threads-coarsen", flag/*ref*/)) { m_threadsCoarsen = flag; } // Undocumented, debug
else if ( onoff (sw, "-trace", flag/*ref*/) ) { m_trace = flag; }
else if ( onoff (sw, "-trace-dups", flag/*ref*/) ) { m_traceDups = flag; }
else if ( onoff (sw, "-trace-params", flag/*ref*/) ) { m_traceParams = flag; }
@ -723,6 +728,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
case 's': m_oSplit = flag; break;
case 't': m_oLifePost = flag; break;
case 'u': m_oSubst = flag; break;
case 'v': m_oReloop = flag; break;
case 'x': m_oExpand = flag; break;
case 'y': m_oAcycSimp = flag; break;
case 'z': m_oLocalize = flag; break;
@ -1012,6 +1018,20 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
shift; m_threads = atoi(argv[i]);
if (m_threads < 0) fl->v3fatal("--threads must be >= 0: "<<argv[i]);
}
else if ( !strcmp (sw, "-threads-dpi") && (i+1)<argc) {
shift;
if (!strcmp(argv[i], "all")) { m_threadsDpiPure=true; m_threadsDpiUnpure=true; }
else if (!strcmp(argv[i], "none")) { m_threadsDpiPure=false; m_threadsDpiUnpure=false; }
else if (!strcmp(argv[i], "pure")) { m_threadsDpiPure=true; m_threadsDpiUnpure=false; }
else {
fl->v3fatal("Unknown setting for --threads-dpi: "<<argv[i]);
}
}
else if ( !strcmp (sw, "-threads-max-mtasks") ) {
shift; m_threadsMaxMTasks = atoi(argv[i]);
if (m_threadsMaxMTasks < 1)
fl->v3fatal("--threads-max-mtasks must be >= 1: "<<argv[i]);
}
else if ( !strcmp (sw, "-top-module") && (i+1)<argc ) {
shift; m_topModule = argv[i];
}
@ -1075,7 +1095,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
// Read the specified -f filename and process as arguments
UINFO(1,"Reading Options File "<<filename<<endl);
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream(filename));
const vl_unique_ptr<std::ifstream> ifp (V3File::new_ifstream(filename));
if (ifp->fail()) {
fl->v3error("Cannot open -f command file: "+filename);
return;
@ -1120,7 +1140,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
}
// Strip off arguments and parse into words
vector<string> args;
std::vector<string> args;
string::size_type startpos = 0;
while (startpos < whole_file.length()) {
while (isspace(whole_file[startpos])) ++startpos;
@ -1222,6 +1242,9 @@ V3Options::V3Options() {
m_coverageUser = false;
m_debugCheck = false;
m_debugLeak = true;
m_debugNondeterminism = false;
m_debugPartition = false;
m_debugSelfTest = false;
m_decoration = true;
m_exe = false;
m_ignc = false;
@ -1236,6 +1259,7 @@ V3Options::V3Options() {
m_pinsScBigUint = false;
m_pinsUint8 = false;
m_profCFuncs = false;
m_profThreads = false;
m_preprocOnly = false;
m_preprocNoLine = false;
m_public = false;
@ -1248,6 +1272,10 @@ V3Options::V3Options() {
m_statsVars = false;
m_systemC = false;
m_threads = 0;
m_threadsDpiPure = true;
m_threadsDpiUnpure = false;
m_threadsCoarsen = true;
m_threadsMaxMTasks = 0;
m_trace = false;
m_traceDups = false;
m_traceParams = true;
@ -1365,6 +1393,7 @@ void V3Options::optimize(int level) {
m_oLife = flag;
m_oLifePost = flag;
m_oLocalize = flag;
m_oReloop = flag;
m_oReorder = flag;
m_oSplit = flag;
m_oSubst = flag;

View File

@ -37,12 +37,12 @@
class V3OptionsImp;
class FileLine;
typedef vector<string> V3StringList;
typedef set<string> V3StringSet;
typedef std::vector<string> V3StringList;
typedef std::set<string> V3StringSet;
class V3Options {
// TYPES
typedef map<string,int> DebugSrcMap;
typedef std::map<string,int> DebugSrcMap;
// MEMBERS (general options)
V3OptionsImp* m_impp; // Slow hidden options
@ -58,7 +58,7 @@ class V3Options {
V3StringList m_forceIncs; // argument: -FI
DebugSrcMap m_debugSrcs; // argument: --debugi-<srcfile>=<level>
DebugSrcMap m_dumpTrees; // argument: --dump-treei-<srcfile>=<level>
map<string,string> m_parameters; // Parameters
std::map<string,string> m_parameters; // Parameters
bool m_preprocOnly; // main switch: -E
@ -76,6 +76,9 @@ class V3Options {
bool m_coverageUser; // main switch: --coverage-func
bool m_debugCheck; // main switch: --debug-check
bool m_debugLeak; // main switch: --debug-leak
bool m_debugNondeterminism; // main switch: --debug-nondeterminism
bool m_debugPartition; // main switch: --debug-partition
bool m_debugSelfTest; // main switch: --debug-self-test
bool m_decoration; // main switch: --decoration
bool m_exe; // main switch: --exe
bool m_ignc; // main switch: --ignc
@ -87,6 +90,7 @@ class V3Options {
bool m_pinsScBigUint;// main switch: --pins-sc-biguint
bool m_pinsUint8; // main switch: --pins-uint8
bool m_profCFuncs; // main switch: --prof-cfuncs
bool m_profThreads; // main switch: --prof-threads
bool m_public; // main switch: --public
bool m_relativeCFuncs; // main switch: --relative-cfuncs
bool m_relativeIncludes; // main switch: --relative-includes
@ -96,6 +100,9 @@ class V3Options {
bool m_skipIdentical;// main switch: --skip-identical
bool m_stats; // main switch: --stats
bool m_statsVars; // main switch: --stats-vars
bool m_threadsCoarsen; // main switch: --threads-coarsen
bool m_threadsDpiPure; // main switch: --threads-dpi all/pure
bool m_threadsDpiUnpure; // main switch: --threads-dpi all
bool m_trace; // main switch: --trace
bool m_traceDups; // main switch: --trace-dups
bool m_traceParams; // main switch: --trace-params
@ -117,6 +124,7 @@ class V3Options {
int m_outputSplitCTrace;// main switch: --output-split-ctrace
int m_pinsBv; // main switch: --pins-bv
int m_threads; // main switch: --threads (0 == --no-threads)
int m_threadsMaxMTasks; // main switch: --threads-max-mtasks
int m_traceDepth; // main switch: --trace-depth
int m_traceMaxArray;// main switch: --trace-max-array
int m_traceMaxWidth;// main switch: --trace-max-width
@ -159,6 +167,7 @@ class V3Options {
bool m_oLifePost; // main switch: -Ot: delayed assignment elimination
bool m_oLocalize; // main switch: -Oz: convert temps to local variables
bool m_oInline; // main switch: -Oi: module inlining
bool m_oReloop; // main switch: -Ov: reform loops
bool m_oReorder; // main switch: -Or: reorder assignments in blocks
bool m_oSplit; // main switch: -Os: always assignment splitting
bool m_oSubst; // main switch: -Ou: substitute expression temp values
@ -231,8 +240,14 @@ class V3Options {
bool coverageUser() const { return m_coverageUser; }
bool debugCheck() const { return m_debugCheck; }
bool debugLeak() const { return m_debugLeak; }
bool debugNondeterminism() const { return m_debugNondeterminism; }
bool debugPartition() const { return m_debugPartition; }
bool debugSelfTest() const { return m_debugSelfTest; }
bool decoration() const { return m_decoration; }
bool exe() const { return m_exe; }
bool threadsDpiPure() const { return m_threadsDpiPure; }
bool threadsDpiUnpure() const { return m_threadsDpiUnpure; }
bool threadsCoarsen() const { return m_threadsCoarsen; }
bool trace() const { return m_trace; }
bool traceDups() const { return m_traceDups; }
bool traceParams() const { return m_traceParams; }
@ -245,6 +260,7 @@ class V3Options {
bool pinsScBigUint() const { return m_pinsScBigUint; }
bool pinsUint8() const { return m_pinsUint8; }
bool profCFuncs() const { return m_profCFuncs; }
bool profThreads() const { return m_profThreads; }
bool allPublic() const { return m_public; }
bool lintOnly() const { return m_lintOnly; }
bool ignc() const { return m_ignc; }
@ -266,6 +282,7 @@ class V3Options {
int outputSplitCTrace() const { return m_outputSplitCTrace; }
int pinsBv() const { return m_pinsBv; }
int threads() const { return m_threads; }
int threadsMaxMTasks() const { return m_threadsMaxMTasks; }
bool mtasks() const { return (m_threads > 1); }
int traceDepth() const { return m_traceDepth; }
int traceMaxArray() const { return m_traceMaxArray; }
@ -320,6 +337,7 @@ class V3Options {
bool oLifePost() const { return m_oLifePost; }
bool oLocalize() const { return m_oLocalize; }
bool oInline() const { return m_oInline; }
bool oReloop() const { return m_oReloop; }
bool oReorder() const { return m_oReorder; }
bool oSplit() const { return m_oSplit; }
bool oSubst() const { return m_oSubst; }

File diff suppressed because it is too large Load Diff

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