Compare commits

..
228 Commits
Author SHA1 Message Date
Wilson Snyder 57c816f906 Merge from master for release. (update) 2023-09-16 21:44:39 -04:00
Wilson Snyder 8f43e74288 Update CMakeList version 2023-09-16 20:54:54 -04:00
Wilson Snyder 82e1dbe162 Merge from master for release. 2023-09-16 20:43:53 -04:00
Wilson Snyder cef3960a4e Version bump 2023-09-16 18:36:39 -04:00
Wilson Snyder 783b7ecdab Fix clang override warning (last commit) 2023-09-16 17:52:02 -04:00
Wilson Snyder 8bd1c63b32 Support function non-constant default arguments (#4470). 2023-09-16 17:37:25 -04:00
Wilson Snyder e6fb7e970d Fix recursive display causing segfault (#4480). 2023-09-16 10:10:21 -04:00
Wilson Snyder 891cc0f9b6 Fix t_dist_cppstyle Perl performance issue (#4085). 2023-09-16 09:22:12 -04:00
Wilson Snyder 10fbe74cef Commentary: Changes update 2023-09-15 23:25:28 -04:00
Wilson Snyder 05d04a3959 Internals: Fix misnamed member. No functional change. 2023-09-15 23:02:34 -04:00
Wilson Snyder 19f7279542 Fix false INFINITELOOP on forever..mailbox.get() (#4323). 2023-09-15 22:05:55 -04:00
Wilson Snyder aa608472ae Support recursive function calls (#3267). 2023-09-15 20:46:31 -04:00
Wilson Snyder 26ed2a06a0 Tests: Add UVM test that generates ok with known issues commented out. (#1538) (#3267) (#4125) (#4323) (#4349) (#4465) (#4467) (#4468) (#4470) (#4493) (#4494) (#4495) (#4496) (#4497) 2023-09-15 20:26:46 -04:00
Wilson Snyder 07013da13d Tests: Disabled test (#4493) 2023-09-15 19:56:05 -04:00
Wilson Snyder d840c612d4 Fix IGNOREDRETURN to not warn on void-cast static function calls. 2023-09-15 19:01:11 -04:00
Wilson Snyder 188cf85b3f Suppress some debug unknown hash/simulate warnings in UVM. 2023-09-15 18:33:02 -04:00
Wilson Snyder c52ba28dd0 Tests: Fix commentary to unify issue references. 2023-09-15 18:12:11 -04:00
Wilson Snyder 10c1653e72 Fix ZERODLY to not warn on 'wait(0)'. 2023-09-15 08:53:29 -04:00
Wilson Snyder 2fbd41e13b Tests: Add t_queue_persistence.pl as broken test (#3385) (#4489) 2023-09-15 07:59:06 -04:00
Ryszard Rozak 96857c5f95 Fix the error message when the type of ref argument is wrong (#4490) 2023-09-15 07:17:24 -04:00
Wilson Snyder 131eb319e0 Tests: Advance UVM through V3LinkDot Param (#1538 partial) 2023-09-14 20:09:43 -04:00
Wilson Snyder 6e589377f4 Fix reference to extended class in parameterized class (#4466). 2023-09-14 20:07:49 -04:00
Wilson Snyder 2c84b37bf7 Internals: Rename some V3LinkDot variables. No functional change. 2023-09-14 20:06:20 -04:00
Kamil Rakoczy 0d67caff77 Internals: V3Task: refactor common code to connectPort. No functional change intended (#4488)
Signed-off-by: Kamil Rakoczy <[email protected]>
2023-09-14 07:24:49 -04:00
Anthony Donlon 3dde57d539 Fix lint of case statements with enum and wildcard bits (#4464) (#4487) 2023-09-14 07:22:49 -04:00
Kamil Rakoczy ec2e3ec0e4 Update clang_check_attributes to take into account MT_DISABLED (#4479) 2023-09-13 16:32:18 -04:00
Kamil Rakoczy 9fe459c820 Internals: V3LockGuard: Add constructor for adopting already locked mutex. (#4476) 2023-09-13 13:52:59 -04:00
Wilson Snyder 823e0723fb Internals: Remove legacy define. No functional change. 2023-09-13 08:46:51 -04:00
Kamil Rakoczy 8bd6d7c5b1 Internals: Add V3ThreadSafety (#4477) 2023-09-13 07:57:48 -04:00
Wilson Snyder 839a8fa4d9 Fix error on enum with VARHIDDEN of cell (#4482). 2023-09-12 17:47:57 -04:00
Krzysztof Boroński 24917a187a Fix V3CUse, do not consider implementations (.cpp) at all (#4386) 2023-09-12 17:59:57 +02:00
Ryszard Rozak 6c6f03cf7c Fix static cast from a stream type (#4469) (#4485) 2023-09-12 11:06:12 -04:00
Kamil RakoczyandMariusz Glebocki 5676443139 Internals: Rename and slightly change other threads stopping/resuming methods. (#4478)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 13:43:26 -04:00
Kamil RakoczyandMariusz Glebocki e77d847671 Check whether V3ERROR_NO_GLOBAL_ is not already defined. (#4475)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 09:01:34 -04:00
Kamil RakoczyandMariusz Glebocki 0baee84e96 Move dpiTemporaryVarSuffix() to header. (#4474)
Co-authored-by: Mariusz Glebocki <[email protected]>
2023-09-11 09:01:09 -04:00
Ryszard Rozak c446cc2596 Internals: Remove unused constructor. No functional change. (#4473)
Signed-off-by: Ryszard Rozak <[email protected]>
2023-09-11 07:10:33 -04:00
Ryszard Rozak b66c4153b1 Compute purity of AstCMethodHard (#4460) 2023-09-11 13:06:15 +02:00
Wilson Snyder d72f1b89fc Internals: Minor internal code coverage cleanups 2023-09-10 18:53:51 -04:00
Wilson Snyder b5b278d072 Fix fork crash with no init, test for (#4471) 2023-09-10 08:53:21 -04:00
Wilson Snyder cb5466a90b Tests: UVM passes V3Param stage 2023-09-09 11:54:55 -04:00
Wilson Snyder 4de25369da Tests: Add t_uvm_all 2023-09-08 22:29:23 -04:00
Wilson Snyder cdd8953085 Disable Subst when not pure 2023-09-08 21:52:33 -04:00
Wilson Snyder 2f2f0164ff Internals: Use specific types on some cloneTrees. No functional change. 2023-09-08 21:51:59 -04:00
Kamil Rakoczy 6e977e1024 Generate compile_commands.json using bear (#4463) 2023-09-08 17:26:11 -04:00
Krzysztof Boroński 70b11f91b4 Support block_item_declaration in forks (#4455) 2023-09-08 16:40:14 +02:00
Wilson Snyder 014301587f Unsupported instead of syntax error on parameter var initial values 2023-09-08 08:37:17 -04:00
Krzysztof Bieganski 139e93d371 Internals: Reduce the number of typechecks in graphs (#4398). No functional change intended. 2023-09-08 07:35:52 -04:00
Krzysztof Bieganski fb1fc46b06 Internals: Rework self pointers (#4396) 2023-09-08 07:34:35 -04:00
Wilson Snyder 9882ab6c67 Tests: Remove unstable new test 2023-09-08 07:22:52 -04:00
Ryszard Rozak 1a1f919882 Check the output in t_class_method_str_literal.v (#4459) 2023-09-08 08:51:54 +02:00
Ryszard Rozak 91227d26bb Internals: Rename pure to dpiPure. No functional change. (#4461) 2023-09-08 08:51:19 +02:00
Wilson Snyder 11b5dae88d Support let 2023-09-07 21:45:51 -04:00
Wilson Snyder 0c7d485417 Fix error printing internal name() 2023-09-06 21:07:06 -04:00
Ryszard Rozak c40e34b134 Support assignments of stream expressions on queues to packed values (#4458) 2023-09-06 15:25:48 +02:00
Wilson Snyder 6cdf8da3f7 Fix documentation line wrap (#4456) 2023-09-05 07:25:33 -04:00
Wilson Snyder c65265477f Tests: Enable cover named property 2023-09-03 21:02:42 -04:00
Wilson Snyder de77f1ee5c Improve error that assert-under-assert is unsupported 2023-09-03 09:08:43 -04:00
Wilson Snyder c8c980c49d Fix mis-warning on #() in classes' own functions. 2023-09-02 08:33:56 -04:00
Wilson Snyder ad329612fd Internals: Remove unnecessary cloneTree. 2023-09-01 20:13:20 -04:00
Wilson Snyder c324895cbc Internals: Remove unnecessary cloneTree. 2023-09-01 19:49:57 -04:00
Wilson Snyder 679a4314dc Tests: Avoid side effect on array index 2023-09-01 19:26:50 -04:00
Wilson Snyder 36932d31c4 Fix C++11 missing C++14's std::make_unique. 2023-09-01 18:07:15 -04:00
Wilson Snyder ed25aaaad9 Internals: Fix g++ std11 warning. No functional change. 2023-09-01 17:59:30 -04:00
Wilson Snyder 147aa41ba5 Internals: Fix virt call in constructor warning. No functional change. 2023-09-01 17:58:17 -04:00
Ryszard Rozak 5b7e32581b Internals: Cleanup V3Task. No function change intended. (#4454) 2023-09-01 08:03:16 -04:00
Wilson Snyder 2f90fb2bec Set lifetime for repeat variable (#4451 partial) 2023-08-31 18:34:37 -04:00
Wilson Snyder 8c480fd39e Internals: Fix cppcheck warnings 2023-08-31 18:29:58 -04:00
Krzysztof Bieganski ffbbd438ae Internals: Use runtime type info instead of dynamic_cast for faster graph type checks (#4397) 2023-08-31 18:00:53 -04:00
Anthony Donlon 96ee81fa3f Fix unstable output of VHashSha256 (#4453) 2023-08-31 13:42:37 -04:00
Wilson Snyder 3d2399ea0f Tests: Enable fixed-earlier (#2314) 2023-08-30 18:10:30 -04:00
Wilson Snyder 6d5dde8645 Fix duplicate Vfork functions (#4418) 2023-08-30 17:59:25 -04:00
Wilson Snyder 048c9f3fe2 Support extra commas before $display 2023-08-30 17:31:11 -04:00
Wilson Snyder 373265752f Fix display %x formatting of real. 2023-08-30 17:21:33 -04:00
Wilson Snyder 5e6519c1b0 Commentary 2023-08-30 07:46:39 -04:00
Wilson Snyder ccdb52c1d9 Fix lifetime unknown error on enum.name (#4448). 2023-08-30 07:20:25 -04:00
Yinan Xu b4b74d72f0 Add prepareClone and atClone APIs for Verilated models (#3503) (#4444)
This API is used if the user copies the process using `fork`
and similar OS-level mechanisms. The `at_clone` member function
ensures that all model-allocated resources are re-allocated, such
that the copied child process/model can simulate correctly.

A typical allocated resource is the thread pool, which every model
has its own pool.
2023-08-30 07:02:55 -04:00
Wilson Snyder ca6ab584d0 Internals: Remove cloneTreeNull. No functional change intended. 2023-08-29 20:29:11 -04:00
github action 799a502cac Apply 'make format' 2023-08-29 23:56:25 +00:00
Ethan Sifferman 8d1570db28 Add check for conflicting options e.g. binary+lintonly (#4409) 2023-08-29 19:55:37 -04:00
Wilson Snyder 2a30bbe493 Fix internal error on real conversion (#4447). 2023-08-29 18:07:23 -04:00
Anthony Donlon cf6566b9bc Internal: Optimize program size by refactoring error reporting routines (#4446) 2023-08-29 16:54:32 -04:00
Krzysztof Boroński c3e19f2821 Move variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356) 2023-08-29 20:01:00 +02:00
Ryszard Rozak e24197fd16 Don't move function calls before the expression (#4413) 2023-08-28 15:44:41 +02:00
Anthony Donlon 63db60f646 Fix using type in parameterized classes without #() (#4281) (#4440)
* Check whether a class is parameterized or not with AstClass::isParameterized method

* Fix usage conflict of user2 pointer in V3Param.cpp
2023-08-26 12:06:26 -01:00
Wilson Snyder e66b28823d Internals: Cleanup some spacing. No functional change. 2023-08-26 05:59:56 -04:00
Wilson Snyder 77bd565ef6 Commentary: Changes update 2023-08-25 08:28:26 -04:00
Wilson Snyder 00d63883f8 Fix false MULTITOP on bound interfaces (#4438). 2023-08-25 08:21:15 -04:00
Wilson Snyder 10dd675371 Fix false UNUSEDPARAM on generate localparam (#4427). 2023-08-25 07:59:29 -04:00
Ryszard Rozak 2daa32b98b Support assignments of packed values to stream expressions on queues (#4401) 2023-08-25 11:24:12 +02:00
Ryszard Rozak e8e7912871 Tests: Fix t_net_delay test (#4436) 2023-08-23 13:27:17 -01:00
Ryszard Rozak 90079c2974 Fix nested assignments on the LHS (#4435) 2023-08-23 12:08:22 +02:00
Wilson Snyder 967a8530dd Fix errors on some $past cases (#4425) 2023-08-22 01:49:06 -04:00
Krzysztof Boroński ef4794e36d Support parenthesesless calls to static methods (#4432) 2023-08-22 06:06:34 +01:00
Aleksander Kiryk 7c7c92d2dd Fix coroutine handle movement during queue manipulation (#4431) 2023-08-21 10:22:09 -04:00
Wilson Snyder 95f3dd0535 Commentary (#4429) 2023-08-21 06:23:35 -04:00
Anthony Donlon 768b78e7d0 Fix checking for parameter and port connections in the wrong place (#4428) 2023-08-20 08:55:16 -04:00
Aleksander Kiryk 4370254e73 Fix ++/-- under statements (#4399) 2023-08-19 07:03:21 -04:00
Wilson Snyder 436f72582b Update GTKWave from upstream. 2023-08-19 06:57:43 -04:00
Wilson Snyder af3aba7821 Commentary: Changes update 2023-08-19 04:56:49 -04:00
github action 7479db6ffc Apply 'make format' 2023-08-19 08:52:38 +00:00
Frans Skarman e9cc2786b7 Add --no-trace-top option (#4422) 2023-08-19 04:51:29 -04:00
Anthony Donlon cbdee5a804 Fix Windows filename format, etc. (#3873) (#4421)
* Ignore CLion project files and CMake outputs
* Supporting stripping file path that contains backslash
* Set /bigobj flag and increase stack size for windows platform
* Fix MSVC warnings
2023-08-16 07:34:57 -04:00
Anthony Donlon 0f66262fa1 Internals: Refactor AstNode::checkTreeIter for better stack size (#4420) 2023-08-16 07:32:39 -04:00
Jose Loyola 6c80457262 CI: Refactor docker build action to define DOCKER_HUB_NAMESPACE as repo variable (#4419) 2023-08-15 06:45:07 +02:00
Wilson Snyder 16008acdf1 Internals: Add disabled duplicate function check (#4418) 2023-08-12 10:51:35 -04:00
Wilson Snyder 5447ed2629 Fix multple function definitions in V3Sched (#4416). 2023-08-12 10:41:44 -04:00
Wilson Snyder 9eba61018a Tests: Ignore debug message in error line 2023-08-12 09:39:02 -04:00
Wilson Snyder bc97319402 Internals: State commentary. No functional change. 2023-08-12 08:55:08 -04:00
Wilson Snyder 709db5c648 Internals: Avoid unordered_set 2023-08-12 08:46:25 -04:00
Krzysztof Boroński b752faa107 Fix jumping over object initialization (#4411) 2023-08-11 18:28:37 +02:00
Krzysztof Boroński 8d512c3187 Fix variable lifetimes in extern methods (#4414) 2023-08-11 16:52:59 +02:00
Ryszard Rozak 2d9bc73709 Fix dtype of condition operation on class objects (#4345) (#4352) 2023-08-07 05:54:30 -04:00
Ryszard Rozak b0942ed8c7 Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404) 2023-08-07 11:35:44 +02:00
Wilson Snyder e6b0bdd4df Merge from master for release. 2023-08-06 14:17:13 -04:00
Wilson Snyder b1c1aa53a9 devel release 2023-08-06 10:53:16 -04:00
Wilson Snyder f79749e7b2 Version bump 2023-08-06 10:39:35 -04:00
Krzysztof Bieganski 1a77030c9b Internals: Speedup to only typecheck node child getters in debug builds (#4394) 2023-08-05 03:24:00 -04:00
Wilson Snyder 817ade27fb Commentary: Changes update 2023-08-05 03:18:00 -04:00
Aleksander Kiryk 4afa14b612 Internals: Remove m_pkgp and m_classp from WidthVisitor (#4402) 2023-08-05 03:07:23 -04:00
Krzysztof Bieganski 9caa79a7ea Internals: Remove the name field from AstVarRef (#4395) 2023-08-03 02:52:52 -04:00
Wilson Snyder 446f21d2a0 Tests: Fix more missing internal code coverage 2023-07-27 06:53:43 -04:00
Wilson Snyder 06521f6cf1 Tests: Fix some missing coverage, and cleanup some error messages 2023-07-27 06:53:43 -04:00
github action 75336670d7 Apply 'make format' 2023-07-27 09:44:05 +00:00
Andrew Miloradovsky 106a9fe67f Fix config_build.h issues (#4380) (#4381) 2023-07-27 05:42:35 -04:00
Wilson Snyder a5eccc199b Commentary: Changes update 2023-07-27 04:01:28 -04:00
Vito Gamberini 837d9f9d77 Require CMake config minimum version 3.13 (#4389) (#4390) 2023-07-26 17:54:18 -04:00
Anthony Donlon 959387b69f Support property declaration with empty parentheses (#4313) (#4317) 2023-07-26 05:18:21 -04:00
Ryszard Rozak fd0703b8c8 Fix selects on unpacked structs (#4359) 2023-07-25 12:50:24 +02:00
Ryszard Rozak 3ff608b9cb Fix non-public methods with wide output (#4364) 2023-07-25 04:55:58 -04:00
Aleksander Kiryk edc0a0e982 Optimize V3Number::opAdd (#4385) 2023-07-24 17:09:27 -04:00
Aleksander Kiryk 1549ff9185 Support more types in wait (#4374) 2023-07-24 08:14:01 -04:00
Wilson Snyder 3454641244 Commentary: Add website badge 2023-07-21 04:11:15 -04:00
Wilson Snyder 5dedf25e3b Commentary: Add website badge 2023-07-21 04:07:29 -04:00
Wilson Snyder 7214c180ae Commentary: Add website badge 2023-07-21 04:06:22 -04:00
Ryszard Rozak aa0ef554b3 Fix order of evaluation of function calls in statements (#4375) 2023-07-21 03:29:11 -04:00
Ryszard Rozak a6015863d8 Support for static method calls as default values of function arguments (#4378) 2023-07-21 03:27:00 -04:00
Aleksander Kiryk 5a694ccb8a Fix assign to input var in methods (#4367) 2023-07-17 09:52:37 -04:00
Aleksander Kiryk f7d09c671a Fix VlProcess not found (#4368) 2023-07-17 09:52:07 -04:00
Ryszard Rozak 4de1b22672 Fix handling of super.new calls (#4366) 2023-07-17 13:30:42 +02:00
Krzysztof Boroński 4bdda3f240 Improve propagation of process requirement and decouple it from suspendability (#4321) 2023-07-14 17:12:02 +02:00
Ryszard Rozak ced1dfeffd Fix select operation on assoc array with wide keys (#4360) 2023-07-12 18:48:24 +02:00
Krzysztof Boroński 0774f825cf Internals: Use pointer to const nodes in V3MemberMap (#4353) 2023-07-12 11:36:24 -04:00
Ryszard Rozak 23fe5c1b93 Fix string concatenations (#4354) 2023-07-10 12:58:54 -04:00
Wilson Snyder bd4afeea69 Tests: Fix removing line numbers (#4352) 2023-07-10 08:17:49 -04:00
Julian Daube 3ee9b33f8a Fix cmake astgen for Rocky Linux 8.7 (#4343) 2023-07-09 06:02:21 +05:00
Wilson Snyder 5be0813893 Explicity reference std:: in header files (#4338). No functional change intended. 2023-07-08 13:20:40 -04:00
Wilson Snyder f94c1b32e5 Internals: Make V3MemberMap to remove member cache (#4350) 2023-07-08 12:40:27 -04:00
Wilson Snyder ff4923cf67 Internals: Make V3MemberMap towards removing member cache (#4350) 2023-07-08 12:27:50 -04:00
Krzysztof Bieganski 5788bc8048 Propagate class timescale to class package (#4348)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2023-07-07 17:27:33 +02:00
Wilson SnyderandAhmed El-Mahmoudy 5749662194 Documentation: Drop analytics_id unless VERILATOR_ANALYTICS_ID set (#4333)
Co-authored-by: Ahmed El-Mahmoudy <[email protected]>
2023-07-07 10:44:33 -04:00
Krzysztof Bieganski d5d1cc47e0 Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344)
Multiple edge timing controls in class methods would cause compilation errors on
the generated C++ code. This is because the `SenExprBuilder` used for these
would get recreated per timing control, resulting in duplicate variable names.
The fix is to have a single `SenExprBuilder` per scope.
2023-07-07 08:19:49 -04:00
Ryszard Rozak da043ca16d Fix comparison of class objects (#4346) 2023-07-07 16:20:23 +05:00
Aleksander Kiryk 9aa90569bf Support string replication with variable (#4341) 2023-07-07 13:05:06 +05:00
Wilson Snyder 294d78e0e2 Backout unstable dbb35ab8 (#4318) (#4320) 2023-07-05 14:26:28 -04:00
Wilson Snyder 8182c3fe2c Tests: Fix t_vpi_unimpl exiting early with missed error 2023-07-05 14:12:22 -04:00
Wilson Snyder 6f7bc38088 Tests: Improve specparam verilog.y coverage 2023-07-05 14:11:55 -04:00
Wilson Snyder dcbc0ccfdc Commentary: Revert lost name. 2023-07-05 13:55:47 -04:00
Aleksander Kiryk 399319dc1c Fix wildcard referring types (#4336) (#4342) 2023-07-05 13:08:00 -04:00
Krzysztof Bieganski 35d6da391b Fix multiple edge timing controls in class methods (#4318) (#4320)
Multiple edge timing controls in class methods would cause compilation errors on the generated C++ code. This is because the `SenExprBuilder` used for these would get recreated per timing control, resulting in duplicate variable names. The fix is to have a single `SenExprBuilder` per scope.
2023-07-05 10:36:10 +00:00
Aleksander Kiryk fcbd7c87af Fix unexpected RefDType on assoc arrays (#4337) 2023-07-04 18:13:22 +05:00
Ryszard Rozak 97feba6898 Support locator methods with "with" on assoc arrays (#4335) 2023-07-04 18:11:15 +05:00
Aleksander Kiryk 9249ffdb84 Fix queue slicing (#4329) 2023-07-03 13:55:24 -04:00
أحمد المحمودي 71df9e3381 Commentary: Fix typo of concurrent (#4332) 2023-07-02 16:12:09 -04:00
Wilson Snyder 3c964147be Add MISINDENT lint warning for misleading indentation. 2023-07-01 10:45:25 -04:00
Wilson Snyder 54b4b6b378 Internals: Fix missing calls to cleanFileline. 2023-07-01 08:59:26 -04:00
Wilson Snyder cff37f0775 Add GENUNNAMED lint warning.
Also fix generate-for blocks with empty statements getting lost.
2023-07-01 08:31:53 -04:00
Wilson Snyder 8b1cc3b303 Commentary: Changes update 2023-07-01 07:35:58 -04:00
Mariusz Glebocki 65b6121186 Revert .gdbinit modified by accident. (#4330)
`test_regress/.gdbinit` has been modified in
d42f9c095b most likely by accident.
Restore its previous content.
2023-06-30 16:57:31 -04:00
Ryszard Rozak 3d30527860 Fix unique..with method on queues of class objects (#4328) 2023-06-30 17:04:09 +02:00
Ryszard Rozak 24a9f7c650 Fix references to members of results of static methods (#4327) 2023-06-30 13:23:34 +02:00
Ryszard Rozak 242f661644 Fix selects of static members (#4326) 2023-06-30 09:12:22 +02:00
Aleksander Kiryk 32019d2bc4 Fix state update for always processes (#4311) 2023-06-29 09:21:49 -04:00
Krzysztof Boroński b252bb048f Fix unhandled overloads in V3InstrCount (#4324) 2023-06-28 08:49:56 -04:00
Ryszard Rozak ba47c01a01 Fix implicit calls of base class constructors with optional arguments (#4319) 2023-06-27 16:08:38 +02:00
Ryszard Rozak 51266898ec Fix comparison of string parameters (#4308) 2023-06-27 10:40:45 +02:00
Wilson Snyder f9164ab0d2 Internals: Assert dtype comparison. No functional change intended. 2023-06-25 07:40:00 -04:00
Wilson Snyder c43e707f43 Internals: refactor sameTreeIter. No functional change. 2023-06-25 07:26:45 -04:00
Mariusz Glebocki 85b7f828b3 Internals: Use std::packaged_task as a job wrapper; add and use V3ThreadPool::ScopedExclusiveAccess (#4310).
* Add VL_ASSERT_CAPABILITY; add assumeLocked and pretendUnlock to V3Mutex.

* Pass jobs as template-arguments and use std::packaged_task.

* Add and use V3ThreadPool::ScopedExclusiveAccess.
2023-06-23 18:25:12 -04:00
Wilson Snyder 7005a65d32 Tests: Attempt 20.04 fix for (#b1aec9b) (#4307) 2023-06-23 07:09:07 -04:00
Wilson Snyder 776d4e6ac2 CI: Build 20.04 on pull requests 2023-06-23 07:08:04 -04:00
Jiamin Zhu 1c88c3083c Fix splitting coroutines (#4297) (#4307) 2023-06-23 06:01:38 -04:00
Ryszard Rozak 4522834f7a Fix duplicate fork names (#4295) 2023-06-22 06:51:53 -04:00
Ryszard Rozak 9ee5fd0585 Fix handling of ref types in initial values of type parameters (#4304) 2023-06-20 14:10:07 -04:00
Wilson Snyder 63b2dbb827 Fix error when multiple duplicate DPI exports (#4301). 2023-06-20 06:26:46 -04:00
Wilson Snyder 5bee8ef15b Remove long deprecated verilated_heavy.h 2023-06-17 08:45:13 -04:00
Wilson Snyder e7de3d368f Commentary: Deprecation planned for 32-bit pointer -m32 mode (#4268). 2023-06-17 08:44:13 -04:00
Wilson Snyder d45deccc0a Internals: Favot std::array. No functional change intended. 2023-06-16 19:44:40 -04:00
Wilson Snyder d5b03ab721 Tests (#3806) (#3807) 2023-06-16 18:30:25 -04:00
Ryszard Rozak d1ff81a41c Fix class reference assignment checking (#4296) 2023-06-15 14:00:47 +02:00
Krzysztof Bieganski 43fa6858f6 Fix VlForkSync redeclaration (#4277)
Given nested forks, if the inner fork had a `join` or `join_any` at the end,
`V3Sched::transformForks()` would decide that the fork's `VlForkSync` variable
should be passed in from the outside. This resulted in the `VlForkSync` getting
redeclared as a function argument. Ultimately, it led to C++ compilation errors
due to variable redeclaration.

Fixed by rearranging the `if`s that decide whether a variable should be passed
in or left as-is.
2023-06-15 07:49:50 -04:00
Wilson Snyder db7eb45433 Contributors: Add Krzysztof Boronski and re-sort 2023-06-14 18:53:44 -04:00
Krzysztof Boronski 21c01ba97b V3Fork - transform processes that can outlive their parents into separate tasks (#4253)
Signed-off-by: Krzysztof Boronski <[email protected]>
2023-06-14 20:44:53 +02:00
Ryszard Rozak b517fb5ee3 Support some stream operations on queues (#4292) 2023-06-13 22:46:42 -04:00
Wilson Snyder 9dbc669199 devel release 2023-06-13 19:39:50 -04:00
Wilson Snyder 4f13c4d1bb Merge from master for release. 2023-06-13 19:37:22 -04:00
Wilson Snyder c37ec19751 Merge from master for release. 2023-04-30 07:09:22 -04:00
Wilson Snyder aaaf8e75af Merge from master for release. 2023-03-04 11:29:11 -05:00
Wilson Snyder 8cc31c6cf1 Merge from master for release. 2023-01-22 11:52:52 -05:00
Wilson Snyder 8468af1a20 Merge from master for release. 2022-12-14 22:07:37 -05:00
Wilson Snyder db39d70c75 Merge from master for release. 2022-10-29 17:52:23 -04:00
Wilson Snyder 04fff7ebd8 Merge from master for release. 2022-10-01 08:33:50 -04:00
Wilson Snyder b154af87a1 Merge from master for release. 2022-08-31 19:47:07 -04:00
Wilson Snyder 8b7480806a Merge from master for release. 2022-06-19 15:11:19 -04:00
Wilson Snyder e29f5ad559 Merge from master for release. 2022-06-19 15:08:17 -04:00
Wilson Snyder 4618fd5a43 Merge from master for release. 2022-05-02 22:22:20 -04:00
Wilson Snyder ed979c6aa1 Merge from master for release. 2022-03-12 13:14:48 -05:00
Wilson Snyder 141c5da3f9 Merge from master for release. 2021-12-05 11:15:32 -05:00
Wilson Snyder 0185ee5df3 Merge from master for release. 2021-10-17 14:46:03 -04:00
Wilson Snyder 27c08a65e1 Merge from master for release. 2021-09-01 21:02:51 -04:00
Wilson Snyder de408a5eb7 Merge from master for release. 2021-07-07 20:48:51 -04:00
Wilson Snyder fa3c072dd1 Merge from master for release. 2021-06-12 13:01:37 -04:00
Wilson Snyder aac4b7090a Merge from master for release. 2021-04-24 10:40:13 -04:00
Wilson Snyder f0d664530d Merge from master for release. 2021-03-12 15:48:45 -05:00
Wilson Snyder 702140bf93 Merge from master for release. 2021-02-25 21:49:42 -05:00
Wilson Snyder 2075db3db0 Merge from master for release. 2021-01-10 13:35:45 -05:00
Wilson Snyder b7fec513cf Merge from master for release. 2020-12-02 20:13:31 -05:00
Wilson Snyder 78e7758022 Merge from master for release. 2020-11-14 09:58:28 -05:00
Wilson Snyder 818318ddd5 Merge from master for release. 2020-10-15 08:25:44 -04:00
Wilson Snyder 39eea781bc Merge from master for release. 2020-09-07 09:38:50 -04:00
Wilson Snyder c4f7bd6a04 'Merge from master for release.' 2020-08-15 09:18:29 -04:00
Wilson Snyder dd03d0f3c9 'Merge from master for release.' 2020-07-11 09:16:56 -04:00
Wilson Snyder 369ce6af2c 'Merge from master for release.' 2020-06-06 07:49:57 -04:00
Wilson Snyder f83817510d 'Merge from master for release.' 2020-05-03 11:15:21 -04:00
Wilson Snyder bfcd779cfb 'Merge from master for release.' 2020-04-04 08:50:02 -04:00
Wilson Snyder e69c380205 'Merge from master for release.' 2020-03-08 08:39:29 -04:00
Wilson Snyder 61e14aa827 'Merge from master for release.' 2020-02-08 09:15:27 -05:00
Wilson Snyder 0c6c83e278 Merge from master for release. 2020-01-11 07:12:09 -05:00
Wilson Snyder 6f52208b68 Fix clang warning. 2019-12-09 18:28:56 -05:00
490 changed files with 86378 additions and 4227 deletions
+6 -6
View File
@@ -36,9 +36,9 @@ jobs:
m32: [0, 1]
exclude:
# Build pull requests only with ubuntu-22.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04 clang++
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
@@ -107,9 +107,9 @@ jobs:
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
exclude:
# Build pull requests only with ubuntu-22.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
+3 -2
View File
@@ -1,6 +1,7 @@
# Build and push verilator docker image when tags are pushed to the repository.
# The following secrets must be configured in the github repository:
# The following variable(s) must be configured in the github repository:
# DOCKER_HUB_NAMESPACE: docker hub namespace.
# The following secrets must be configured in the github repository:
# DOCKER_HUB_USER: user name for logging into docker hub
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
name: Build Verilator Container
@@ -52,7 +53,7 @@ jobs:
uses: docker/metadata-action@v4
with:
images: |
${{ secrets.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
tags: |
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
+2
View File
@@ -41,3 +41,5 @@ verilator-config-version.cmake
**/_build/*
**/obj_dir/*
/.vscode/
/.idea/
/cmake-build-*/
+1 -1
View File
@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.012
VERSION 5.016
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
+100
View File
@@ -8,6 +8,106 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.016 2023-09-16
==========================
**Minor:**
* Add prepareClone and atClone APIs for Verilated models (#3503) (#4444). [Yinan Xu]
* Add check for conflicting options e.g. binary and lint-only (#4409). [Ethan Sifferman]
* Add --no-trace-top to not trace top signals (#4412) (#4422). [Frans Skarman]
* Support recursive function calls (#3267).
* Support assignments of packed values to stream expressions on queues (#4401). [Ryszard Rozak, Antmicro Ltd]
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
* Support function non-constant default arguments (#4470).
* Support 'let'.
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
* Fix data type of condition operation on class objects (#4345) (#4352). [Ryszard Rozak, Antmicro Ltd]
* Fix variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356). [Krzysztof Boroński]
* Fix V3CUse, do not consider implementations (.cpp) at all (#4386). [Krzysztof Boroński]
* Fix ++/-- under statements (#4399). [Aleksander Kiryk, Antmicro Ltd]
* Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404). [Ryszard Rozak, Antmicro Ltd]
* Fix jumping over object initialization (#4411). [Krzysztof Boroński]
* Fix multiple issues towards short circuit support (#4413) (#4460). [Ryszard Rozak, Antmicro Ltd]
* Fix variable lifetimes in extern methods (#4414). [Krzysztof Boroński]
* Fix multiple function definitions in V3Sched (#4416). [Hennadii Chernyshchyk]
* Fix false UNUSEDPARAM on generate localparam (#4427). [Bill Pringlemeir]
* Fix checking for parameter and port connections in the wrong place (#4428). [Anthony Donlon]
* Fix coroutine handle movement during queue manipulation (#4431). [Aleksander Kiryk, Antmicro Ltd]
* Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd]
* Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov]
* Fix internal error on real conversion (#4447). [vdhotre-ventana]
* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra]
* Fix unstable output of VHashSha256 (#4453). [Anthony Donlon]
* Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd]
* Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov]
* Fix lint of case statements with enum and wildcard bits (#4464) (#4487). [Anthony Donlon]
* Fix reference to extended class in parameterized class (#4466).
* Fix recursive display causing segfault (#4480). [Kuoping Hsu]
* Fix the error message when the type of ref argument is wrong (#4490). [Ryszard Rozak, Antmicro Ltd]
* Fix display %x formatting of real.
* Fix mis-warning on #() in classes' own functions.
* Fix IGNOREDRETURN to not warn on void-cast static function calls.
* Fix ZERODLY to not warn on 'wait(0)'.
Verilator 5.014 2023-08-06
==========================
**Minor:**
* Deprecation planned for 32-bit pointer -m32 mode (#4268).
* Deprecate CMake config below version 3.13 (#4389) (#4390). [Vito Gamberini]
* Support some stream operations on queues (#4292). [Ryszard Rozak, Antmicro Ltd]
* Support property declaration with empty parentheses (#4313) (#4317). [Anthony Donlon]
* Support locator methods with "with" on assoc arrays (#4335). [Ryszard Rozak, Antmicro Ltd]
* Support string replication with variable (#4341). [Aleksander Kiryk, Antmicro Ltd]
* Support more types in wait (#4374). [Aleksander Kiryk, Antmicro Ltd]
* Support static method calls as default values of function arguments (#4378). [Ryszard Rozak, Antmicro Ltd]
* Add GENUNNAMED lint warning. [Srinivasan Venkataramanan, Deepa Palaniappan]
* Add MISINDENT lint warning for misleading indentation.
* Fix 'VlForkSync' redeclaration (#4277). [Krzysztof Bieganski, Antmicro Ltd]
* Fix processes that can outlive their parents (#4253). [Krzysztof Boronski, Antmicro Ltd]
* Fix duplicate fork names (#4295). [Ryszard Rozak, Antmicro Ltd]
* Fix splitting coroutines (#4297) (#4307). [Jiamin Zhu]
* Fix error when multiple duplicate DPI exports (#4301).
* Fix class reference assignment checking (#4296). [Ryszard Rozak, Antmicro Ltd]
* Fix handling of ref types in initial values of type parameters (#4304). [Ryszard Rozak, Antmicro Ltd]
* Fix comparison of string parameters (#4308). [Ryszard Rozak, Antmicro Ltd]
* Fix state update for always processes (#4311). [Aleksander Kiryk, Antmicro Ltd]
* Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344). [Krzysztof Bieganski, Antmicro Ltd]
* Fix implicit calls of base class constructors with optional arguments (#4319). [Ryszard Rozak, Antmicro Ltd]
* Fix propagation of process requirement (#4321). [Krzysztof Boroński]
* Fix unhandled overloads in V3InstrCount (#4324). [Krzysztof Boroński]
* Fix selects of static members (#4326). [Ryszard Rozak, Antmicro Ltd]
* Fix references to members of results of static methods (#4327). [Ryszard Rozak, Antmicro Ltd]
* Fix unique..with method on queues of class objects (#4328). [Ryszard Rozak, Antmicro Ltd]
* Fix queue slicing (#4329). [Aleksander Kiryk, Antmicro Ltd]
* Fix wildcard referring types (#4336) (#4342). [Aleksander Kiryk, Antmicro Ltd]
* Fix comparison of class objects (#4346). [Ryszard Rozak, Antmicro Ltd]
* Fix unexpected RefDType on assoc arrays (#4337). [Aleksander Kiryk, Antmicro Ltd]
* Fix cmake astgen for Rocky Linux 8.7 (#4343). [Julian Daube]
* Fix class timescale in class packages (#4348). [Krzysztof Bieganski, Antmicro Ltd]
* Fix string concatenations (#4354). [Ryszard Rozak, Antmicro Ltd]
* Fix unlinked task error from broken context (#4355) (#4402). [Aleksander Kiryk, Antmicro Ltd]
* Fix selects on unpacked structs (#4359). [Ryszard Rozak, Antmicro Ltd]
* Fix select operation on assoc array with wide keys (#4360). [Ryszard Rozak, Antmicro Ltd]
* Fix non-public methods with wide output (#4364). [Ryszard Rozak, Antmicro Ltd]
* Fix handling of super.new calls (#4366). [Ryszard Rozak, Antmicro Ltd]
* Fix assign to input var in methods (#4367). [Aleksander Kiryk, Antmicro Ltd]
* Fix VlProcess not found (#4368). [Aleksander Kiryk, Antmicro Ltd]
* Fix order of evaluation of function calls in statements (#4375). [Ryszard Rozak, Antmicro Ltd]
* Fix config_build.h issues (#4380) (#4381). [Andrew Miloradovsky]
Verilator 5.012 2023-06-13
==========================
+2 -2
View File
@@ -449,8 +449,8 @@ install-msg:
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
# autoheader might not change config_build.h.in, so touch it
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
# autoheader might not change config_package.h.in, so touch it
${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
cd ${srcdir} && autoheader
touch $@
Makefile: Makefile.in config.status $(IN_WILD)
+5 -3
View File
@@ -1,20 +1,22 @@
.. Github doesn't render images unless absolute URL
.. Do not know of a conditional tag, "only: github" nor "github display" works
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
:target: https://verilator.org
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
:target: https://www.gnu.org/licenses/lgpl-3.0]
:target: https://www.gnu.org/licenses/lgpl-3.0
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
:target: https://codecov.io/gh/verilator/verilator
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
Welcome to Verilator
+1
View File
@@ -440,6 +440,7 @@ detailed descriptions of these arguments.
--trace-params Enable tracing of parameters
--trace-structs Enable tracing structure names
--trace-threads <threads> Enable FST waveform creation on separate threads
--no-trace-top Do not emit traces for signals in the top module generated by verilator
--trace-underscore Enable tracing of _signals
-U<var> Undefine preprocessor define
--no-unlimited-stack Don't disable stack size limit
+2 -2
View File
@@ -71,8 +71,8 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
sudo apt-get install libsystemc libsystemc-dev
fi
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install mold ||
sudo apt-get install mold
sudo apt-get install bear mold ||
sudo apt-get install bear mold
fi
if [ "$COVERAGE" = 1 ]; then
yes yes | sudo cpan -fi Parallel::Forker
+2 -2
View File
@@ -10,11 +10,11 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.012 2023-06-13],
AC_INIT([Verilator],[5.016 2023-09-16],
[https://verilator.org],
[verilator],[https://verilator.org])
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_HEADERS(src/config_package.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
# Version
+12 -5
View File
@@ -13,7 +13,9 @@ Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte
Anthony Donlon
Arkadiusz Kozdra
Aylon Chaim Porat
Cameron Kirk
@@ -38,6 +40,8 @@ Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Frans Skarman
G-A. Kamendje
Garrett Smith
Geza Lore
Gianfranco Costamagna
@@ -46,7 +50,6 @@ Gökçe Aydos
Graham Rushton
Guokai Chen
Gustav Svensk
G-A. Kamendje
Harald Heckmann
Hennadii Chernyshchyk
Howard Su
@@ -54,9 +57,9 @@ Huang Rui
Huanghuang Zhou
HungMingWu
HyungKi Jeong
Ilya Barkov
Iru Cai
Ivan Vnučec
Ilya Barkov
Iztok Jeras
Jake Merdich
James Hanlon
@@ -71,17 +74,18 @@ Jeremy Bennett
Jesse Taube
Jevin Sweval
Jiacheng Qian
Jiamin Zhu
Jiuyang Liu
Joey Liu
John Coiner
John Demme
John Wehle
Jiamin Zhu
Jonathan Drolet
Jose Loyola
Joseph Nwabueze
Josep Sans
Joseph Nwabueze
Josh Redford
Julian Daube
Julie Schwartz
Julien Margetts
Kaleb Barrett
@@ -91,6 +95,7 @@ Keith Colbert
Kevin Kiningham
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
Krzysztof Boroński
Kuba Ober
Larry Doolittle
@@ -162,15 +167,17 @@ Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
Vito Gamberini
William D. Jones
Wilson Snyder
Xi Zhang
Yinan Xu
Yoda Lee
Yossi Nivin
Yu-Sheng Lin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu
Zhanglei Wang
Zixi Li
أحمد المحمودي
+5 -1
View File
@@ -18,12 +18,16 @@
RST2HTML = rst2html
PYTHON3 = python3
DOXYGEN = doxygen
SPHINXOPTS ?= -c guide
SPHINXBUILD ?= sphinx-build
SOURCEDIR = guide
BUILDDIR = _build
SPHINXOPTS ?= -c guide
ifneq ($(VERILATOR_ANALYTICS_ID),)
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
endif
######################################################################
.SUFFIXES:
-1
View File
@@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme_options = {
'analytics_id': 'G-D27B0C9QEB',
'logo_only': True,
'style_nav_header_background': "#45acf8", # Default is #2980B9
# 'canonical_url':
+37
View File
@@ -128,6 +128,43 @@ in the distribution. These headers use Doxygen comments, `///` and `//<`,
to indicate and document those functions that are part of the Verilated
public API.
Process-Level Clone APIs
--------------------------
Modern operating systems support process-level clone (a.k.a copying, forking)
with system call interfaces in C/C++, e.g., :code:`fork()` in Linux.
However, after cloning a parent process, some resources cannot be inherited
in the child process. For example, in POSIX systems, when you fork a process,
the child process inherits all the memory of the parent process. However,
only the thread that called fork is replicated in the child process. Other
threads are not.
Therefore, to support the process-level clone mechanisms, Verilator supports
:code:`prepareClone()` and :code:`atClone()` APIs to allow the user to manually
re-construct the model in the child process. The two APIs handle all necessary
resources required for releasing and re-initializing before and after cloning.
The two APIs are supported in the verilated models. Here is an example of usage
with Linux :code:`fork()` and :code:`pthread_atfork` APIs:
.. code-block:: C++
// static function pointers to fit pthread_atfork
static auto prepareClone = [](){ topp->prepareClone(); };
static auto atClone = [](){ topp->atClone(); };
// in main function, register the handlers:
pthread_atfork(prepareClone, atClone, atClone);
For better flexibility, you can also manually call the handlers before and
after :code:`fork()`.
With the process-level clone APIs, users can create process-level snapshots
for the verilated models. While the Verilator save/restore option provides
persistent and circuit-dependent snapshots, the process-level clone APIs
enable in-memory, circuit-transparent, and highly efficient snapshots.
Direct Programming Interface (DPI)
==================================
+4 -4
View File
@@ -17,10 +17,10 @@ C++11 compiler support
Verilator will require C++20 or newer compilers for both compiling
Verilator and compiling all Verilated models no sooner than January 2025.
Verilated_heavy.h
The legacy "verilated_heavy.h" include was replaced with just including
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
July 2022.
32-bit compiler support
Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
`-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
sooner than January 2024.
Option `-O<letter>`
The debug `-O<letter>` options have been replaced with
+47 -23
View File
@@ -125,9 +125,12 @@ Summary:
After generating the SystemC/C++ code, Verilator will invoke the
toolchain to build the model library (and executable when :vlopt:`--exe`
is also used). Verilator manages the build itself, and for this --build
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
:vlopt:`--build` cannot be specified when using :vlopt:`-E`,
:vlopt:`--dpi-hdr-only`, :vlopt:`--lint-only`, or :vlopt:`--xml-only`.
.. option:: --build-dep-bin <filename>
Rarely needed. When a dependency (.d) file is created, this filename
@@ -1314,6 +1317,7 @@ Summary:
This is not needed with standard designs with only one top. See also
:option:`MULTITOP` warning.
.. option:: --trace
Adds waveform tracing code to the model using VCD format. This overrides
@@ -1392,6 +1396,17 @@ Summary:
This option is accepted, but has absolutely no effect with
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
.. option:: --no-trace-top
Disables tracing for the input and output signals in the top wrapper which
Verilator adds to the design. The signals are still traced in the original
verilog top modules.
When combined with :option:`--main-top-name` set to "-" or when the name of
the top module is set to "" in its constructor, the generated trace file
will have the verilog top module as its root, rather than another module
added by Verilator.
.. option:: --trace-underscore
Enable tracing of signals or modules that start with an
@@ -1523,13 +1538,15 @@ Summary:
.. option:: -Wno-lint
Disable all lint-related warning messages, and all style warnings. This is
equivalent to ``-Wno-ALWCOMBORDER -Wno-ASCRANGE -Wno-BSSPACE -Wno-CASEINCOMPLETE
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
-Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-PINCONNECTEMPTY
-Wno-PINMISSING -Wno-STATICVAR -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-WIDTH`` plus the list shown for Wno-style.
Disable all lint-related warning messages, and all style warnings. This
is equivalent to ``-Wno-ALWCOMBORDER`` ``-Wno-ASCRANGE``
``-Wno-BSSPACE`` ``-Wno-CASEINCOMPLETE`` ``-Wno-CASEOVERLAP``
``-Wno-CASEX`` ``-Wno-CASTCONST`` ``-Wno-CASEWITHX`` ``-Wno-CMPCONST``
``-Wno-COLONPLUS`` ``-Wno-IMPLICIT`` ``-Wno-IMPLICITSTATIC``
``-Wno-PINCONNECTEMPTY`` ``-Wno-PINMISSING`` ``-Wno-STATICVAR``
``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN`` ``-Wno-UNSIGNED``
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
``-Wno-WIDTH``, plus the list shown for :vlopt:`-Wno-style`.
It is strongly recommended that you clean up your code rather than using this
option; it is only intended to be used when running test-cases of code
@@ -1537,12 +1554,13 @@ Summary:
.. option:: -Wno-style
Disable all code style related warning messages (note that by default, they are
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
-Wno-VARHIDDEN``.
Disable all code style related warning messages (note that by default,
they are already disabled). This is equivalent to ``-Wno-DECLFILENAME``
``-Wno-DEFPARAM`` ``-Wno-EOFNEWLINE`` ``-Wno-GENUNNAMED``
``-Wno-IMPORTSTAR`` ``-Wno-INCABSPATH`` ``-Wno-PINCONNECTEMPTY``
``-Wno-PINNOCONNECT`` ``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN``
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
``-Wno-VARHIDDEN``.
.. option:: -Wpedantic
@@ -1560,19 +1578,25 @@ Summary:
.. option:: -Wwarn-lint
Enable all lint-related warning messages (note that by default, they are already
enabled), but do not affect style messages. This is equivalent to
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``.
Enable all lint-related warning messages (note that by default, they are
already enabled), but do not affect style messages. This is equivalent
to ``-Wwarn-ALWCOMBORDER`` ``-Wwarn-ASCRANGE`` ``-Wwarn-BSSPACE``
``-Wwarn-CASEINCOMPLETE`` ``-Wwarn-CASEOVERLAP`` ``-Wwarn-CASEWITHX``
``-Wwarn-CASEX`` ``-Wwarn-CASTCONST`` ``-Wwarn-CMPCONST``
``-Wwarn-COLONPLUS`` ``-Wwarn-IMPLICIT`` ``-Wwarn-IMPLICITSTATIC``
``-Wwarn-LATCH`` ``-Wwarn-MISINDENT`` ``-Wwarn-NEWERSTD``
``-Wwarn-PINMISSING`` ``-Wwarn-REALCVT`` ``-Wwarn-STATICVAR``
``-Wwarn-UNSIGNED`` ``-Wwarn-WIDTHTRUNC`` ``-Wwarn-WIDTHEXPAND``
``-Wwarn-WIDTHXZEXPAND``.
.. option:: -Wwarn-style
Enable all code style-related warning messages. This is equivalent to
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
``-Wwarn-ASSIGNDLY`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM``
``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-INCABSPATH``
``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN``
``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL``
``-Wwarn-VARHIDDEN``.
.. option:: --x-assign 0
+1 -1
View File
@@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
#include "Vour.h"
#include "Vour_our.h"
cout << "clock is " << top->our->clk << endl;
std::cout << "clock is " << top->our->clk << std::endl;
In this example, clk is a bool you can read or set as any other variable.
+1 -1
View File
@@ -126,7 +126,7 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
sudo apt-get install libclang-dev yapf3
sudo apt-get install libclang-dev yapf3 bear
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
cpan install Pod::Perldoc
cpan install Parallel::Forker
+1 -1
View File
@@ -44,7 +44,7 @@ The best place to get started is to try the :ref:`Examples`.
.. [#] Verilog is defined by the `Institute of Electrical and Electronics
Engineers (IEEE) Standard for Verilog Hardware Description
Language`, Std. 1364, released in 1995, 2001, and 2005. The
Verilator documentation uses the shorthand, e.g., "IEEE 1394-2005",
Verilator documentation uses the shorthand, e.g., "IEEE 1364-2005",
to refer to the, e.g., 2005 version of this standard.
.. [#] SystemVerilog is defined by the `Institute of Electrical and
+1 -1
View File
@@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
documentation.
.. _Runtime Debugging
.. _Runtime Debugging:
Runtime Debugging
=================
+1 -1
View File
@@ -165,7 +165,7 @@ compiled on the different target system.
To support this, none of the files that Verilator produces will reference
any configure-generated build-system-specific files, such as
:file:`config.h` (which is renamed in Verilator to :file:`config_build.h`
:file:`config.h` (which is renamed in Verilator to :file:`config_package.h`
to reduce confusion.) The disadvantage of this approach is that
:file:`include/verilatedos.h` must self-detect the requirements of the
target system, rather than using configure.
+192 -1
View File
@@ -670,6 +670,52 @@ List Of Warnings
used as a clock.
.. option:: GENUNNAMED
Warns that a generate block was unnamed and "genblk" will be used per
IEEE.
The potential issue is that adding additional generate blocks will
renumber the assigned names, which may cause eventual problems with
synthesis constraints or other tools that depend on hierarchical paths
remaining consistent.
Blocks that are empty may not be reported with this warning, as no
scopes are created for empty blocks, so there is no harm in having them
unnamed.
Disabled by default as this is a code-style warning; it will simulate
correctly.
.. code-block:: sv
:linenos:
:emphasize-lines: 2
generate
if (PARAM == 1) begin //<--- Warning
end
Results in:
.. code-block::
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
To fix this assign a label (often with the naming convention prefix of
:code:`gen_` or :code:`g_`), for example:
.. code-block:: sv
:linenos:
:emphasize-lines: 2
generate
if (PARAM == 1) begin : gen_param_1 //<--- Repaired
end
Other tools with similar warnings: Verible's generate-label, "All
generate block statements must have a label."
.. option:: HIERBLOCK
Warns that the top module is marked as a hierarchy block by the
@@ -876,6 +922,102 @@ List Of Warnings
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: LIFETIME
Error when a variable is referenced in a process that can outlive the process
in which it was declared. This can happen when using 'fork..join_none' or
'fork..join_any' blocks, which spawn process that can outlive their parents.
This error occurs only when Verilator can't replace the reference with a
reference to copy of this variable, local to the forked process. For example:
.. code-block:: sv
:linenos:
:emphasize-lines: 3
task foo(int local_var);
fork
#10 local_var++;
#20 $display("local_var = %d", local_var);
join_none
endtask
In the example above 'local_var' exists only within scope of 'foo', once foo
finishes, the stack frame containing 'i' gets removed. However, the process
forked from foo continues, as it contains a delay. After 10 units of time
pass, this process attempts to modify 'local_var'. However, this variable no
longer exits. It can't be made local to the forked process upon spawning, because
it's modified and can be referenced somewhere else, for example in the other
forked process, that was delayed by 20 units of time in this example. Thus,
there's no viable stack allocation for it.
In order to fix it, if the intent is not to share the variable's state outside
of the process, then create a local copy of the variable.
For example:
.. code-block:: sv
:linenos:
:emphasize-lines: 4
task foo(int local_var);
fork
#10 begin
int forked_var = local_var;
forked_var++;
end
#20 begin
// Note that we are going to print the original value here,
// as `forked_var`is a local copy that was initialized while
// `foo` was still alive.
int forked_var = local_var;
$display("forked_var = %d", forked_var)
end
join_none
endtask
If you need to share its state, another strategy is to ensure it's allocated
statically:
.. code-block:: sv
:linenos:
:emphasize-lines: 1
int static_var;
task foo();
fork
#10 static_var++;
#20 $display("static_var = %d", static_var);
join_none
endtask
However, if you need to be able to instantiate at runtime, the solution would be to
wrap it in an object, since the forked process can hold a reference to that object
and ensure that the variable stays alive this way:
.. code-block:: sv
:linenos:
:emphasize-lines: 2
class Wrapper;
int m_var;
// Here we implicitly hold a reference to `this`
task foo();
fork
#10 m_var++;
#20 $display("this.m_var = %d", m_var);
join_none
endtask
endclass
// Here we explicitly hold a handle to an object
task bar(Wrapper wrapper);
fork
#10 wrapper.m_var++;
#20 $display("wrapper.m_var = %d", wrapper.m_var);
join_none
endtask
.. option:: LITENDIAN
@@ -897,6 +1039,51 @@ List Of Warnings
unsupported. Verilator uses only the typical delay value.
.. option:: MISINDENT
Warns that the indentation of a statement is misleading, suggesting the
statement is part of a previous :code:`if` or :code:`while` block while
it is not.
Verilator suppresses this check when there is an inconsistent mix of
spaces and tabs, as it cannot ensure the width of tabs. Verilator also
ignores blocks with :code:`begin`/:code:`end`, as the :code:`end`
visually indicates the earlier statement's end.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
For example
.. code-block:: sv
:linenos:
:emphasize-lines: 3
if (something)
statement_in_if;
statement_not_in_if; //<--- Warning
Results in:
.. code-block::
%Warning-MISINDENT: example.v:3:9: Misleading indentation
To fix this repair the indentation to match the correct earlier
statement, for example:
.. code-block:: sv
:linenos:
:emphasize-lines: 3
if (something)
statement_in_if;
statement_not_in_if; //<--- Repaired
Other tools with similar warnings: GCC -Wmisleading-indentation,
clang-tidy readability-misleading-indentation.
.. option:: MODDUP
.. TODO better example
@@ -1803,11 +1990,15 @@ List Of Warnings
.. code-block:: sv
wait(0); // Blocks forever
wait(1); // Blocks forever
Warns that a `wait` statement awaits a constant condition, which means it
either blocks forever or never blocks.
As a special case `wait(0)` with the literal constant `0` (as opposed to
something that elaborates to zero), does not warn, as it is presumed the
code is making the intent clear.
.. option:: WIDTH
+1 -1
View File
@@ -2123,7 +2123,7 @@ Or in a hand-written C++ wrapper:
::
#ifdef TEST_VERBOSE
cout << "Read a=" << a << endl;
std::cout << "Read a=" << a << std::endl;
#endif
A filename that should be used to check the output results is given with
+13 -1
View File
@@ -56,6 +56,7 @@ Das
Dcache
Deadman
Debacker
Deepa
Defparams
Denio
Deprecations
@@ -213,6 +214,7 @@ Maupin
Mdir
Mednick
Menküc
Michail
Michiels
Microsystems
Milanovic
@@ -246,6 +248,7 @@ Olof
Olofsson
Oyvind
PLI
Palaniappan
Patricio
Petr
Piechotka
@@ -261,6 +264,7 @@ Pre
Preprocess
Pretet
Pretl
Pringlemeir
Priyadharshini
Pullup
Pulver
@@ -275,6 +279,7 @@ Renga
Requin
Rodionov
Rolfe
Rontionov
Roodselaar
Runtime
Ruud
@@ -376,6 +381,7 @@ Wfuture
Whatson
Wildman
Wim
Wmisleading
Wno
Wojciech
Wolfel
@@ -410,7 +416,9 @@ architected
args
arrarys
assertOn
astgen
async
atClone
ato
atoi
autoconf
@@ -649,6 +657,7 @@ ish
isunbounded
isunknown
jobserver
jwoutersymatra
killua
lang
lcov
@@ -768,6 +777,7 @@ pragmas
pre
precisions
predefines
prepareClone
prepend
prepended
preprocess
@@ -905,6 +915,7 @@ undef
undefineall
undriven
ungetc
unhandled
uniquified
unistd
unlink
@@ -925,7 +936,9 @@ vc
vcd
vcddiff
vcoverage
vdhotre
vec
ventana
ver
verFiles
verible
@@ -971,4 +984,3 @@ ypq
yurivict
zdave
Øyvind
+1 -1
View File
@@ -4181,7 +4181,7 @@ if(!(isfeof=feof(xc->fh)))
if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM))
{
int sidx_skiplen_dummy = 0;
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_attr, &sidx_skiplen_dummy);
}
}
break;
+16 -2
View File
@@ -320,7 +320,7 @@ std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
static_assert(sizeof(m_state) == 16, "");
std::string result{"R00112233445566770011223344556677"};
for (int i = 0; i < sizeof(m_state); ++i) {
for (size_t i = 0; i < sizeof(m_state); ++i) {
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
}
@@ -332,7 +332,7 @@ void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
return;
}
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
for (int i = 0; i < sizeof(m_state); ++i) {
for (size_t i = 0; i < sizeof(m_state); ++i) {
stateCharsp[i]
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
}
@@ -1896,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
return std::string{destout, len};
}
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
std::string output;
for (const std::string& s : q) output += s;
return output;
}
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs;
const int32_t rhs_s = rhs; // To signed value
@@ -2602,6 +2608,14 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() {
return m_threadPool.get();
}
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
if (VL_UNLIKELY(m_threadPool)) m_threadPool.release();
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
+2
View File
@@ -568,6 +568,8 @@ public:
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
+4 -4
View File
@@ -210,8 +210,8 @@ private:
std::string result = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<result<<endl;
// std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
// << old << "\nch b=" << add << "\ncho=" << result << std::endl;
return result;
}
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
@@ -316,7 +316,7 @@ public:
valps[2] = page_default.c_str();
// Keys -> strings
std::string keys[VerilatedCovConst::MAX_KEYS];
std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
}
@@ -337,7 +337,7 @@ public:
const std::string key = keys[i];
if (!keys[i].empty()) {
const std::string val = valps[i];
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << std::endl;
m_insertp->m_keys[addKeynum] = valueIndex(key);
m_insertp->m_vals[addKeynum] = valueIndex(val);
++addKeynum;
+23
View File
@@ -1526,6 +1526,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
return owp;
}
template <typename T>
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
const int size = (lbits + elem_size - 1) / elem_size;
q.renew(size);
const QData mask = VL_MASK_Q(elem_size);
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
}
template <typename T>
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
IData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
return ret;
}
template <typename T>
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
QData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
return ret;
}
// Because concats are common and wide, it's valuable to always have a clean output.
// Thus we specify inputs must be clean, so we don't need to clean the output.
// Note the bit shifts are always constants, so the adds in these constify out.
@@ -2196,6 +2218,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
extern std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
VlWide<VL_WQ_WORDS_E> lw;
VL_SET_WQ(lw, lhs);
-31
View File
@@ -1,31 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2010-2023 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated old string and data-type header
///
/// This file is deprecated, and provided for backwards compatibility.
/// Include verilated.h instead.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_HEAVY_H_
#define VERILATOR_VERILATED_HEAVY_H_
#ifdef VL_NO_LEGACY
#error "Include <verilated.h> instead of <verilated_heavy.h>"
#endif
#include "verilated.h"
#endif // Guard
+7 -31
View File
@@ -86,36 +86,6 @@ public:
#endif
};
//===================================================================
// VlProcess stores metadata of running processes
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
public:
// TYPES
enum : int { // Type int for compatibility with $c
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4,
};
// CONSTRUCTORS
VlProcess()
: m_state{RUNNING} {}
// METHODS
int state() { return m_state; }
void state(int s) { m_state = s; }
};
using VlProcessRef = std::shared_ptr<VlProcess>;
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
//=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
@@ -132,6 +102,8 @@ class VlCoroutineHandle final {
public:
// CONSTRUCTORS
// Construct
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VlCoroutineHandle(VlProcessRef process)
: m_coro{nullptr}
, m_process{process} {
@@ -144,9 +116,11 @@ public:
if (m_process) m_process->state(VlProcess::WAITING);
}
// Move the handle, leaving a nullptr
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VlCoroutineHandle(VlCoroutineHandle&& moved)
: m_coro{std::exchange(moved.m_coro, nullptr)}
, m_process{moved.m_process}
, m_process{std::exchange(moved.m_process, nullptr)}
, m_fileline{moved.m_fileline} {}
// Destroy if the handle isn't null
~VlCoroutineHandle() {
@@ -163,6 +137,8 @@ public:
// Move the handle, leaving a null handle
auto& operator=(VlCoroutineHandle&& moved) {
m_coro = std::exchange(moved.m_coro, nullptr);
m_process = std::exchange(moved.m_process, nullptr);
m_fileline = moved.m_fileline;
return *this;
}
// Resume the coroutine if the handle isn't null and the process isn't killed
+91 -4
View File
@@ -73,6 +73,36 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
//===================================================================
// VlProcess stores metadata of running processes
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
public:
// TYPES
enum : int { // Type int for compatibility with $c
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4,
};
// CONSTRUCTORS
VlProcess()
: m_state{RUNNING} {}
// METHODS
int state() { return m_state; }
void state(int s) { m_state = s; }
};
using VlProcessRef = std::shared_ptr<VlProcess>;
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
//===================================================================
// Activity trigger vector
@@ -487,7 +517,7 @@ public:
template <typename Func>
VlQueue unique(Func with_func) const {
VlQueue out;
std::set<T_Value> saw;
std::set<decltype(with_func(0, m_deque[0]))> saw;
for (const auto& i : m_deque) {
const auto i_mapped = with_func(0, i);
const auto it = saw.find(i_mapped);
@@ -516,7 +546,7 @@ public:
VlQueue<IData> unique_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
std::unordered_set<T_Value> saw;
std::set<decltype(with_func(0, m_deque[0]))> saw;
for (const auto& i : m_deque) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
@@ -830,6 +860,22 @@ public:
}
return out;
}
template <typename Func>
VlQueue<T_Value> unique(Func with_func) const {
VlQueue<T_Value> out;
T_Key default_key;
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
std::set<WithType> saw;
for (const auto& i : m_map) {
const auto i_mapped = with_func(default_key, i.second);
const auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i.second);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
std::set<T_Key> saw;
@@ -843,6 +889,21 @@ public:
return out;
}
template <typename Func>
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
std::set<WithType> saw;
for (const auto& i : m_map) {
const auto i_mapped = with_func(i.first, i.second);
auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i.first);
}
}
return out;
}
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
@@ -903,6 +964,16 @@ public:
});
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
@@ -912,6 +983,16 @@ public:
});
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
}
T_Value r_sum() const {
T_Value out(0); // Type must have assignment operator
@@ -1457,8 +1538,14 @@ public:
// For 'if (ptr)...'
operator bool() const { return m_objp; }
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
bool operator==(const VlClassRef& rhs) const { return m_objp == rhs.m_objp; };
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; };
template <typename T_OtherClass>
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
return m_objp == rhs.m_objp;
};
template <typename T_OtherClass>
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
return m_objp != rhs.m_objp;
};
};
template <typename T, typename U>
+2 -2
View File
@@ -175,7 +175,7 @@ public:
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
: m_varp{varp}
, m_scopep{scopep}
, m_fullname{std::string{m_scopep->name()} + '.' + name()} {}
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
if (varp) {
m_varp = varp->m_varp;
@@ -2307,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
Verilated::runFlushCallbacks();
return 0;
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
}
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
+37 -16
View File
@@ -94,20 +94,6 @@
// Function requires a capability inbound (-fthread-safety)
#define VL_CAPABILITY(x) \
VL_CLANG_ATTR(capability(x))
// Function requires not having a capability inbound (-fthread-safety)
#define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES")) \
VL_CLANG_ATTR(requires_capability(x))
// Name of capability/lock (-fthread-safety)
#define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
VL_CLANG_ATTR(guarded_by(x))
// The data that the annotated pointer points to is protected by the given capability.
// The pointer itself is not protected.
// Allowed on: pointer data member. (-fthread-safety)
#define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
VL_CLANG_ATTR(pt_guarded_by(x))
// Name of mutex protecting this variable (-fthread-safety)
#define VL_EXCLUDES(x) \
VL_CLANG_ATTR(annotate("EXCLUDES")) \
@@ -119,6 +105,36 @@
// Allowed on: function, method. (-fthread-safety)
#define VL_RETURN_CAPABILITY(x) \
VL_CLANG_ATTR(lock_returned(x))
// Assert that capability is already held.
// Allowed on: function, method. (-fthread-safety)
#define VL_ASSERT_CAPABILITY(x) \
VL_CLANG_ATTR(assert_capability(x))
// Require mutex locks only in code units which work with enabled multi-threading.
#if !defined(VL_MT_DISABLED_CODE_UNIT)
// Function requires not having a capability inbound (-fthread-safety)
# define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES")) \
VL_CLANG_ATTR(requires_capability(x))
// Name of capability/lock (-fthread-safety)
# define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
VL_CLANG_ATTR(guarded_by(x))
// The data that the annotated pointer points to is protected by the given capability.
// The pointer itself is not protected.
// Allowed on: pointer data member. (-fthread-safety)
# define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
VL_CLANG_ATTR(pt_guarded_by(x))
#else
// Keep annotations for clang_check_attributes
# define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES"))
# define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY"))
# define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
#endif
// Defaults for unsupported compiler features
#ifndef VL_ATTR_ALWINLINE
@@ -426,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
Type(const Type& other) = delete; \
Type& operator=(const Type&) = delete
// Declare a class as unmovable; put after a private:
#define VL_UNMOVABLE(Type) \
Type(Type&& other) = delete; \
Type& operator=(Type&&) = delete
//=========================================================================
// Verilated function size macros
@@ -573,8 +594,8 @@ static inline double VL_ROUND(double n) {
//=========================================================================
// Stringify macros
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
#define VL_STRINGIFY2(x) #x
#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
#define VL_STRINGIFY2(...) #__VA_ARGS__
//=========================================================================
// Offset of field in type
+267 -50
View File
@@ -10,16 +10,36 @@ import argparse
import os
import sys
import shlex
from typing import Callable, Iterable, Optional, Union
from typing import Callable, Iterable, Optional, Union, TYPE_CHECKING
import dataclasses
from dataclasses import dataclass
import enum
from enum import Enum
import multiprocessing
import re
import tempfile
import clang.cindex
from clang.cindex import CursorKind, Index, TranslationUnitSaveError, TranslationUnitLoadError
from clang.cindex import (
Index,
TranslationUnitSaveError,
TranslationUnitLoadError,
CompilationDatabase,
)
if not TYPE_CHECKING:
from clang.cindex import CursorKind
else:
# Workaround for missing support for members defined out-of-class in Pylance:
# https://github.com/microsoft/pylance-release/issues/2365#issuecomment-1035803067
class CursorKindMeta(type):
def __getattr__(cls, name: str) -> clang.cindex.CursorKind:
return getattr(clang.cindex.CursorKind, name)
class CursorKind(clang.cindex.CursorKind, metaclass=CursorKindMeta):
pass
def fully_qualified_name(node):
@@ -60,6 +80,7 @@ class VlAnnotations:
stable_tree: bool = False
mt_safe_postinit: bool = False
mt_unsafe: bool = False
mt_disabled: bool = False
mt_unsafe_one: bool = False
pure: bool = False
guarded: bool = False
@@ -81,7 +102,7 @@ class VlAnnotations:
return self.stable_tree or self.mt_start
def is_mt_unsafe_call(self):
return self.mt_unsafe or self.mt_unsafe_one
return self.mt_unsafe or self.mt_unsafe_one or self.mt_disabled
def is_mt_safe_call(self):
return (not self.is_mt_unsafe_call()
@@ -131,6 +152,8 @@ class VlAnnotations:
result.mt_unsafe = True
elif node.displayname == "MT_UNSAFE_ONE":
result.mt_unsafe_one = True
elif node.displayname == "MT_DISABLED":
result.mt_disabled = True
elif node.displayname == "PURE":
result.pure = True
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
@@ -203,6 +226,19 @@ class FunctionInfo:
def copy(self, /, **changes):
return dataclasses.replace(self, **changes)
@staticmethod
def from_decl_file_line_and_refd_node(file: str, line: int,
refd: clang.cindex.Cursor,
annotations: VlAnnotations):
file = os.path.abspath(file)
refd = refd.canonical
assert refd is not None
name_parts = fully_qualified_name(refd)
usr = refd.get_usr()
ftype = FunctionType.from_node(refd)
return FunctionInfo(name_parts, usr, file, line, annotations, ftype)
@staticmethod
def from_node(node: clang.cindex.Cursor,
refd: Optional[clang.cindex.Cursor] = None,
@@ -228,6 +264,7 @@ class DiagnosticKind(Enum):
NON_PURE_CALL_IN_PURE_CTX = enum.auto()
NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto()
NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto()
MISSING_MT_DISABLED_ANNOTATION = enum.auto()
def __lt__(self, other):
return self.value < other.value
@@ -265,35 +302,142 @@ class CallAnnotationsValidator:
self._index = Index.create()
self._processed_headers: set[str] = set()
# Map key represents translation unit initial defines
# (from command line and source's lines before any include)
self._processed_headers: dict[str, set[str]] = {}
self._external_decls: dict[str, set[tuple[str, int]]] = {}
# Current context
self._main_source_file: str = ""
self._defines: dict[str, str] = {}
self._call_location: Optional[FunctionInfo] = None
self._caller: Optional[FunctionInfo] = None
self._level: int = 0
self._constructor_context: list[clang.cindex.Cursor] = []
self._level: int = 0
def is_mt_disabled_code_unit(self):
return "VL_MT_DISABLED_CODE_UNIT" in self._defines
def is_constructor_context(self):
return len(self._constructor_context) > 0
# Parses all lines in a form: `#define KEY VALUE` located before any `#include` line.
# The parsing is very simple, there is no support for line breaks, etc.
@staticmethod
def parse_initial_defines(source_file: str) -> dict[str, str]:
defs: dict[str, str] = {}
with open(source_file, "r", encoding="utf-8") as file:
for line in file:
line = line.strip()
match = re.fullmatch(
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
if match:
if match.group(1).startswith("define"):
key = match.group(2)
value = match.groups("1")[2]
defs[key] = value
elif match.group(1).startswith("include"):
break
return defs
@staticmethod
def filter_out_unsupported_compiler_args(
args: list[str]) -> tuple[list[str], dict[str, str]]:
filtered_args = []
defines = {}
args_iter = iter(args)
try:
while arg := next(args_iter):
# Skip positional arguments (input file name).
if not arg.startswith("-") and (arg.endswith(".cpp")
or arg.endswith(".c")
or arg.endswith(".h")):
continue
# Skipped options with separate value argument.
if arg in ["-o", "-T", "-MT", "-MQ", "-MF"
"-L"]:
next(args_iter)
continue
# Skipped options without separate value argument.
if arg == "-c" or arg.startswith("-W") or arg.startswith("-L"):
continue
# Preserved options with separate value argument.
if arg in [
"-x"
"-Xclang", "-I", "-isystem", "-iquote", "-include",
"-include-pch"
]:
filtered_args += [arg, next(args_iter)]
continue
kv_str = None
d_or_u = None
# Preserve define/undefine with separate value argument.
if arg in ["-D", "-U"]:
filtered_args.append(arg)
d_or_u = arg[1]
kv_str = next(args_iter)
filtered_args.append(kv_str)
# Preserve define/undefine without separate value argument.
elif arg[0:2] in ["-D", "-U"]:
filtered_args.append(arg)
kv_str = arg[2:]
d_or_u = arg[1]
# Preserve everything else.
else:
filtered_args.append(arg)
continue
# Keep track of defines for class' internal purposes.
key_value = kv_str.split("=", 1)
key = key_value[0]
val = "1" if len(key_value) == 1 else key_value[1]
if d_or_u == "D":
defines[key] = val
elif d_or_u == "U" and key in defines:
del defines[key]
except StopIteration:
pass
return (filtered_args, defines)
def compile_and_analyze_file(self, source_file: str,
compiler_args: list[str],
build_dir: Optional[str]):
filename = os.path.abspath(source_file)
initial_cwd = "."
filtered_args, defines = self.filter_out_unsupported_compiler_args(
compiler_args)
defines.update(self.parse_initial_defines(source_file))
if build_dir:
initial_cwd = os.getcwd()
os.chdir(build_dir)
translation_unit = self._index.parse(filename, compiler_args)
has_errors = False
for diag in translation_unit.diagnostics:
if diag.severity > clang.cindex.Diagnostic.Error:
has_errors = True
if translation_unit and not has_errors:
try:
translation_unit = self._index.parse(filename, filtered_args)
except TranslationUnitLoadError:
translation_unit = None
errors = []
if translation_unit:
for diag in translation_unit.diagnostics:
if diag.severity >= clang.cindex.Diagnostic.Error:
errors.append(str(diag))
if translation_unit and len(errors) == 0:
self._defines = defines
self._main_source_file = filename
self.process_translation_unit(translation_unit)
self._main_source_file = ""
self._defines = {}
else:
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
for error in errors:
print(f" {error}", file=sys.stderr)
if build_dir:
os.chdir(initial_cwd)
@@ -563,6 +707,18 @@ class CallAnnotationsValidator:
f" from: {node.location.file.name}:{node.location.line}")
return True
def process_function_declaration(self, node: clang.cindex.Cursor):
# Ignore declarations in main .cpp file
if node.location.file.name != self._main_source_file:
children = list(node.get_children())
annotations = VlAnnotations.from_nodes_list(children)
if not annotations.mt_disabled:
self._external_decls.setdefault(node.get_usr(), set()).add(
(str(node.location.file.name), int(node.location.line)))
return self.iterate_children(children, self.dispatch_node)
return self.iterate_children(node.get_children(), self.dispatch_node)
# Definition handling
def dispatch_node_inside_definition(self, node: clang.cindex.Cursor):
@@ -593,6 +749,18 @@ class CallAnnotationsValidator:
assert refd is not None
def_annotations = VlAnnotations.from_nodes_list(node_children)
# Implicitly mark definitions in VL_MT_DISABLED_CODE_UNIT .cpp files as
# VL_MT_DISABLED. Existence of the annotation on declarations in .h
# files is verified below.
# Also sets VL_REQUIRES, as this annotation is added together with
# explicit VL_MT_DISABLED.
if self.is_mt_disabled_code_unit():
if node.location.file.name == self._main_source_file:
annotations.mt_disabled = True
annotations.requires = True
if refd.location.file.name == self._main_source_file:
def_annotations.mt_disabled = True
def_annotations.requires = True
if not (def_annotations.is_empty() or def_annotations == annotations):
# Use definition's annotations for the diagnostic
@@ -605,12 +773,26 @@ class CallAnnotationsValidator:
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
# Use concatenation of definition and declaration annotations
# for callees validation.
# for calls validation.
self._caller = FunctionInfo.from_node(node, refd,
def_annotations | annotations)
prev_call_location = self._call_location
self._call_location = self._caller
if self.is_mt_disabled_code_unit():
# Report declarations of this functions that don't have MT_DISABLED annotation
# and are located in headers.
if node.location.file.name == self._main_source_file:
usr = node.get_usr()
declarations = self._external_decls.get(usr, set())
for file, line in declarations:
self.emit_diagnostic(
FunctionInfo.from_decl_file_line_and_refd_node(
file, line, refd, def_annotations),
DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION)
if declarations:
del self._external_decls[usr]
self.iterate_children(node_children,
self.dispatch_node_inside_definition)
@@ -622,34 +804,36 @@ class CallAnnotationsValidator:
# Nodes not located inside definition
def dispatch_node(self, node: clang.cindex.Cursor):
if node.is_definition() and node.kind in [
if node.kind in [
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
]:
return self.process_function_definition(node)
if node.is_definition() and node.kind in [
CursorKind.NAMESPACE, CursorKind.STRUCT_DECL,
CursorKind.UNION_DECL, CursorKind.CLASS_DECL
]:
return self.iterate_children(node.get_children(),
self.dispatch_node)
if node.is_definition():
return self.process_function_definition(node)
# else:
return self.process_function_declaration(node)
return self.iterate_children(node.get_children(), self.dispatch_node)
def process_translation_unit(
self, translation_unit: clang.cindex.TranslationUnit):
self._level += 1
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
concat_defines = '\n'.join(kv_defines)
# List of headers already processed in a TU with specified set of defines.
tu_processed_headers = self._processed_headers.setdefault(
concat_defines, set())
for child in translation_unit.cursor.get_children():
if self._is_ignored_top_level(child):
continue
if self._processed_headers:
if tu_processed_headers:
filename = os.path.abspath(child.location.file.name)
if filename in self._processed_headers:
if filename in tu_processed_headers:
continue
self.dispatch_node(child)
self._level -= 1
self._processed_headers.update([
tu_processed_headers.update([
os.path.abspath(str(hdr.source))
for hdr in translation_unit.get_includes()
])
@@ -711,34 +895,40 @@ def get_filter_funcs(verilator_root: str):
def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
initial_cwd = os.getcwd()
errors = []
try:
initial_cwd = os.getcwd()
os.chdir(compile_command.directory)
index = Index.create()
translation_unit = index.parse(compile_command.filename,
compile_command.args)
for diag in translation_unit.diagnostics:
if diag.severity > clang.cindex.Diagnostic.Error:
pch_file = None
break
else:
if diag.severity >= clang.cindex.Diagnostic.Error:
errors.append(str(diag))
if len(errors) == 0:
pch_file = os.path.join(
tmp_dir,
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
)
translation_unit.save(pch_file)
if pch_file:
return pch_file
except (TranslationUnitSaveError, TranslationUnitLoadError,
OSError) as exception:
print(f"%Warning: {exception}", file=sys.stderr)
finally:
os.chdir(initial_cwd)
if pch_file:
return pch_file
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError):
pass
print(
f"%Warning: Precompiling failed, skipping: {compile_command.filename}")
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
file=sys.stderr)
for error in errors:
print(f" {error}", file=sys.stderr)
return ""
@@ -761,7 +951,7 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
is_ignored_def, is_ignored_call)
for compile_command in ccl:
cav.compile_and_analyze_file(compile_command.filename,
compile_command.args + extra_args,
extra_args + compile_command.args,
compile_command.directory)
@@ -881,6 +1071,10 @@ class TopDownSummaryPrinter():
name += "is pure but calls non-pure function(s)"
elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX:
name += "is stable_tree but calls non-stable_tree or non-mtsafe"
elif func.reason == DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION:
name += ("defined in a file marked as " +
"VL_MT_DISABLED_CODE_UNIT has declaration(s) " +
"without VL_MT_DISABLED annotation")
else:
name += "for unknown reason (please add description)"
@@ -947,10 +1141,15 @@ def main():
type=int,
default=0,
help="Number of parallel jobs to use.")
parser.add_argument(
"--compile-commands-dir",
type=str,
default=None,
help="Path to directory containing compile_commands.json.")
parser.add_argument("--cxxflags",
type=str,
default=None,
help="Flags passed to clang++.")
help="Extra flags passed to clang++.")
parser.add_argument(
"--compilation-root",
type=str,
@@ -975,34 +1174,52 @@ def main():
cmdline.compilation_root = cmdline.verilator_root
verilator_root = os.path.abspath(cmdline.verilator_root)
compilation_root = os.path.abspath(cmdline.compilation_root)
default_compilation_root = os.path.abspath(cmdline.compilation_root)
compdb: Optional[CompilationDatabase] = None
if cmdline.compile_commands_dir:
compdb = CompilationDatabase.fromDirectory(
cmdline.compile_commands_dir)
default_cxx_flags = [
f"-I{verilator_root}/src",
f"-I{verilator_root}/include",
f"-I{verilator_root}/src/obj_opt",
"-fcoroutines-ts",
]
if cmdline.cxxflags is not None:
cxxflags = shlex.split(cmdline.cxxflags)
common_cxxflags = shlex.split(cmdline.cxxflags)
else:
cxxflags = default_cxx_flags
common_cxxflags = []
precompile_commands_list = []
if cmdline.precompile:
hdr_cxxflags = ['-xc++-header'] + cxxflags
hdr_cxxflags = ['-xc++-header'] + common_cxxflags
for refid, file in enumerate(cmdline.precompile):
filename = os.path.abspath(file)
compile_command = CompileCommand(refid, filename, hdr_cxxflags,
compilation_root)
default_compilation_root)
precompile_commands_list.append(compile_command)
compile_commands_list = []
for refid, file in enumerate(cmdline.file):
filename = os.path.abspath(file)
compile_command = CompileCommand(refid, filename, cxxflags,
compilation_root)
root = default_compilation_root
cxxflags = []
if compdb:
entry = compdb.getCompileCommands(filename)
entry_list = list(entry)
# Compilation database can contain multiple entries for single file,
# e.g. when it has been updated by appending new entries.
# Use last entry for the file, if it exists, as it is the newest one.
if len(entry_list) > 0:
last_entry = entry_list[-1]
root = last_entry.directory
entry_args = list(last_entry.arguments)
# First argument in compile_commands.json arguments list is
# compiler executable name/path. CIndex (libclang) always
# implicitly prepends executable name, so it shouldn't be passed
# here.
cxxflags = common_cxxflags + entry_args[1:]
else:
cxxflags = common_cxxflags[:]
compile_command = CompileCommand(refid, filename, cxxflags, root)
compile_commands_list.append(compile_command)
summary_printer = TopDownSummaryPrinter()
+1 -1
View File
@@ -1,6 +1,6 @@
*~
*.old
config_build.h
config_package.h
Makefile
Makefile_obj
.objcache*
+7 -2
View File
@@ -82,6 +82,7 @@ set(HEADERS
V3File.h
V3FileLine.h
V3Force.h
V3Fork.h
V3FunctionTraits.h
V3Gate.h
V3Global.h
@@ -150,6 +151,7 @@ set(HEADERS
V3Table.h
V3Task.h
V3ThreadPool.h
V3ThreadSafety.h
V3Timing.h
V3Trace.h
V3TraceDecl.h
@@ -227,6 +229,7 @@ set(COMMON_SOURCES
V3File.cpp
V3FileLine.cpp
V3Force.cpp
V3Fork.cpp
V3Gate.cpp
V3Global.cpp
V3Graph.cpp
@@ -322,7 +325,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV)
configure_file(config_build.h.in config_build.h @ONLY)
configure_file(config_package.h.in config_package.h @ONLY)
add_custom_command(
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
@@ -414,7 +417,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
MAIN_DEPENDENCY ${astgen_name}.cpp
DEPENDS ${ASTGEN} V3Ast.h
COMMAND ${PYTHON3} ARGS
${ASTGEN} -I"${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
)
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
endforeach()
@@ -488,11 +491,13 @@ target_include_directories(${verilator}
)
if (WIN32)
target_compile_options(${verilator} PRIVATE /bigobj)
target_compile_definitions(${verilator} PRIVATE
YY_NO_UNISTD_H
)
target_include_directories(${verilator} PRIVATE ../platform/win32)
target_link_libraries(${verilator} PRIVATE bcrypt psapi)
target_link_options(${verilator} PRIVATE /STACK:10000000)
endif()
install(TARGETS ${verilator})
+24 -5
View File
@@ -18,14 +18,13 @@
#### Start of system configuration section. ####
srcdir = @srcdir@
PYTHON3 = @PYTHON3@
EXEEXT = @EXEEXT@
PYTHON3 = @PYTHON3@
# VPATH only does sources; fix install_test's not creating ../bin
vpath %.h @srcdir@
#### End of system configuration section. ####
default: dbg opt
debug: dbg
optimize: opt
@@ -36,6 +35,26 @@ endif
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
ifeq (,$(wildcard obj_dbg/bear.o))
ifneq (, $(shell which bear 2>/dev/null))
BEAR := $(shell which bear)
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
else
# unsupported version
BEAR :=
endif
endif
else
BEAR := $(shell cat obj_dbg/bear.o)
endif
ifneq ($(BEAR),)
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
else
BEAR_OBJ_OPT :=
endif
#*********************************************************************
obj_opt:
@@ -62,8 +81,8 @@ endif
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
@@ -95,7 +114,7 @@ clean mostlyclean distclean maintainer-clean::
-rm -f .objcache*
distclean maintainer-clean::
-rm -f Makefile Makefile_obj config_build.h
-rm -f Makefile Makefile_obj config_package.h
maintainer-clean::
-rm -f config_rev.h
+2
View File
@@ -205,6 +205,7 @@ RAW_OBJS = \
V3File.o \
V3FileLine.o \
V3Force.o \
V3Fork.o \
V3Gate.o \
V3Global.o \
V3Graph.o \
@@ -288,6 +289,7 @@ NON_STANDALONE_HEADERS = \
V3AstNodeExpr.h \
V3AstNodeOther.h \
V3DfgVertices.h \
V3ThreadPool.h \
V3WidthCommit.h \
AST_DEFS := \
+1
View File
@@ -47,6 +47,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Extend V3GraphVertex class for use in latch detection graph
class LatchDetectGraphVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
public:
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
+2 -2
View File
@@ -157,7 +157,7 @@ private:
sentreep->unlinkFrBack();
if (m_procedurep) {
// To support this need queue of asserts to activate
nodep->v3error("Unsupported: Procedural concurent assertion with"
nodep->v3error("Unsupported: Procedural concurrent assertion with"
" clocking event inside always (IEEE 1800-2917 16.14.6)");
}
}
@@ -256,7 +256,7 @@ private:
ifp = nextifp;
} while (ifp);
AstNode* const newifp = nodep->cloneTree(false);
AstIf* const newifp = nodep->cloneTree(false);
const bool allow_none = nodep->unique0Pragma();
// Empty case means no property
+87 -21
View File
@@ -55,12 +55,28 @@ bool VNUser5InUse::s_userBusy = false;
int AstNodeDType::s_uniqueNum = 0;
//######################################################################
// V3AstType
// VNType
const VNTypeInfo VNType::typeInfoTable[] = {
#include "V3Ast__gen_type_info.h" // From ./astgen
};
std::ostream& operator<<(std::ostream& os, VNType rhs);
//######################################################################
// Creators
// VSelfPointerText
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("this");
string VSelfPointerText::protect(bool useSelfForThis, bool protect) const {
const string& sp
= useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString();
return VIdProtect::protectWordsIf(sp, protect);
}
//######################################################################
// AstNode
AstNode::AstNode(VNType t, FileLine* fl)
: m_type{t}
@@ -125,9 +141,7 @@ string AstNode::encodeNumber(int64_t num) {
}
}
string AstNode::nameProtect() const VL_MT_STABLE {
return VIdProtect::protectIf(name(), protect());
}
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
string AstNode::shortName() const {
@@ -285,7 +299,7 @@ string AstNode::vpiName(const string& namein) {
return pretty;
}
string AstNode::prettyTypeName() const VL_MT_STABLE {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyName() + "'";
}
@@ -1081,10 +1095,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
// private: Return true if the two trees are identical
if (!node1p && !node2p) return true;
if (!node1p || !node2p) return false;
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
return false;
}
if (node1p->type() != node2p->type()) return false;
UASSERT_OBJ(
(!node1p->dtypep() && !node2p->dtypep()) || (node1p->dtypep() && node2p->dtypep()), node1p,
"Comparison of a node with dtypep() with a node without dtypep()\n-node2=" << node2p);
if (node1p->dtypep() && !node1p->dtypep()->similarDType(node2p->dtypep())) return false;
if (!node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) return false;
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
@@ -1098,9 +1114,57 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
// private: Check a tree and children
UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent");
switch (this->type()) {
#include "V3Ast__gen_op_checks.h"
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
const VNTypeInfo& typeInfo = *type().typeInfo();
for (int i = 1; i <= 4; i++) {
AstNode* nodep = nullptr;
switch (i) {
case 1: nodep = op1p(); break;
case 2: nodep = op2p(); break;
case 3: nodep = op3p(); break;
case 4: nodep = op4p(); break;
default: this->v3fatalSrc("Bad case"); break;
}
const char* opName = typeInfo.m_opNamep[i - 1];
switch (typeInfo.m_opType[i - 1]) {
case VNTypeInfo::OP_UNUSED:
UASSERT_OBJ(!nodep, this, typeInfo.m_namep << " must not use " << opName << "()");
break;
case VNTypeInfo::OP_USED:
UASSERT_OBJ(nodep, this,
typeInfo.m_namep << " must have non nullptr " << opName << "()");
UASSERT_OBJ(!nodep->nextp(), this,
typeInfo.m_namep << "::" << opName
<< "() cannot have a non nullptr nextp()");
nodep->checkTreeIter(this);
break;
case VNTypeInfo::OP_LIST:
if (const AstNode* const headp = nodep) {
const AstNode* backp = this;
const AstNode* tailp = headp;
const AstNode* opp = headp;
do {
opp->checkTreeIter(backp);
UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp,
"Headtailp should be null in middle of lists");
backp = tailp = opp;
opp = opp->nextp();
} while (opp);
UASSERT_OBJ(headp->m_headtailp == tailp, headp,
"Tail in headtailp is inconsistent");
UASSERT_OBJ(tailp->m_headtailp == headp, tailp,
"Head in headtailp is inconsistent");
}
break;
case VNTypeInfo::OP_OPTIONAL:
if (nodep) {
UASSERT_OBJ(!nodep->nextp(), this,
typeInfo.m_namep << "::" << opName
<< "() cannot have a non-nullptr nextp()");
nodep->checkTreeIter(this);
}
break;
default: this->v3fatalSrc("Bad case"); break;
}
}
}
@@ -1289,12 +1353,6 @@ bool AstNode::isTreePureRecurse() const {
return true;
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
string AstNode::instanceStr() const {
// Max iterations before giving up on location search,
// in case we have some circular reference bug.
@@ -1319,9 +1377,9 @@ string AstNode::instanceStr() const {
return "";
}
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
if (!m_fileline) {
V3Error::s().v3errorEnd(str, instanceStr());
V3Error::v3errorEnd(str, instanceStr());
} else {
std::ostringstream nsstr;
nsstr << str.str();
@@ -1338,6 +1396,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s()
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
}
}
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
//======================================================================
// Data type conversion
@@ -1406,6 +1469,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
AstNodeDType* AstNode::findVoidDType() const {
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
}
AstNodeDType* AstNode::findStreamDType() const {
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
}
//######################################################################
// VNDeleter
+173 -97
View File
@@ -82,12 +82,36 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
// Ast<nodetypename>*' is returned.
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
// Same as VN_AS, but only checks the type in debug builds. Type checking is less critical in node
// child getters (the strongly-typed functions that wrap op*p pointers). This is because the op*p
// pointers are usually populated by code that already asserts the correct type. Having fewer type
// assertions yields better performance in release builds.
#ifdef VL_DEBUG
#define VN_DBG_AS(nodep, nodetypename) VN_AS(nodep, nodetypename)
#else
#define VN_DBG_AS(nodep, nodetypename) \
(AstNode::unsafePrivateAs<Ast##nodetypename, decltype(nodep)>(nodep))
#endif
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
//######################################################################
struct VNTypeInfo {
const char* m_namep;
enum uint8_t {
OP_UNUSED,
OP_USED,
OP_LIST,
OP_OPTIONAL,
} m_opType[4];
const char* m_opNamep[4];
};
class VNType final {
static const VNTypeInfo typeInfoTable[];
public:
#include "V3Ast__gen_type_enum.h" // From ./astgen
// Above include has:
@@ -100,6 +124,7 @@ public:
constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {}
explicit VNType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
const VNTypeInfo* typeInfo() const VL_MT_SAFE { return &typeInfoTable[m_e]; }
constexpr operator en() const VL_MT_SAFE { return m_e; }
};
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE {
@@ -791,6 +816,7 @@ public:
"BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
return names[m_e];
}
bool isParam() const { return m_e == GPARAM || m_e == LPARAM; }
bool isSignal() const {
return (m_e == WIRE || m_e == WREAL || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0
|| m_e == TRI1 || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
@@ -1157,30 +1183,34 @@ inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
class VUseType final {
public:
enum en : uint8_t {
IMP_INCLUDE, // Implementation (.cpp) needs an include
INT_INCLUDE, // Interface (.h) needs an include
IMP_FWD_CLASS, // Implementation (.cpp) needs a forward class declaration
INT_FWD_CLASS, // Interface (.h) needs a forward class declaration
// Enum values are compared with <, so order matters
INT_FWD_CLASS = 1 << 0, // Interface (.h) needs a forward class declaration
INT_INCLUDE = 1 << 1, // Interface (.h) needs an include
};
enum en m_e;
VUseType()
: m_e{IMP_FWD_CLASS} {}
: m_e{INT_FWD_CLASS} {}
// cppcheck-suppress noExplicitConstructor
constexpr VUseType(en _e)
: m_e{_e} {}
explicit VUseType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
bool isInclude() const { return m_e == IMP_INCLUDE || m_e == INT_INCLUDE; }
bool isFwdClass() const { return m_e == IMP_FWD_CLASS || m_e == INT_FWD_CLASS; }
constexpr operator en() const { return m_e; }
bool containsAny(VUseType other) { return m_e & other.m_e; }
const char* ascii() const {
static const char* const names[] = {"IMP_INC", "INT_INC", "IMP_FWD", "INT_FWD"};
return names[m_e];
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
return names[m_e - 1];
}
};
constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs.m_e == rhs.m_e; }
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
}
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
}
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
return os << rhs.ascii();
}
@@ -1221,6 +1251,44 @@ public:
~VBasicTypeKey() = default;
};
// ######################################################################
// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to
// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the
// string 'this', or 'vlSymsp->...'
class VSelfPointerText final {
private:
// STATIC MEMBERS
// Keep these in shared pointers to avoid branching for special cases
static const std::shared_ptr<const string> s_emptyp; // Holds ""
static const std::shared_ptr<const string> s_thisp; // Holds "this"
// MEMBERS
std::shared_ptr<const string> m_strp;
public:
// CONSTRUCTORS
class Empty {}; // for creator type-overload selection
VSelfPointerText(Empty)
: m_strp{s_emptyp} {}
class This {}; // for creator type-overload selection
VSelfPointerText(This)
: m_strp{s_thisp} {}
VSelfPointerText(This, const string& field)
: m_strp{std::make_shared<const string>("this->" + field)} {}
class VlSyms {}; // for creator type-overload selection
VSelfPointerText(VlSyms, const string& field)
: m_strp{std::make_shared<const string>("(&vlSymsp->" + field + ')')} {}
// METHODS
bool isEmpty() const { return m_strp == s_emptyp; }
bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; }
bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); }
string protect(bool useSelfForThis, bool protect) const;
const std::string& asString() const { return *m_strp; }
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
};
//######################################################################
// AstNUser - Generic base class for AST User nodes.
// - Also used to allow parameter passing up/down iterate calls
@@ -1303,55 +1371,55 @@ protected:
class VNUser1InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser2InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser3InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser4InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
class VNUser5InUse final : VNUserInUseBase {
protected:
friend class AstNode;
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
static bool s_userBusy; // Count is in use
public:
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
};
@@ -1634,7 +1702,7 @@ public:
const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName
AstNode* nextp() const VL_MT_STABLE { return m_nextp; }
AstNode* backp() const VL_MT_STABLE { return m_backp; }
AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow
AstNode* abovep() const; // Get parent node above, only for list head and tail
AstNode* op1p() const VL_MT_STABLE { return m_op1p; }
AstNode* op2p() const VL_MT_STABLE { return m_op2p; }
AstNode* op3p() const VL_MT_STABLE { return m_op3p; }
@@ -1686,7 +1754,7 @@ public:
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
// ACCESSORS
virtual string name() const VL_MT_STABLE { return ""; }
virtual string name() const { return ""; }
virtual string origName() const { return ""; }
virtual void name(const string& name) {
this->v3fatalSrc("name() called on object without name() method");
@@ -1694,7 +1762,7 @@ public:
virtual void tag(const string& text) {}
virtual string tag() const { return ""; }
virtual string verilogKwd() const { return ""; }
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
string nameProtect() const; // Name with --protect-id applied
string origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string dedotName(const string& namein); // Name with dots removed
@@ -1707,10 +1775,10 @@ public:
static string encodeName(const string& namein);
static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const VL_MT_STABLE { return prettyName(name()); }
string prettyName() const { return prettyName(name()); }
string prettyNameQ() const { return prettyNameQ(name()); }
// "VARREF" for error messages (NOT dtype's pretty name)
string prettyTypeName() const VL_MT_STABLE;
string prettyTypeName() const;
virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
void fileline(FileLine* fl) { m_fileline = fl; }
@@ -1742,74 +1810,74 @@ public:
inline bool isString() const VL_MT_STABLE;
// clang-format off
VNUser user1u() const VL_MT_STABLE {
VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy");
return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
}
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
int user1() const { return user1u().toInt(); }
void user1(int val) { user1u(VNUser{val}); }
int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; }
int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(int val) { user1u(VNUser{val}); }
int user1() const { return user1u().toInt(); }
int user1Inc(int val = 1) { int v = user1(); user1(v + val); return v; }
int user1SetOnce() { int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user2u() const VL_MT_STABLE {
VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy");
return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
}
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
int user2() const { return user2u().toInt(); }
void user2(int val) { user2u(VNUser{val}); }
int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; }
int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(int val) { user2u(VNUser{val}); }
int user2() const { return user2u().toInt(); }
int user2Inc(int val = 1) { int v = user2(); user2(v + val); return v; }
int user2SetOnce() { int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user3u() const VL_MT_STABLE {
VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy");
return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
}
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
int user3() const { return user3u().toInt(); }
void user3(int val) { user3u(VNUser{val}); }
int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; }
int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(int val) { user3u(VNUser{val}); }
int user3() const { return user3u().toInt(); }
int user3Inc(int val = 1) { int v = user3(); user3(v + val); return v; }
int user3SetOnce() { int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user4u() const VL_MT_STABLE {
VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy");
return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
}
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
int user4() const { return user4u().toInt(); }
void user4(int val) { user4u(VNUser{val}); }
int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; }
int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(int val) { user4u(VNUser{val}); }
int user4() const { return user4u().toInt(); }
int user4Inc(int val = 1) { int v = user4(); user4(v + val); return v; }
int user4SetOnce() { int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user5u() const VL_MT_STABLE {
VNUser user5u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy");
return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "user5p used without AstUserInUse");
return ((m_user5Cnt == VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
}
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; }
void user5p(void* userp) { user5u(VNUser{userp}); }
int user5() const { return user5u().toInt(); }
void user5(int val) { user5u(VNUser{val}); }
int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; }
int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
void user5u(const VNUser& user) { m_user5u = user; m_user5Cnt = VNUser5InUse::s_userCntGbl; }
void user5p(void* userp) { user5u(VNUser{userp}); }
void user5(int val) { user5u(VNUser{val}); }
int user5() const { return user5u().toInt(); }
int user5Inc(int val = 1) { int v = user5(); user5(v + val); return v; }
int user5SetOnce() { int v = user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
@@ -1867,6 +1935,7 @@ public:
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
void dtypeSetVoid() { dtypep(findVoidDType()); }
void dtypeSetStream() { dtypep(findStreamDType()); }
// Data type locators
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
@@ -1878,6 +1947,7 @@ public:
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findStreamDType() const;
AstNodeDType* findQueueIndexDType() const;
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
@@ -1888,9 +1958,9 @@ public:
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
// METHODS - dump and error
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
VL_REQUIRES(V3Error::s().m_mutex);
VL_RELEASE(V3Error::s().m_mutex);
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
return fileline()->warnContextPrimary();
}
@@ -1928,10 +1998,6 @@ public:
AstNode* belowp); // When calling, "this" is second argument
// METHODS - Iterate on a tree
// Clone or return nullptr if nullptr
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) {
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
}
AstNode* cloneTree(bool cloneNextLink); // Not const, as sets clonep() on original nodep
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
inline bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
@@ -2065,24 +2131,34 @@ public:
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
}
// For use via the VN_AS macro only
// For use via privateAs or the VN_DBG_AS macro only
template <typename T, typename E>
static T* privateAs(AstNode* nodep) VL_PURE {
static T* unsafePrivateAs(AstNode* nodep) VL_PURE {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<T*>(nodep);
}
template <typename T, typename E>
static const T* privateAs(const AstNode* nodep) VL_PURE {
static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
return reinterpret_cast<const T*>(nodep);
}
// For use via the VN_AS macro only
template <typename T, typename E>
static T* privateAs(AstNode* nodep) VL_PURE {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return reinterpret_cast<const T*>(nodep);
return unsafePrivateAs<T, E>(nodep);
}
template <typename T, typename E>
static const T* privateAs(const AstNode* nodep) VL_PURE {
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
<< "'");
return unsafePrivateAs<T, E>(nodep);
}
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
+8 -4
View File
@@ -154,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
}
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
// This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
varScopep(varscp);
}
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
bool AstVarRef::same(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
@@ -173,13 +176,14 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& (!selfPointer().empty() || !samep->selfPointer().empty())
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
&& varp()->name() == samep->varp()->name());
}
}
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
: ASTGEN_SUPER_VarXRef(fl, varp, access)
, m_name{varp->name()}
, m_dotted{dotted} {
dtypeFrom(varp);
}
+33 -13
View File
@@ -126,13 +126,13 @@ public:
const char* charIQWN() const {
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const VL_MT_STABLE;
string cType(const string& name, bool forFunc, bool isRef) const;
// Represents a C++ LiteralType? (can be constexpr)
bool isLiteralType() const VL_MT_STABLE;
private:
class CTypeRecursed;
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE;
CTypeRecursed cTypeRecurse(bool compound) const;
};
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
// Array data type, ie "some_dtype var_name [2:0]"
@@ -199,12 +199,9 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
// A struct or union; common handling
// @astgen op1 := membersp : List[AstMemberDType]
private:
// TYPES
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
// MEMBERS
string m_name; // Name from upper typedef, if any
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
MemberNameMap m_members;
AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
const int m_uniqueNum;
bool m_packed;
bool m_isFourstate = false; // V3Width computes
@@ -250,12 +247,6 @@ public:
static bool packedUnsup() { return true; }
void isFourstate(bool flag) { m_isFourstate = flag; }
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
void clearCache() { m_members.clear(); }
void repairMemberCache();
AstMemberDType* findMember(const string& name) const {
const auto it = m_members.find(name);
return (it == m_members.end()) ? nullptr : it->second;
}
static int lo() { return 0; }
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
@@ -1062,7 +1053,7 @@ private:
// Post-width typedefs are removed and point to type directly
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
string m_name; // Name of an AstTypedef
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Class/package in which it was defined
public:
AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl)
@@ -1077,6 +1068,7 @@ public:
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
: ASTGEN_SUPER_RefDType(fl) {
this->typeofp(typeofp);
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
}
ASTGEN_MEMBERS_AstRefDType;
// METHODS
@@ -1188,6 +1180,34 @@ public:
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
bool isCompound() const override { return true; }
};
class AstStreamDType final : public AstNodeDType {
// Stream data type, used only as data type of stream operations
// Should behave like AstPackArrayDType, but it doesn't have a size
public:
explicit AstStreamDType(FileLine* fl)
: ASTGEN_SUPER_StreamDType(fl) {
dtypep(this);
}
ASTGEN_MEMBERS_AstStreamDType;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
bool undead() const override { return true; }
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
AstNodeDType* virtRefDTypep() const override { return nullptr; }
void virtRefDTypep(AstNodeDType* nodep) override {}
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
// cppcheck-suppress csyleCast
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 1; }
bool isCompound() const override { return false; }
};
class AstUnsizedArrayDType final : public AstNodeDType {
// Unsized/open-range Array data type, ie "some_dtype var_name []"
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
+78 -49
View File
@@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
// @astgen op3 := pinsp : List[AstNodeExpr]
// @astgen op4 := scopeNamep : Optional[AstScopeName]
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the task
string m_name; // Name of variable
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
@@ -360,14 +360,8 @@ class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
// @astgen alias op2 := thenp
// @astgen alias op3 := elsep
protected:
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
: AstNodeTriop{t, fl, condp, thenp, elsep} {
if (thenp) {
dtypeFrom(thenp);
} else if (elsep) {
dtypeFrom(elsep);
}
}
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
AstNodeExpr* elsep);
public:
ASTGEN_MEMBERS_AstNodeCond;
@@ -449,21 +443,19 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
VAccess m_access; // Left hand side assignment
AstVar* m_varp; // [AfterLink] Pointer to variable itself
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
string m_name; // Name of variable
string m_selfPointer; // Output code object pointer (e.g.: 'this')
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
VSelfPointerText m_selfPointer
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
// pointer (e.g.: 'this')
protected:
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
: AstNodeExpr{t, fl}
, m_access{access}
, m_name{name} {
, m_access{access} {
varp(nullptr);
}
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
: AstNodeExpr{t, fl}
, m_access{access}
, m_name{name} {
, m_access{access} {
// May have varp==nullptr
this->varp(varp);
}
@@ -474,8 +466,6 @@ public:
const char* broken() const override;
int instrCount() const override { return widthInstrs(); }
void cloneRelink() override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void name(const string& name) override { m_name = name; }
VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
@@ -485,9 +475,11 @@ public:
}
AstVarScope* varScopep() const { return m_varScopep; }
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
string selfPointerProtect(bool useSelfForThis) const {
return selfPointer().protect(useSelfForThis, protect());
}
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
// Know no children, and hot function, so skip iterator for speed
@@ -593,21 +585,13 @@ class AstCMethodHard final : public AstNodeExpr {
string m_name; // Name of method
bool m_pure = false; // Pure optimizable
public:
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
AstNodeExpr* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl)
, m_name{name} {
// TODO: this constructor is exactly the same as the other, bar the ignored tag argument
this->fromp(fromp);
this->addPinsp(pinsp);
dtypep(nullptr); // V3Width will resolve
}
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
AstNodeExpr* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl)
, m_name{name} {
this->fromp(fromp);
this->addPinsp(pinsp);
setPurity();
}
ASTGEN_MEMBERS_AstCMethodHard;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
@@ -617,11 +601,13 @@ public:
return (m_name == asamep->m_name);
}
bool isPure() const override { return m_pure; }
void pure(bool flag) { m_pure = flag; }
int instrCount() const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
private:
void setPurity();
};
class AstCast final : public AstNodeExpr {
// Cast to appropriate data type
@@ -721,7 +707,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
private:
string m_name;
// Node not NodeModule to appease some early parser usage
AstNode* m_classOrPackageNodep; // Package hierarchy
AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
public:
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
AstPin* paramsp)
@@ -1024,6 +1010,34 @@ public:
// May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstCvtDynArrayToPacked final : public AstNodeExpr {
// Cast from dynamic queue data type to packed array
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstCvtPackedToDynArray final : public AstNodeExpr {
// Cast from packed array to dynamic queue data type
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstDot final : public AstNodeExpr {
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
// These are eliminated in the link stage
@@ -1062,7 +1076,7 @@ public:
};
class AstEnumItemRef final : public AstNodeExpr {
AstEnumItem* m_itemp; // [AfterLink] Pointer to item
AstNodeModule* m_classOrPackagep; // Package hierarchy
AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl)
@@ -1107,6 +1121,7 @@ public:
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isPure() const override { return false; }
bool same(const AstNode*) const override { return true; }
};
class AstFError final : public AstNodeExpr {
@@ -1313,7 +1328,9 @@ public:
bool cleanOut() const override { return true; }
};
class AstImplication final : public AstNodeExpr {
// Verilog |-> |=>
// Verilog Implication Operator
// Nonoverlapping "|=>"
// Overlapping "|->" (doesn't currently use this - might make new Ast type)
// @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := rhsp : AstNodeExpr
// @astgen op3 := sentreep : Optional[AstSenTree]
@@ -1428,6 +1445,7 @@ class AstMemberSel final : public AstNodeExpr {
// @astgen op1 := fromp : AstNodeExpr
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
string m_name;
VAccess m_access; // Read or write, as in AstNodeVarRef
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
public:
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
@@ -1448,10 +1466,12 @@ public:
void dump(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
bool same(const AstNode* samep) const override;
int instrCount() const override { return widthInstrs(); }
AstVar* varp() const { return m_varp; }
void varp(AstVar* nodep) { m_varp = nodep; }
@@ -1911,7 +1931,7 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstStructSel final : public AstNodeExpr {
// Unpacked struct member access
// Unpacked struct/union member access
// Parents: math|stmt
// Children: varref, math
// @astgen op1 := fromp : AstNodeExpr
@@ -1929,7 +1949,10 @@ public:
void name(const string& name) override { m_name = name; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool cleanOut() const override {
// Not a union
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
}
bool same(const AstNode* samep) const override {
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
return m_name == sp->m_name;
@@ -4022,16 +4045,19 @@ public:
// === AstNodeCCall ===
class AstCCall final : public AstNodeCCall {
// C++ function call
string m_selfPointer; // Output code object pointer (e.g.: 'this')
VSelfPointerText m_selfPointer
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
// pointer (e.g.: 'this')
public:
AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
ASTGEN_MEMBERS_AstCCall;
string selfPointer() const { return m_selfPointer; }
void selfPointer(const string& value) { m_selfPointer = value; }
string selfPointerProtect(bool useSelfForThis) const;
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
string selfPointerProtect(bool useSelfForThis) const {
return selfPointer().protect(useSelfForThis, protect());
}
};
class AstCMethodCall final : public AstNodeCCall {
// C++ method call
@@ -4671,7 +4697,7 @@ public:
ASTGEN_MEMBERS_AstCvtPackString;
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
string emitVerilog() override { return "%f$_CAST(%l)"; }
string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
@@ -5349,15 +5375,15 @@ public:
class AstVarRef final : public AstNodeVarRef {
// A reference to a variable (lvalue or rvalue)
public:
AstVarRef(FileLine* fl, const string& name, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
// This form only allowed post-link because output/wire compression may
// lead to deletion of AstVar's
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
// This form only allowed post-link (see above)
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
ASTGEN_MEMBERS_AstVarRef;
inline string name() const override; // * = Var name
void dump(std::ostream& str) const override;
const char* broken() const override;
bool same(const AstNode* samep) const override;
inline bool same(const AstVarRef* samep) const;
inline bool sameNoLvalue(AstVarRef* samep) const;
@@ -5369,14 +5395,17 @@ public:
class AstVarXRef final : public AstNodeVarRef {
// A VarRef to something in another module before AstScope.
// Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
string m_name;
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
, m_name{name}
, m_dotted{dotted} {}
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
ASTGEN_MEMBERS_AstVarXRef;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void dump(std::ostream& str) const override;
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
+78 -37
View File
@@ -32,7 +32,7 @@
class AstNodeBlock VL_NOT_FINAL : public AstNode {
// A Begin/fork block
// @astgen op1 := stmtsp : List[AstNode]
// @astgen op2 := stmtsp : List[AstNode]
// Parents: statement
private:
string m_name; // Name of block
@@ -81,12 +81,12 @@ private:
bool m_isConstructor : 1; // Class constructor
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
bool m_pure : 1; // DPI import pure (vs. virtual pure)
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
bool m_pureVirtual : 1; // Pure virtual
bool m_recursive : 1; // Recursive or part of recursion
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
bool m_fromStd : 1; // Part of std
bool m_needProcess : 1; // Implements part of a process that allocates std::process
VLifetime m_lifetime; // Lifetime
protected:
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
@@ -107,22 +107,25 @@ protected:
, m_isConstructor{false}
, m_isHideLocal{false}
, m_isHideProtected{false}
, m_pure{false}
, m_dpiPure{false}
, m_pureVirtual{false}
, m_recursive{false}
, m_underGenerate{false}
, m_virtual{false}
, m_fromStd{false} {
, m_needProcess{false} {
addStmtsp(stmtsp);
cname(name); // Might be overridden by dpi import/export
}
public:
ASTGEN_MEMBERS_AstNodeFTask;
virtual AstNodeFTask* cloneType(const string& name) = 0;
void dump(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool maybePointedTo() const override { return true; }
bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); }
bool isGateOptimizable() const override {
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
}
// {AstFunc only} op1 = Range output variable
void name(const string& name) override { m_name = name; }
string cname() const { return m_cname; }
@@ -162,8 +165,8 @@ public:
void isHideLocal(bool flag) { m_isHideLocal = flag; }
bool isHideProtected() const { return m_isHideProtected; }
void isHideProtected(bool flag) { m_isHideProtected = flag; }
void pure(bool flag) { m_pure = flag; }
bool pure() const { return m_pure; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool dpiPure() const { return m_dpiPure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void recursive(bool flag) { m_recursive = flag; }
@@ -172,11 +175,20 @@ public:
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void isFromStd(bool flag) { m_fromStd = flag; }
bool isFromStd() const { return m_fromStd; }
void setNeedProcess() { m_needProcess = true; }
bool needProcess() const { return m_needProcess; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
void propagateAttrFrom(const AstNodeFTask* fromp) {
// Creating a wrapper with e.g. cloneType(); preserve some attributes
classMethod(fromp->classMethod());
isHideLocal(fromp->isHideLocal());
isHideProtected(fromp->isHideProtected());
isVirtual(fromp->isVirtual());
lifetime(fromp->lifetime());
underGenerate(fromp->underGenerate());
}
};
class AstNodeFile VL_NOT_FINAL : public AstNode {
// Emitted Output file
@@ -578,7 +590,7 @@ private:
bool m_isInline : 1; // Inline function
bool m_isVirtual : 1; // Virtual function
bool m_entryPoint : 1; // User may call into this top level function
bool m_pure : 1; // Pure function
bool m_dpiPure : 1; // Pure DPI function
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
@@ -607,7 +619,7 @@ public:
m_isVirtual = false;
m_needProcess = false;
m_entryPoint = false;
m_pure = false;
m_dpiPure = false;
m_dpiContext = false;
m_dpiExportDispatcher = false;
m_dpiExportImpl = false;
@@ -678,8 +690,8 @@ public:
void setNeedProcess() { m_needProcess = true; }
bool entryPoint() const { return m_entryPoint; }
void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; }
bool dpiPure() const { return m_dpiPure; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
bool dpiExportDispatcher() const VL_MT_SAFE { return m_dpiExportDispatcher; }
@@ -699,6 +711,16 @@ public:
&& finalsp() == nullptr;
}
};
class AstCLocalScope final : public AstNode {
// Pack statements into an unnamed scope when generating C++
// @astgen op1 := stmtsp : List[AstNode]
public:
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
: ASTGEN_SUPER_CLocalScope(fl) {
this->addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCLocalScope;
};
class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE
@@ -1378,7 +1400,6 @@ public:
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string nameDotless() const;
string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); }
AstNodeModule* modp() const { return m_modp; }
//
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
@@ -1513,6 +1534,7 @@ class AstTypeTable final : public AstNode {
AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr;
AstStreamDType* m_streamp = nullptr;
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
@@ -1538,6 +1560,7 @@ public:
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
AstQueueDType* findQueueIndexDType(FileLine* fl);
AstVoidDType* findVoidDType(FileLine* fl);
AstStreamDType* findStreamDType(FileLine* fl);
void clearCache();
void repairCache();
void dump(std::ostream& str = std::cout) const override;
@@ -1810,7 +1833,7 @@ public:
string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool asRef = false) const VL_MT_STABLE;
bool asRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
@@ -1910,9 +1933,7 @@ public:
bool isClassMember() const { return varType() == VVarType::MEMBER; }
bool isStatementTemp() const { return (varType() == VVarType::STMTTEMP); }
bool isXTemp() const { return (varType() == VVarType::XTEMP); }
bool isParam() const VL_MT_SAFE {
return (varType() == VVarType::LPARAM || varType() == VVarType::GPARAM);
}
bool isParam() const { return varType().isParam(); }
bool isGParam() const { return (varType() == VVarType::GPARAM); }
bool isGenVar() const { return (varType() == VVarType::GENVAR); }
bool isBitLogic() const {
@@ -1953,18 +1974,25 @@ public:
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
void propagateAttrFrom(AstVar* fromp) {
void propagateAttrFrom(const AstVar* fromp) {
// This is getting connected to fromp; keep attributes
// Note the method below too
if (fromp->attrFileDescr()) attrFileDescr(true);
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
if (fromp->isContinuously()) isContinuously(true);
}
void propagateWrapAttrFrom(const AstVar* fromp) {
// Creating a function wrapper; keep attributes
propagateAttrFrom(fromp);
direction(fromp->direction());
declDirection(fromp->declDirection());
lifetime(fromp->lifetime());
}
bool gateMultiInputOptimizable() const {
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
return !isUsedClock();
}
void combineType(AstVar* typevarp) {
void combineType(const AstVar* typevarp) {
// This is same as typevarp (for combining input & reg decls)
// "this" is the input var. typevarp is the reg var.
propagateAttrFrom(typevarp);
@@ -2038,7 +2066,7 @@ public:
class AstBegin final : public AstNodeBlock {
// A Begin/end named block, only exists shortly after parsing until linking
// Parents: statement
// @astgen op2 := genforp : Optional[AstNode]
// @astgen op1 := genforp : Optional[AstNode]
bool m_generate; // Underneath a generate
const bool m_implied; // Not inserted by user
@@ -2057,6 +2085,7 @@ public:
};
class AstFork final : public AstNodeBlock {
// A fork named block
// @astgen op1 := initsp : List[AstNode]
// Parents: statement
// Children: statements
private:
@@ -2082,6 +2111,24 @@ public:
}
ASTGEN_MEMBERS_AstFunc;
bool hasDType() const override { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstFunc{fileline(), name, nullptr, nullptr};
}
};
class AstLet final : public AstNodeFTask {
// Verilog "let" statement
// Parents: MODULE
// stmtp is always a StmtExpr as Let always returns AstNodeExpr
public:
AstLet(FileLine* fl, const string& name)
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
ASTGEN_MEMBERS_AstLet;
bool hasDType() const override { return true; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
return nullptr;
}
AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; }
};
class AstProperty final : public AstNodeFTask {
// A property inside a module
@@ -2090,6 +2137,9 @@ public:
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstProperty;
bool hasDType() const override { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstProperty{fileline(), name, nullptr};
}
};
class AstTask final : public AstNodeFTask {
// A task inside a module
@@ -2097,6 +2147,9 @@ public:
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstTask;
AstNodeFTask* cloneType(const string& name) override {
return new AstTask{fileline(), name, nullptr};
}
};
// === AstNodeFile ===
@@ -2135,17 +2188,13 @@ public:
// === AstNodeModule ===
class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends]
// TYPES
using MemberNameMap = std::map<const std::string, AstNode*>;
// MEMBERS
MemberNameMap m_members; // Members or method children
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_virtual = false; // Virtual class
bool m_parameterized = false; // Parameterized class
void insertCache(AstNode* nodep);
public:
AstClass(FileLine* fl, const string& name)
@@ -2160,16 +2209,7 @@ public:
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
AstNode* membersp() const { return stmtsp(); }
void addMembersp(AstNode* nodep) {
insertCache(nodep);
addStmtsp(nodep);
}
void clearCache() { m_members.clear(); }
void repairCache();
AstNode* findMember(const string& name) const {
const auto it = m_members.find(name);
return (it == m_members.end()) ? nullptr : it->second;
}
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool isExtended() const { return m_extended; }
void isExtended(bool flag) { m_extended = flag; }
bool isInterfaceClass() const { return m_interfaceClass; }
@@ -2185,6 +2225,7 @@ public:
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
// Return the lowest class extended from, or this class
AstClass* baseMostClassp();
static bool isCacheableChild(const AstNode* nodep);
};
class AstClassPackage final : public AstNodeModule {
// The static information portion of a class (treated similarly to a package)
+116 -62
View File
@@ -25,6 +25,7 @@
#include "V3Hasher.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3Width.h"
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
@@ -63,7 +64,7 @@ void AstNodeFTaskRef::cloneRelink() {
bool AstNodeFTaskRef::isPure() const {
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
// pureness.
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->dpiPure();
}
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
@@ -83,12 +84,6 @@ void AstNodeVarRef::cloneRelink() {
}
}
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
}
void AstAddrOfCFunc::cloneRelink() {
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
}
@@ -103,29 +98,8 @@ int AstNodeSel::bitConst() const {
return (constp ? constp->toSInt() : 0);
}
void AstNodeUOrStructDType::repairMemberCache() {
clearCache();
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (m_members.find(itemp->name()) != m_members.end()) {
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
} else {
m_members.emplace(itemp->name(), itemp);
}
}
}
const char* AstNodeUOrStructDType::broken() const {
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
std::unordered_set<AstMemberDType*> exists;
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
exists.insert(itemp);
}
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
this->v3error("Internal: Structure member broken: " << it->first);
return "member broken";
}
}
return nullptr;
}
@@ -147,14 +121,22 @@ const char* AstNodeCCall::broken() const {
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
return nullptr;
}
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); }
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
const string& sp
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
return VIdProtect::protectWordsIf(sp, protect());
AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
AstNodeExpr* elsep)
: AstNodeTriop{t, fl, condp, thenp, elsep} {
UASSERT_OBJ(thenp, this, "No thenp expression");
UASSERT_OBJ(elsep, this, "No elsep expression");
if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) {
// Get the most-deriving class type that both arguments can be casted to.
AstNodeDType* const commonClassTypep = V3Width::getCommonClassTypep(thenp, elsep);
UASSERT_OBJ(commonClassTypep, this, "No common base class exists");
dtypep(commonClassTypep);
} else {
dtypeFrom(thenp);
}
}
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
const V3Number& ths) {
if (lhs.isNeqZero()) {
@@ -399,7 +381,7 @@ string AstVar::verilogKwd() const {
}
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
bool asRef) const VL_MT_STABLE {
bool asRef) const {
UASSERT_OBJ(!forReturn, this,
"Internal data is never passed as return, but as first argument");
string ostatic;
@@ -716,12 +698,12 @@ public:
}
};
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_STABLE {
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_MT_STABLE {
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
// Legacy compound argument currently just passed through and unused
CTypeRecursed info;
@@ -1071,6 +1053,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
return m_voidp;
}
AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) {
if (VL_UNLIKELY(!m_streamp)) {
AstStreamDType* const newp = new AstStreamDType{fl};
addTypesp(newp);
m_streamp = newp;
}
return m_streamp;
}
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
if (VL_UNLIKELY(!m_queueIndexp)) {
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
@@ -1418,29 +1409,10 @@ const char* AstClassPackage::broken() const {
void AstClassPackage::cloneRelink() {
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
}
void AstClass::insertCache(AstNode* nodep) {
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
} else {
m_members.emplace(nodep->name(), nodep);
}
}
}
void AstClass::repairCache() {
clearCache();
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
insertCache(blockp);
}
} else {
insertCache(itemp);
}
}
bool AstClass::isCacheableChild(const AstNode* nodep) {
return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc));
}
AstClass* AstClass::baseMostClassp() {
AstClass* basep = this;
@@ -1667,6 +1639,12 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
}
bool AstMemberSel::same(const AstNode* samep) const {
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
&& name() == sp->name() && varp()->same(sp->varp());
}
void AstMemberSel::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " -> ";
@@ -2055,6 +2033,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
}
void AstStreamDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "stream";
}
void AstVarScope::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isTrace()) str << " [T]";
@@ -2097,6 +2079,10 @@ void AstVarRef::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
const char* AstVarRef::broken() const {
BROKEN_RTN(!varp());
return AstNodeVarRef::broken();
}
bool AstVarRef::same(const AstNode* samep) const {
return same(static_cast<const AstVarRef*>(samep));
}
@@ -2301,7 +2287,7 @@ void AstCFile::dump(std::ostream& str) const {
void AstCFunc::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (slow()) str << " [SLOW]";
if (pure()) str << " [PURE]";
if (dpiPure()) str << " [DPIPURE]";
if (isStatic()) str << " [STATIC]";
if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExportImpl()) str << " [DPIEI]";
@@ -2331,7 +2317,7 @@ void AstCAwait::dump(std::ostream& str) const {
int AstCMethodHard::instrCount() const {
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
// TODO: add a more structured description of library methods, rather than using string
// matching. See #3715.
// matching. See issue #3715.
if (basicp->isTriggerVec() && m_name == "word") {
// This is an important special case for scheduling so we compute it precisely,
// it is simply a load.
@@ -2340,6 +2326,74 @@ int AstCMethodHard::instrCount() const {
}
return 0;
}
void AstCMethodHard::setPurity() {
static const std::map<std::string, bool> isPureMethod{{"andNot", false},
{"any", true},
{"assign", false},
{"at", true},
{"atBack", true},
{"awaitingCurrentTime", true},
{"clear", false},
{"clearFired", false},
{"commit", false},
{"delay", false},
{"done", false},
{"erase", false},
{"evaluate", false},
{"evaluation", false},
{"exists", true},
{"find", true},
{"find_first", true},
{"find_first_index", true},
{"find_index", true},
{"find_last", true},
{"find_last_index", true},
{"fire", false},
{"first", false},
{"init", false},
{"insert", false},
{"isFired", true},
{"isTriggered", true},
{"join", false},
{"last", false},
{"max", true},
{"min", true},
{"neq", true},
{"next", false},
{"pop", false},
{"pop_back", false},
{"pop_front", false},
{"prev", false},
{"push", false},
{"push_back", false},
{"push_front", false},
{"r_and", true},
{"r_or", true},
{"r_product", true},
{"r_sum", true},
{"r_xor", true},
{"renew", false},
{"renew_copy", false},
{"resume", false},
{"reverse", false},
{"rsort", false},
{"set", false},
{"shuffle", false},
{"size", true},
{"slice", true},
{"sliceBackBack", true},
{"sliceFrontBack", true},
{"sort", false},
{"thisOr", false},
{"trigger", false},
{"unique", true},
{"unique_index", true},
{"word", true}};
auto isPureIt = isPureMethod.find(name());
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
m_pure = isPureIt->second;
}
const char* AstCFunc::broken() const {
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
return nullptr;
+4 -38
View File
@@ -40,39 +40,6 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class RenameStaticVisitor final : public VNVisitor {
private:
// STATE
const std::set<AstVar*>& m_staticFuncVarsr; // Static variables from m_ftaskp
AstNodeFTask* const m_ftaskp; // Current function/task
// VISITORS
void visit(AstVarRef* nodep) override {
const auto it = m_staticFuncVarsr.find(nodep->varp());
if (it != m_staticFuncVarsr.end()) nodep->name((*it)->name());
iterateChildren(nodep);
}
void visit(AstInitialStatic* nodep) override {
iterateChildren(nodep);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
RenameStaticVisitor(std::set<AstVar*>& staticFuncVars, AstNodeFTask* ftaskp, AstNode* nodep)
: m_staticFuncVarsr(staticFuncVars)
, m_ftaskp(ftaskp) {
iterateChildren(nodep);
}
~RenameStaticVisitor() override = default;
};
//######################################################################
class BeginState final {
private:
// NODE STATE
@@ -105,7 +72,6 @@ private:
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
int m_ifDepth = 0; // Current if depth
bool m_keepBegins = false; // True if begins should not be inlined
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
// METHODS
@@ -212,7 +178,10 @@ private:
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep};
nodep->foreach([&](AstInitialStatic* const initp) {
initp->unlinkFrBack();
m_ftaskp->addHereThisAsNext(initp);
});
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
@@ -222,7 +191,6 @@ private:
nodep->addStmtsp(m_liftedp);
m_liftedp = nullptr;
}
m_staticFuncVars.clear();
m_ftaskp = nullptr;
}
}
@@ -266,7 +234,6 @@ private:
nodep->name(newName);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
m_staticFuncVars.insert(nodep);
nodep->funcLocal(false);
} else if (m_unnamedScope != "") {
// Rename it
@@ -377,7 +344,6 @@ private:
void visit(AstVarRef* nodep) override {
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef " << nodep << endl);
nodep->name(nodep->varp()->name());
}
iterateChildren(nodep);
}
+31 -12
View File
@@ -147,22 +147,25 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka
// Check every node in tree
class BrokenCheckVisitor final : public VNVisitorConst {
bool m_inScope = false; // Under AstScope
// Constants for marking we are under/not under a node
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
// Current CFunc, if any
const AstCFunc* m_cfuncp = nullptr;
// STATE - across all visitors
// All local variables declared in current function
std::unordered_set<const AstVar*> m_localVars;
std::set<const AstVar*> m_localVars;
// Variable references in current function that do not reference an in-scope local
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
// Local variables declared in the scope of the current statement
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
// STATE - for current visit position (use VL_RESTORER)
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
bool m_inScope = false; // Under AstScope
std::set<std::string> m_cFuncNames; // CFunc by name in current class/module
private:
// METHODS
static void checkWidthMin(const AstNode* nodep) {
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
@@ -220,6 +223,7 @@ private:
}
return false;
}
// VISITORS
void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
@@ -235,10 +239,20 @@ private:
}
void visit(AstScope* nodep) override {
VL_RESTORER(m_inScope);
{
m_inScope = true;
processAndIterate(nodep);
}
m_inScope = true;
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeUOrStructDType* nodep) override {
VL_RESTORER(m_cFuncNames);
m_cFuncNames.clear();
processAndIterate(nodep);
}
void visit(AstNodeVarRef* nodep) override {
processAndIterate(nodep);
@@ -260,12 +274,19 @@ private:
}
void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
m_localVars.clear();
m_suspectRefs.clear();
m_localsStack.clear();
pushLocalScope();
// Check for duplicate names, otherwise linker might just ignore them
string nameArgs = nodep->name();
if (!nodep->argTypes().empty()) nameArgs += "(" + nodep->argTypes() + ")";
UASSERT_OBJ(m_cFuncNames.emplace(nameArgs).second, nodep,
"Duplicate cfunc name: '" << nameArgs << "'");
processAndIterate(nodep);
// Check suspect references are all to non-locals
@@ -273,8 +294,6 @@ private:
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
"Local variable not in scope where referenced: " << pair.first);
}
m_cfuncp = nullptr;
}
void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
+2 -2
View File
@@ -117,7 +117,7 @@ public:
callp->argTypes("vlSymsp");
} else {
if (m_type.isCoverage()) callp->argTypes("first");
callp->selfPointer("this");
callp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
}
rootFuncp->addStmtsp(callp->makeStmt());
}
@@ -229,7 +229,7 @@ void V3CCtors::evalAsserts() {
// if (signal & CONST(upper_non_clean_mask)) { fail; }
AstVarRef* const vrefp
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
vrefp->selfPointer("this");
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
AstNodeExpr* newp = vrefp;
if (varp->isWide()) {
newp = new AstWordSel{
+19 -32
View File
@@ -28,9 +28,11 @@
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3FileLine.h"
#include "V3Global.h"
#include <set>
#include <map>
#include <utility>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -46,52 +48,31 @@ class CUseVisitor final : public VNVisitor {
// MEMBERS
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
bool m_dtypesImplOnly = false;
std::map<std::string, std::pair<FileLine*, VUseType>> m_didUse; // What we already used
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_dtypesImplOnly
&& (useType == VUseType::INT_INCLUDE || useType == VUseType::INT_FWD_CLASS))
return;
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtsp(newp);
UINFO(8, "Insert " << newp << endl);
auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType));
if (e.second || ((e.first->second.second & useType) != useType)) {
e.first->second.second = e.first->second.second | useType;
}
}
// VISITORS
void visit(AstClassRefDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
}
void visit(AstCFunc* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->user1SetOnce()) return;
iterateAndNextNull(nodep->argsp());
{
VL_RESTORER(m_dtypesImplOnly);
m_dtypesImplOnly = true;
iterateAndNextNull(nodep->initsp());
iterateAndNextNull(nodep->stmtsp());
iterateAndNextNull(nodep->finalsp());
}
iterateAndNextNull(nodep->stmtsp());
}
void visit(AstCCall* nodep) override { return; }
void visit(AstCReturn* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (m_dtypesImplOnly) {
for (AstNode* exprp = nodep->op1p(); exprp; exprp = exprp->nextp()) {
if (exprp->dtypep()) iterate(exprp->dtypep());
}
} else {
iterateChildren(nodep);
}
UASSERT(!nodep->user1SetOnce(), "Visited same return twice.");
iterate(nodep->lhsp()->dtypep());
}
void visit(AstNodeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
@@ -106,7 +87,7 @@ class CUseVisitor final : public VNVisitor {
}
void visit(AstNode* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
if (nodep->dtypep()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
void visit(AstCell* nodep) override {
@@ -121,6 +102,12 @@ public:
explicit CUseVisitor(AstNodeModule* modp)
: m_modp(modp) {
iterate(modp);
for (auto& used : m_didUse) {
AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first};
m_modp->addStmtsp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
+83 -40
View File
@@ -139,32 +139,42 @@ private:
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
// METHODS
bool caseIsEnumComplete(AstCase* nodep, uint32_t numCases) {
// Return true if case is across an enum, and every value in the case
// statement corresponds to one of the enum values
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return false;
AstEnumDType* const enumDtp
//! Determine whether we should check case items are complete
//! @return Enum's dtype if should check, nullptr if shouldn't
const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return nullptr;
const AstEnumDType* const enumDtp
= VN_CAST(nodep->exprp()->dtypep()->skipRefToEnump(), EnumDType);
if (!enumDtp) return false; // Case isn't enum
AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
if (!basicp) return false; // Not simple type (perhaps IEEE illegal)
if (basicp->width() > 32) return false;
// Find all case values into a set
std::set<uint32_t> caseSet;
for (uint32_t i = 0; i < numCases; ++i) { // All case items
if (m_valueItem[i]) caseSet.emplace(i);
}
// Find all enum values into a set
std::set<uint32_t> enumSet;
for (AstEnumItem* itemp = enumDtp->itemsp(); itemp;
if (!enumDtp) return nullptr; // Case isn't enum
const AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
if (!basicp) return nullptr; // Not simple type (perhaps IEEE illegal)
if (basicp->width() > 32) return nullptr;
return enumDtp;
}
//! @return True if case items are complete, false if there are uncovered enums
bool checkCaseEnumComplete(const AstCase* const nodep, const AstEnumDType* const dtype) {
const uint32_t numCases = 1UL << m_caseWidth;
for (AstEnumItem* itemp = dtype->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), EnumItem)) {
AstConst* const econstp = VN_AS(itemp->valuep(), Const);
const uint32_t val = econstp->toUInt();
// UINFO(9, "Complete enum item " << val << ": " << itemp << endl);
enumSet.emplace(val);
V3Number nummask{itemp, econstp->width()};
nummask.opBitsNonX(econstp->num());
const uint32_t mask = nummask.toUInt();
V3Number numval{itemp, econstp->width()};
numval.opBitsOne(econstp->num());
const uint32_t val = numval.toUInt();
for (uint32_t i = 0; i < numCases; ++i) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE, "Enum item " << itemp->prettyNameQ()
<< " not covered by case\n");
return false; // enum has uncovered value by case items
}
}
}
}
// If sets match, all covered
return (caseSet == enumSet);
return true; // enum is fully covered
}
bool isCaseTreeFast(AstCase* nodep) {
int width = 0;
@@ -193,6 +203,8 @@ private:
// We can cheat and use uint32_t's because we only support narrow case's
bool reportedOverlap = false;
bool reportedSubcase = false;
bool hasDefaultCase = false;
std::map<AstNode*, AstCaseItem*> caseItemMap; // case condition -> case item
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
@@ -202,6 +214,7 @@ private:
if (neverItem(nodep, iconstp)) {
// X in casez can't ever be executed
} else {
const bool isCondWildcard = iconstp->num().isAnyXZ();
V3Number nummask{itemp, iconstp->width()};
nummask.opBitsNonX(iconstp->num());
const uint32_t mask = nummask.toUInt();
@@ -210,16 +223,17 @@ private:
const uint32_t val = numval.toUInt();
uint32_t firstOverlap = 0;
bool foundOverlap = false;
AstNode* overlappedCondp = nullptr;
bool foundHit = false;
for (uint32_t i = 0; i < numCases; ++i) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
m_valueItem[i] = icondp;
caseItemMap[icondp] = itemp;
foundHit = true;
} else if (!foundOverlap) {
} else if (!overlappedCondp) {
firstOverlap = i;
foundOverlap = true;
overlappedCondp = m_valueItem[i];
m_caseNoOverlapsAllCovered = false;
}
}
@@ -227,9 +241,19 @@ private:
if (!nodep->priorityPragma()) {
// If this case statement doesn't have the priority
// keyword, we want to warn on any overlap.
if (!reportedOverlap && foundOverlap) {
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
<< std::hex << firstOverlap << ")");
if (!reportedOverlap && overlappedCondp) {
std::ostringstream examplePattern;
if (isCondWildcard) {
examplePattern << " (example pattern 0x" << std::hex
<< firstOverlap << ")";
}
icondp->v3warn(CASEOVERLAP,
"Case conditions overlap"
<< examplePattern.str() << "\n"
<< icondp->warnContextPrimary() << '\n'
<< overlappedCondp->warnOther()
<< "... Location of overlapping condition\n"
<< overlappedCondp->warnContextSecondary());
reportedOverlap = true;
}
} else {
@@ -240,7 +264,11 @@ private:
if (!reportedSubcase && !foundHit) {
icondp->v3warn(CASEOVERLAP,
"Case item ignored: every matching value is covered "
"by an earlier item");
"by an earlier condition\n"
<< icondp->warnContextPrimary() << '\n'
<< overlappedCondp->warnOther()
<< "... Location of previous condition\n"
<< overlappedCondp->warnContextPrimary());
reportedSubcase = true;
}
}
@@ -251,17 +279,28 @@ private:
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) m_valueItem[i] = itemp;
}
caseItemMap[itemp] = itemp;
hasDefaultCase = true;
}
}
if (!caseIsEnumComplete(nodep, numCases)) {
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
"(example pattern 0x"
<< std::hex << i << ")");
if (!hasDefaultCase) {
const AstEnumDType* const dtype = getEnumCompletionCheckDType(nodep);
if (dtype) {
if (!checkCaseEnumComplete(nodep, dtype)) {
// checkCaseEnumComplete has already warned of incompletion
m_caseNoOverlapsAllCovered = false;
return false;
}
} else {
for (uint32_t i = 0; i < numCases; ++i) {
if (!m_valueItem[i]) { // has uncovered case
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
"(example pattern 0x"
<< std::hex << i << ")");
m_caseNoOverlapsAllCovered = false;
return false;
}
}
}
}
@@ -274,8 +313,10 @@ private:
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
for (uint32_t i = 0; i < numCases; ++i) {
if (AstCaseItem* const itemp = VN_AS(m_valueItem[i], CaseItem)) {
m_valueItem[i] = itemp->stmtsp();
if (AstNode* const condp = m_valueItem[i]) {
AstCaseItem* caseItemp = caseItemMap[condp];
UASSERT(caseItemp, "caseItemp should exist");
m_valueItem[i] = caseItemp->stmtsp();
}
}
return true; // All is fine
@@ -543,10 +584,12 @@ private:
}
}
//--------------------
void visit(AstNode* nodep) override {
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
void visit(AstAlways* nodep) override {
VL_RESTORER(m_alwaysp)
m_alwaysp = nodep;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
+1 -1
View File
@@ -67,6 +67,7 @@ private:
nodep->editCountInc();
nodep->classOrPackagep(packagep);
packagep->classp(nodep);
packagep->timeunit(nodep->timeunit());
v3Global.rootp()->addModulesp(packagep);
// Add package to hierarchy
AstCell* const cellp = new AstCell{packagep->fileline(),
@@ -105,7 +106,6 @@ private:
m_prefix = nodep->name() + "__02e"; // .
iterateChildren(nodep);
}
nodep->repairCache();
}
void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
+1 -5
View File
@@ -95,6 +95,7 @@ private:
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else {
@@ -234,11 +235,6 @@ private:
operandTriop(nodep);
setClean(nodep, nodep->cleanOut());
}
void visit(AstStructSel* nodep) override {
iterateChildren(nodep);
AstStructDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), StructDType);
setClean(nodep, dtypep && !dtypep->packed());
}
void visit(AstUCFunc* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
+102 -38
View File
@@ -1799,7 +1799,7 @@ private:
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
"tried to merge two selects which are not adjacent");
AstSel* const newselp = new AstSel{
lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth + rwidth};
lselp->fromp()->fileline(), rselp->fromp()->unlinkFrBack(), rstart, lwidth + rwidth};
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
<< endl);
@@ -1809,6 +1809,7 @@ private:
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void replaceConcatMerge(AstConcat* nodep) {
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
AstNodeExpr* const llp = lp->lhsp()->cloneTree(false);
@@ -2052,7 +2053,7 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return true;
}
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
} else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isTreePureRecurse()) {
bool need_temp = false;
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
// Note only do this (need user4) when m_warn, which is
@@ -2145,33 +2146,42 @@ private:
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
// The right-streaming operator on rhs of assignment does not
// change the order of bits. Eliminate stream but keep its lhsp
// Unlink the stuff
AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
// change the order of bits. Eliminate stream but keep its lhsp.
// Add a cast if needed.
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (nodep->lhsp()->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
}
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
// Push the stream operator to the rhs of the assignment statement
const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack();
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
const int sWidth = srcp->width();
const int dWidth = dstp->width();
// Connect the rhs to the stream operator and update its width
VN_AS(streamp, StreamL)->lhsp(srcp);
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
// Shrink the RHS if necessary
if (sWidth > dWidth) {
if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType)
|| VN_IS(srcp->dtypep(), UnpackArrayDType)) {
streamp->dtypeSetStream();
} else {
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
}
if (dWidth == 0) {
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
} else if (sWidth > dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
}
// Link the nodes back in
nodep->lhsp(dstp);
nodep->rhsp(streamp);
return true;
@@ -2179,23 +2189,35 @@ private:
// The right stream operator on lhs of assignment statement does
// not reorder bits. However, if the rhs is wider than the lhs,
// then we select bits from the left-most, not the right-most.
const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width();
const int sWidth = nodep->rhsp()->width();
// Unlink the stuff
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack();
AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
if (sWidth > dWidth) {
const int sWidth = srcp->width();
const int dWidth = dstp->width();
if (dWidth == 0) {
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
} else if (sWidth > dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
}
nodep->lhsp(dstp);
nodep->rhsp(srcp);
// Cleanup
VL_DO_DANGLING(sizep->deleteTree(), sizep);
VL_DO_DANGLING(streamp->deleteTree(), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep();
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
AstNodeExpr* const srcp = streamp->lhsp();
const AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (lhsDtypep->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp->unlinkFrBack();
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
streamp->dtypeFrom(lhsDtypep);
}
} else if (m_doV && replaceAssignMultiSel(nodep)) {
return true;
}
@@ -2283,6 +2305,13 @@ private:
iterateChildren(nodep);
}
}
void visit(AstCLocalScope* nodep) override {
iterateChildren(nodep);
if (!nodep->stmtsp()) {
nodep->unlinkFrBack();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstScope* nodep) override {
// No ASSIGNW removals under scope, we've long eliminated INITIALs
VL_RESTORER(m_wremove);
@@ -3175,8 +3204,17 @@ private:
iterateChildren(nodep);
}
void visit(AstFuncRef* nodep) override {
void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep);
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
}
void visit(AstNodeFTaskRef* nodep) override {
// Note excludes AstFuncRef as other visitor below
iterateChildren(nodep);
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
}
void visit(AstFuncRef* nodep) override {
visit(static_cast<AstNodeFTaskRef*>(nodep));
if (m_params) { // Only parameters force us to do constant function call propagation
replaceWithSimulation(nodep);
}
@@ -3650,6 +3688,7 @@ private:
public:
// Processing Mode Enum
enum ProcMode : uint8_t {
PROC_PARAMS_NOWARN,
PROC_PARAMS,
PROC_GENERATE,
PROC_LIVE,
@@ -3665,16 +3704,18 @@ public:
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
// clang-format off
switch (pmode) {
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break;
case PROC_LIVE: break;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
default: v3fatalSrc("Bad case"); break;
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
m_required = false; break;
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; break;
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
m_required = true; m_doGenerate = true; break;
case PROC_LIVE: break;
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
default: v3fatalSrc("Bad case"); break;
}
// clang-format on
}
@@ -3722,6 +3763,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
return nodep;
}
//! Constify this cell node's parameter list if possible
//! @return Pointer to the edited node.
AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
// Resize even if the node already has a width, because buried in the tree
// we may have a node we just created with signing, etc, that isn't sized yet.
// Make sure we've sized everything first
nodep = V3Width::widthParamsEdit(nodep);
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
if (AstVar* const varp = VN_CAST(nodep, Var)) {
// If a var wants to be constified, it's really a param, and
// we want the value to be constant. We aren't passed just the
// init value because we need widthing above to handle the var's type.
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
} else {
nodep = visitor.mainAcceptEdit(nodep);
}
// Because we do edits, nodep links may get trashed and core dump this.
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
return nodep;
}
//! Force this cell node's parameter list to become a constant inside generate.
//! If we are inside a generated "if", "case" or "for", we don't want to
//! trigger warnings when we deal with the width. It is possible that these
+4
View File
@@ -30,6 +30,10 @@ public:
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr);
}
static AstNode* constifyParamsNoWarnEdit(AstNode* nodep);
static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) {
return VN_AS(constifyParamsNoWarnEdit(static_cast<AstNode*>(exprp)), NodeExpr);
}
static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
// Only do constant pushing, without removing dead logic
static void constifyAllLive(AstNetlist* nodep);
-1
View File
@@ -288,7 +288,6 @@ private:
VL_RESTORER(m_sideEffect);
m_inAssign = true;
m_sideEffect = false;
if (assignInAssign) m_sideEffect = true;
iterateAndNextNull(nodep->rhsp());
checkAll(nodep);
// Has to be direct assignment without any EXTRACTing.
+9 -1
View File
@@ -551,7 +551,9 @@ private:
}
if (!lhsp->backp()) VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
} else {
iterateChildren(nodep);
iterate(nodep->lhsp());
m_inDly = false;
iterate(nodep->rhsp());
}
}
@@ -623,6 +625,12 @@ private:
m_inLoop = true;
iterateChildren(nodep);
}
void visit(AstExprStmt* nodep) override {
VL_RESTORER(m_inDly);
// Restoring is needed, because AstExprStmt may contain assignments
m_inDly = false;
iterateChildren(nodep);
}
//--------------------
void visit(AstNode* nodep) override { iterateChildren(nodep); }
+37 -16
View File
@@ -46,6 +46,10 @@ private:
// TYPES
using FuncMmap = std::multimap<std::string, AstCFunc*>;
struct ScopeSelfPtr final {
VSelfPointerText thisPtr = VSelfPointerText{VSelfPointerText::Empty()};
VSelfPointerText vlSymsPtr = VSelfPointerText{VSelfPointerText::Empty()};
};
// STATE
AstNodeModule* m_modp = nullptr; // Current module
@@ -53,6 +57,7 @@ private:
const AstCFunc* m_funcp = nullptr; // Current function
bool m_modSingleton = false; // m_modp is only instantiated once
FuncMmap m_modFuncs; // Name of public functions added
std::map<const AstScope*, ScopeSelfPtr> m_scopeToSelf; // Scope to self pointers
// METHODS
@@ -68,12 +73,32 @@ private:
return (instances == 1);
}
// Construct a 'this' self pointer for the given scope
VSelfPointerText scopeThis(const AstScope* scopep) {
auto& ret = m_scopeToSelf[scopep];
if (ret.thisPtr.isEmpty()) {
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
ret.thisPtr = VSelfPointerText{VSelfPointerText::This(), name};
}
return ret.thisPtr;
}
// Construct a 'vlSyms' self pointer for the given scope
VSelfPointerText scopeVlSyms(const AstScope* scopep) {
auto& ret = m_scopeToSelf[scopep];
if (ret.vlSymsPtr.isEmpty()) {
ret.vlSymsPtr = VSelfPointerText{VSelfPointerText::VlSyms(), scopep->nameDotless()};
}
return ret.vlSymsPtr;
}
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
//
// Using relative references allows V3Combine'ing code across multiple instances of the same
// module.
string descopedSelfPointer(const AstScope* scopep) {
VSelfPointerText descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();
@@ -85,23 +110,20 @@ private:
if (VN_IS(scopep->modp(), Class)) {
// Direct reference to class members are from within the class itself, references from
// outside the class must go via AstMemberSel
return "this";
return VSelfPointerText{VSelfPointerText::This()};
} else if (relativeRefOk && scopep == m_scopep) {
return "this";
return VSelfPointerText{VSelfPointerText::This()};
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
&& VN_IS(scopep->modp(), Module)) {
// Reference to scope of instance directly under this module, can just "this->cell",
// which can potentially be V3Combined, but note this requires one extra pointer
// dereference which is slower, so we only use it if the source scope is not a
// singleton.
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
return "this->" + name;
return scopeThis(scopep);
} else {
// Reference to something elsewhere, or relative references are disabled. Use global
// variable
return "(&" + scopep->nameVlSym() + ")";
return scopeVlSyms(scopep);
}
}
@@ -163,11 +185,10 @@ private:
if (moreOfSame) {
AstIf* const ifp = new AstIf{
funcp->fileline(),
new AstEq{
funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64},
new AstCExpr{funcp->fileline(),
string{"&("} + funcp->scopep()->nameVlSym() + ")",
64}},
new AstEq{funcp->fileline(),
new AstCExpr{funcp->fileline(), "this", 64},
new AstCExpr{funcp->fileline(),
scopeVlSyms(funcp->scopep()).asString(), 64}},
returnp};
newfuncp->addStmtsp(ifp);
} else {
@@ -225,15 +246,15 @@ private:
const AstScope* const scopep = nodep->varScopep()->scopep();
if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer
nodep->selfPointer("");
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
// Reference to constant pool value need no self pointer
nodep->selfPointer("");
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else {
nodep->selfPointer(descopedSelfPointer(scopep));
}
nodep->varScopep(nullptr);
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl);
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer().asString() << endl);
}
void visit(AstCCall* nodep) override {
// UINFO(9, " " << nodep << endl);
+2 -2
View File
@@ -503,11 +503,11 @@ public:
inline bool inlined() const;
// Methods that allow DfgVertex to participate in error reporting/messaging
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
m_filelinep->v3errorEnd(str);
}
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
VL_REQUIRES(V3Error::s().m_mutex) {
VL_RELEASE(V3Error::s().m_mutex) {
m_filelinep->v3errorEndFatal(str);
}
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
+2 -2
View File
@@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
const std::string m_label; // Label to apply to stats
// Enable flags for each optimization
bool m_enabled[VDfgPeepholePattern::_ENUM_END];
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
// Count of applications for each optimization (for statistics)
VDouble0 m_count[VDfgPeepholePattern::_ENUM_END];
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
explicit V3DfgPeepholeContext(const std::string& label);
~V3DfgPeepholeContext();
+5 -4
View File
@@ -62,7 +62,7 @@ public:
static string symClassAssign() {
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
}
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
}
static bool isAnonOk(const AstVar* varp) {
@@ -117,11 +117,12 @@ public:
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
if (usep->useType() == useType) {
if (usep->useType().isInclude()) {
if (usep->useType().containsAny(useType)) {
if (usep->useType().containsAny(VUseType::INT_INCLUDE)) {
action("#include \"" + prefixNameProtect(usep) + ".h\"\n");
continue; // Forward declaration is not necessary
}
if (usep->useType().isFwdClass()) {
if (usep->useType().containsAny(VUseType::INT_FWD_CLASS)) {
action("class " + prefixNameProtect(usep) + ";\n");
}
}
+9 -4
View File
@@ -108,7 +108,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
UASSERT_OBJ(m_wideTempRefp, nodep,
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
COMMA;
if (!m_wideTempRefp->selfPointer().empty()) {
if (!m_wideTempRefp->selfPointer().isEmpty()) {
emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
}
puts(m_wideTempRefp->varp()->nameProtect());
@@ -314,6 +314,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
// Convert Verilog display to C printf formats
// "%0t" becomes "%d"
VL_RESTORER(m_emitDispState);
m_emitDispState.clear();
string vfmt;
string::const_iterator pos = vformat.begin();
@@ -419,7 +420,7 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
}
if (nodep->funcp()->needProcess()) {
if (comma) puts(", ");
if (VN_IS(nodep->backp(), CAwait)) {
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
puts("vlProcess");
} else {
puts("std::make_shared<VlProcess>()");
@@ -452,6 +453,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
putbs("std::string{");
putsQuoted(constp->num().toString());
puts("}");
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
putbs("VL_CVT_PACK_STR_ND(");
iterateAndNextConstNull(nodep);
puts(")");
} else {
putbs("VL_CVT_PACK_STR_N");
emitIQW(nodep);
@@ -512,7 +517,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (!assigntop) {
puts(assignString);
} else {
if (!assigntop->selfPointer().empty()) {
if (!assigntop->selfPointer().isEmpty()) {
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
}
puts(assigntop->varp()->nameProtect());
@@ -534,7 +539,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (!assigntop) {
puts(assignString);
} else {
if (!assigntop->selfPointer().empty()) {
if (!assigntop->selfPointer().isEmpty()) {
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
}
puts(assigntop->varp()->nameProtect());
+111 -28
View File
@@ -22,6 +22,7 @@
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include "V3MemberMap.h"
#include <algorithm>
#include <map>
@@ -115,6 +116,7 @@ public:
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
private:
VMemberMap m_memberMap;
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
int m_labelNum = 0; // Next label number
int m_splitSize = 0; // # of cfunc nodes placed into output file
@@ -148,6 +150,22 @@ protected:
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
const AstNodeModule* m_modp = nullptr; // Current module being emitted
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
bool m_instantiatesOwnProcess = false;
bool constructorNeedsProcess(const AstClass* const classp) {
const AstNode* const newp = m_memberMap.findMember(classp, "new");
if (!newp) return false;
const AstCFunc* const ctorp = VN_CAST(newp, CFunc);
if (!ctorp) return false;
UASSERT_OBJ(ctorp->isConstructor(), ctorp, "`new` is not a constructor!");
return ctorp->needProcess();
}
bool constructorNeedsProcess(const AstNodeDType* const dtypep) {
if (const AstClassRefDType* const crefdtypep = VN_CAST(dtypep, ClassRefDType))
return constructorNeedsProcess(crefdtypep->classp());
return false;
}
public:
// METHODS
@@ -174,12 +192,10 @@ public:
}
void emitScIQW(AstVar* nodep) {
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
// clang-format off
puts(nodep->isScBigUint() ? "SB"
: nodep->isScUint() ? "SU"
: nodep->isScBv() ? "SW"
: (nodep->isScQuad() ? "SQ" : "SI"));
// clang-format on
: (nodep->isScQuad() ? "SQ" : "SI"));
}
void emitDatap(AstNode* nodep) {
// When passing to a function with va_args the compiler doesn't
@@ -212,13 +228,34 @@ public:
comma = true;
}
}
template <typename T>
string optionalProcArg(const T* const nodep) {
return (nodep && constructorNeedsProcess(nodep)) ? "vlProcess, " : "";
}
const AstCNew* getSuperNewCallRecursep(AstNode* const nodep) {
// Get the super.new call
if (!nodep) return nullptr;
if (const AstCNew* const cnewp = VN_CAST(nodep, CNew)) return cnewp;
if (const AstStmtExpr* const stmtp = VN_CAST(nodep, StmtExpr)) {
if (const AstCNew* const cnewp = VN_CAST(stmtp->exprp(), CNew)) return cnewp;
}
if (const AstJumpBlock* const blockp = VN_CAST(nodep, JumpBlock)) {
if (const AstCNew* const cnewp = getSuperNewCallRecursep(blockp->stmtsp())) {
return cnewp;
}
}
if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp;
return nullptr;
}
// VISITORS
using EmitCConstInit::visit;
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_useSelfForThis);
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_instantiatesOwnProcess)
m_cfuncp = nodep;
m_instantiatesOwnProcess = false;
splitSizeInc(nodep);
@@ -231,20 +268,16 @@ public:
if (!nodep->baseCtors().empty()) {
puts(": ");
puts(nodep->baseCtors());
puts("(vlSymsp");
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
// Find call to super.new to get the arguments
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
AstNode* exprp;
if (VN_IS(stmtp, StmtExpr)) {
exprp = VN_CAST(stmtp, StmtExpr)->exprp();
} else {
exprp = stmtp;
}
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
putCommaIterateNext(newRefp->argsp(), true);
break;
}
if (classp && constructorNeedsProcess(classp)) {
puts("(vlProcess, vlSymsp");
} else {
puts("(vlSymsp");
}
const AstCNew* const superNewCallp = getSuperNewCallRecursep(nodep->stmtsp());
UASSERT_OBJ(superNewCallp, nodep, "super.new call not found");
putCommaIterateNext(superNewCallp->argsp(), true);
puts(")");
}
puts(" {\n");
@@ -265,6 +298,23 @@ public:
puts(nodep->isLoose() ? "__" : "::");
puts(nodep->nameProtect() + "\\n\"); );\n");
// Instantiate a process class if it's going to be needed somewhere later
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
if (ccallp->funcp()->needProcess()
&& (ccallp->funcp()->isCoroutine() == VN_IS(ccallp->backp(), CAwait))) {
if (!nodep->needProcess() && !m_instantiatesOwnProcess) {
m_instantiatesOwnProcess = true;
return false;
}
}
return true;
});
if (m_instantiatesOwnProcess) {
AstNode* const vlprocp = new AstCStmt{
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();\n"};
nodep->stmtsp()->addHereThisAsNext(vlprocp);
}
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
if (varp->isFuncReturn()) emitVarDecl(varp);
@@ -295,6 +345,19 @@ public:
emitVarDecl(nodep);
}
void visit(AstCvtDynArrayToPacked* nodep) override {
puts("VL_DYN_TO_");
emitIQW(nodep);
puts("<");
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
putbs(elemDTypep->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->fromp());
puts(", ");
puts(cvtToStr(elemDTypep->widthMin()));
puts(")");
}
void visit(AstNodeAssign* nodep) override {
bool paren = true;
bool decind = false;
@@ -349,6 +412,19 @@ public:
puts(cvtToStr(nodep->widthMin()) + ",");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
} else if (const AstCvtPackedToDynArray* const castp
= VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) {
puts("VL_ASSIGN_DYN_Q<");
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
puts(cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
puts(", ");
puts(cvtToStr(castp->fromp()->widthMin()));
puts(", ");
rhs = false;
iterateAndNextConstNull(castp->fromp());
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
&& !VN_IS(nodep->rhsp(), CExpr) //
&& !VN_IS(nodep->rhsp(), CMethodHard) //
@@ -356,7 +432,8 @@ public:
&& !VN_IS(nodep->rhsp(), AssocSel) //
&& !VN_IS(nodep->rhsp(), MemberSel) //
&& !VN_IS(nodep->rhsp(), StructSel) //
&& !VN_IS(nodep->rhsp(), ArraySel)) {
&& !VN_IS(nodep->rhsp(), ArraySel) //
&& !VN_IS(nodep->rhsp(), ExprStmt)) {
// Wide functions assign into the array directly, don't need separate assign statement
m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef);
paren = false;
@@ -383,19 +460,17 @@ public:
void visit(AstAssocSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
putbs(".at(");
AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType);
AstAssocArrayDType* const adtypep
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
if (adtypep->keyDTypep()->isWide()) {
emitCvtWideArray(nodep->bitp(), nodep->fromp());
} else {
iterateAndNextConstNull(nodep->bitp());
}
iterateAndNextConstNull(nodep->bitp());
puts(")");
}
void visit(AstWildcardSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
putbs(".at(");
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
AstWildcardArrayDType* const adtypep
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
iterateAndNextConstNull(nodep->bitp());
puts(")");
@@ -419,7 +494,7 @@ public:
puts(funcNameProtect(funcp));
} else {
// Calling regular method/function
if (!nodep->selfPointer().empty()) {
if (!nodep->selfPointer().isEmpty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}
puts(funcp->nameProtect());
@@ -443,8 +518,9 @@ public:
// super.new case
return;
}
// assignment case
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp");
// assignment case;
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
putCommaIterateNext(nodep->argsp(), true);
puts(")");
}
@@ -832,6 +908,11 @@ public:
iterateAndNextConstNull(nodep->endStmtsp());
puts("}\n");
}
void visit(AstCLocalScope* nodep) override {
puts("{\n");
iterateAndNextConstNull(nodep->stmtsp());
puts("}\n");
}
void visit(AstJumpGo* nodep) override {
puts("goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
}
@@ -1026,6 +1107,7 @@ public:
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
}
void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); }
void visit(AstRedXor* nodep) override {
if (nodep->lhsp()->isWide()) {
visit(static_cast<AstNodeUniop*>(nodep));
@@ -1088,7 +1170,8 @@ public:
puts(")");
}
void visit(AstNewCopy* nodep) override {
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
+ optionalProcArg(nodep->dtypep()));
puts("*"); // i.e. make into a reference
iterateAndNextConstNull(nodep->rhsp());
puts(")");
@@ -1182,7 +1265,7 @@ public:
} else if (varp->isIfaceRef()) {
puts(nodep->selfPointerProtect(m_useSelfForThis));
return;
} else if (!nodep->selfPointer().empty()) {
} else if (!nodep->selfPointer().isEmpty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}
puts(nodep->varp()->nameProtect());
+3 -4
View File
@@ -380,11 +380,10 @@ class EmitCHeader final : public EmitCConstInit {
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
forModCUse(modp, VUseType::INT_INCLUDE, add_to_cuse_set);
forModCUse(modp, VUseType::INT_FWD_CLASS, add_to_cuse_set);
forModCUse(modp, VUseType::INT_FWD_CLASS | VUseType::INT_INCLUDE, add_to_cuse_set);
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
forModCUse(packagep->classp(), VUseType::INT_INCLUDE, add_to_cuse_set);
forModCUse(packagep->classp(), VUseType::INT_FWD_CLASS, add_to_cuse_set);
forModCUse(packagep->classp(), VUseType::INT_INCLUDE | VUseType::INT_FWD_CLASS,
add_to_cuse_set);
}
for (const string& s : cuse_set) puts(s);
+12 -13
View File
@@ -59,18 +59,18 @@ class EmitCGatherDependencies final : VNVisitorConst {
}
}
}
void addSelfDependency(const string& selfPointer, AstNode* nodep) {
if (selfPointer.empty()) {
void addSelfDependency(VSelfPointerText selfPointer, AstNode* nodep) {
if (selfPointer.isEmpty()) {
// No self pointer (e.g.: function locals, const pool values, loose static methods),
// so no dependency
} else if (VString::startsWith(selfPointer, "this")) {
} else if (selfPointer.hasThis()) {
// Dereferencing 'this', we need the definition of this module, which is also the
// module that contains the variable.
addModDependency(EmitCParentModule::get(nodep));
} else {
// Must be an absolute reference
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
"Unknown self pointer: '" << selfPointer << "'");
UASSERT_OBJ(selfPointer.isVlSym(), nodep,
"Unknown self pointer: '" << selfPointer.asString() << "'");
// Dereferencing vlSymsp, so we need it's definition...
addSymsDependency();
}
@@ -147,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
}
public:
static const std::set<std::string> gather(AstCFunc* cfuncp) VL_MT_STABLE {
static const std::set<std::string> gather(AstCFunc* cfuncp) {
const EmitCGatherDependencies visitor{cfuncp};
return std::move(visitor.m_dependencies);
}
@@ -566,8 +566,7 @@ class EmitCImp final : EmitCFunc {
~EmitCImp() override = default;
public:
static void main(const AstNodeModule* modp, bool slow,
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
EmitCImp{modp, slow, cfilesr};
}
};
@@ -678,7 +677,7 @@ class EmitCTrace final : EmitCFunc {
string fstvt;
// Doubles have special decoding properties, so must indicate if a double
if (nodep->dtypep()->basicp()->isDouble()) {
if (vartype == VVarType::GPARAM || vartype == VVarType::LPARAM) {
if (vartype.isParam()) {
fstvt = "FST_VT_VCD_REAL_PARAMETER";
} else {
fstvt = "FST_VT_VCD_REAL";
@@ -932,11 +931,11 @@ void V3EmitC::emitcImp() {
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
cfiles.emplace_back();
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>(
futures.push_back(V3ThreadPool::s().enqueue(
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
cfiles.emplace_back();
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>(
futures.push_back(V3ThreadPool::s().enqueue(
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
}
@@ -944,12 +943,12 @@ void V3EmitC::emitcImp() {
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
cfiles.emplace_back();
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&slowCfilesr]() {
futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
}));
cfiles.emplace_back();
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&fastCfilesr]() {
futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
}));
}
+15
View File
@@ -234,6 +234,12 @@ class EmitCModel final : public EmitCFunc {
puts("const char* hierName() const override final;\n");
puts("const char* modelName() const override final;\n");
puts("unsigned threads() const override final;\n");
puts("/// Prepare for cloning the model at the process level (e.g. fork in Linux)\n");
puts("/// Release necessary resources. Called before cloning.\n");
puts("void prepareClone() const;\n");
puts("/// Re-init after cloning the model at the process level (e.g. fork in Linux)\n");
puts("/// Re-allocate necessary resources. Called after cloning.\n");
puts("void atClone() const;\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
}
@@ -479,6 +485,15 @@ class EmitCModel final : public EmitCFunc {
+ "\"; }\n");
puts("unsigned " + topClassName() + "::threads() const { return "
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
puts("void " + topClassName()
+ "::prepareClone() const { contextp()->prepareClone(); }\n");
puts("void " + topClassName() + "::atClone() const {\n");
if (v3Global.opt.threads() > 1) {
puts("vlSymsp->__Vm_threadPoolp = static_cast<VlThreadPool*>(");
}
puts("contextp()->threadPoolpOnClone()");
if (v3Global.opt.threads() > 1) puts(")");
puts(";\n}\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> " + topClassName()
+1 -1
View File
@@ -468,7 +468,7 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.opt.mtasks()) {
puts("\n// MULTI-THREADING\n");
puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("VlThreadPool* __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle__ico = false;\n");
puts("bool __Vm_even_cycle__act = false;\n");
puts("bool __Vm_even_cycle__nba = false;\n");
+9 -2
View File
@@ -664,6 +664,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
iterateAndNextConstNull(nodep->pinsp());
puts(")");
}
void visit(AstCCall* nodep) override {
puts(nodep->funcp()->name());
puts("(");
iterateAndNextConstNull(nodep->argsp());
puts(")");
}
void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
void visit(AstPrintTimeScale* nodep) override {
puts(nodep->verilogKwd());
@@ -676,10 +682,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putfs(nodep, nodep->varScopep()->prettyName());
} else {
if (nodep->varp()) {
if (nodep->selfPointer().empty()) {
if (nodep->selfPointer().isEmpty()) {
putfs(nodep, nodep->varp()->prettyName());
} else {
putfs(nodep, nodep->selfPointer() + "->");
putfs(nodep, nodep->selfPointer().asString());
putfs(nodep, "->");
puts(nodep->varp()->prettyName());
}
} else {
+44 -14
View File
@@ -222,20 +222,18 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
#ifndef V3ERROR_NO_GLOBAL_
if (dumpTreeLevel() || debug()) {
V3Broken::allowMidvisitorCheck(true);
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
if (dumpTreeLevel()) {
v3Global.rootp()->dumpTreeFile(
v3Global.debugFilename("final.tree", 990));
}
if (debug()) {
execErrorExitCb();
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
// Abort in exclusive access to make sure other threads
// don't change error code
vlAbortOrExit();
});
const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
if (dumpTreeLevel()) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
}
if (debug()) {
execErrorExitCb();
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
// Abort in exclusive access to make sure other threads
// don't change error code
vlAbortOrExit();
}
#endif
}
@@ -293,3 +291,35 @@ void V3Error::vlAbort() {
VL_GCOV_DUMP();
std::abort();
}
void V3Error::v3errorAcquireLock() VL_ACQUIRE(s().m_mutex) {
#ifndef V3ERROR_NO_GLOBAL_
V3Error::s().m_mutex.lockCheckStopRequest(
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); });
#else
V3Error::s().m_mutex.lock();
#endif
}
std::ostringstream& V3Error::v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex) {
v3errorAcquireLock();
s().v3errorPrep(code);
return v3errorStr();
}
std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* file, int line)
VL_ACQUIRE(s().m_mutex) {
v3errorPrep(code) << file << ":" << std::dec << line << ": ";
return v3errorStr();
}
std::ostringstream& V3Error::v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); }
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) {
s().v3errorEnd(sstr, extra);
V3Error::s().m_mutex.unlock();
}
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
}
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
+53 -80
View File
@@ -60,6 +60,7 @@ public:
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
// Error codes:
E_LIFETIME, // Error: Reference to a variable might outlive the variable.
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
E_NOTIMING, // Timing control encountered with --no-timing
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
@@ -98,6 +99,7 @@ public:
ENUMVALUE, // Error: enum type needs explicit cast
EOFNEWLINE, // End-of-file missing newline
GENCLK, // Generated Clock. Historical, never issued.
GENUNNAMED, // Generate unnamed, without label
HIERBLOCK, // Ignored hierarchical block setting
IFDEPTH, // If statements too deep
IGNOREDRETURN, // Ignoring return value (function as task)
@@ -113,6 +115,7 @@ public:
LATCH, // Latch detected outside of always_latch block
LITENDIAN, // Little endian, renamed to ASCRANGE
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
MISINDENT, // Misleading indentation
MODDUP, // Duplicate module
MULTIDRIVEN, // Driven from multiple blocks
MULTITOP, // Multiple top level modules
@@ -183,7 +186,7 @@ public:
// Boolean
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
// Errors
"NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
"LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
// Warnings
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
@@ -191,11 +194,12 @@ public:
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG",
"DECLFILENAME", "DEFPARAM", "DEPRECATED",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED",
"HIERBLOCK",
"IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
@@ -234,18 +238,18 @@ public:
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|| m_e == LATCH || m_e == NEWERSTD || m_e == PINMISSING || m_e == REALCVT
|| m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC
|| m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PINMISSING
|| m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|| m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
}
// Warnings that are style only
bool styleError() const VL_MT_SAFE {
return (m_e == ASSIGNDLY // More than style, but for backward compatibility
|| m_e == BLKSEQ || m_e == DEFPARAM || m_e == DECLFILENAME || m_e == EOFNEWLINE
|| m_e == IMPORTSTAR || m_e == INCABSPATH || m_e == PINCONNECTEMPTY
|| m_e == PINNOCONNECT || m_e == SYNCASYNCNET || m_e == UNDRIVEN
|| m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL
|| m_e == VARHIDDEN);
|| m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH
|| m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET
|| m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM
|| m_e == UNUSEDSIGNAL || m_e == VARHIDDEN);
}
// Warnings that are unused only
bool unusedError() const VL_MT_SAFE {
@@ -282,9 +286,13 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) {
}
// ######################################################################
class V3Error;
class V3ErrorGuarded final {
// Should only be used by V3ErrorGuarded::m_mutex is already locked
// contains guarded members
friend class V3Error;
public:
using MessagesSet = std::set<std::string>;
using ErrorExitCb = void (*)(void);
@@ -315,6 +323,16 @@ private:
= MAX_ERRORS; // Option: --error-limit Number of errors before exit
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
m_errorStr.str("");
m_errorCode = code;
m_errorContexted = false;
m_errorSuppressed = false;
}
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
public:
V3RecursiveMutex m_mutex; // Make sure only single thread is in class
@@ -335,7 +353,7 @@ public:
v3errorEnd(
(v3errorPrep(V3ErrorCode::EC_FATALEXIT),
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
<< errorCount() << endl),
<< errorCount() << std::endl),
v3errorStr()));
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
@@ -357,13 +375,6 @@ public:
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
m_errorStr.str("");
m_errorCode = code;
m_errorContexted = false;
m_errorSuppressed = false;
}
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
@@ -381,7 +392,6 @@ public:
void describedWarnings(bool flag) VL_REQUIRES(m_mutex) { m_describedWarnings = flag; }
int tellManual() VL_REQUIRES(m_mutex) { return m_tellManual; }
void tellManual(int level) VL_REQUIRES(m_mutex) { m_tellManual = level; }
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
void suppressThisWarning() VL_REQUIRES(m_mutex);
string warnContextNone() VL_REQUIRES(m_mutex) {
errorContexted(true);
@@ -508,66 +518,32 @@ public:
// Internals for v3error()/v3fatal() macros only
// Error end takes the string stream to output, be careful to seek() as needed
static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const V3RecursiveLockGuard guard{s().m_mutex};
s().v3errorPrep(code);
}
static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const V3RecursiveLockGuard guard{s().m_mutex};
return s().v3errorStr();
}
static void vlAbort();
static void v3errorAcquireLock() VL_ACQUIRE(s().m_mutex);
static std::ostringstream& v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex);
static std::ostringstream& v3errorPrepFileLine(V3ErrorCode code, const char* file, int line)
VL_ACQUIRE(s().m_mutex);
static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex);
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE {
const V3RecursiveLockGuard guard{s().m_mutex};
s().v3errorEnd(sstr, extra);
}
// We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal',
// due to bug in GCC (tested on 11.3.0 version with --enable-m32)
// causing internal error when backtrace is printed.
// Instead use this wrapper.
static void v3errorEndGuardedCall(std::ostringstream& sstr, const string& extra = "")
VL_REQUIRES(s().m_mutex) VL_MT_SAFE {
s().v3errorEnd(sstr, extra);
}
VL_RELEASE(s().m_mutex);
static void vlAbort();
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
inline void v3errorEnd(std::ostringstream& sstr) VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
V3Error::v3errorEndGuardedCall(sstr);
}
inline void v3errorEndFatal(std::ostringstream& sstr)
VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
V3Error::v3errorEndGuardedCall(sstr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
#ifndef V3ERROR_NO_GLOBAL_
#define V3ErrorLockAndCheckStopRequested \
V3Error::s().m_mutex.lockCheckStopRequest( \
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); })
#else
#define V3ErrorLockAndCheckStopRequested V3Error::s().m_mutex.lock()
#endif
// Global versions, so that if the class doesn't define an operator, we get the functions anyway.
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) VL_ATTR_NORETURN;
// Theses allow errors using << operators: v3error("foo"<<"bar");
// Careful, you can't put () around msg, as you would in most macro definitions
// Note the commas are the comma operator, not separating arguments. These are needed to ensure
// evaluation order as otherwise we couldn't ensure v3errorPrep is called first.
// Note: due to limitations of clang thread-safety analysis, we can't use
// lock guard here, instead we are locking the mutex as first operation in temporary,
// but we are unlocking the mutex after function using comma operator.
// This way macros should also work when they are in 'if' stmt without '{}'.
#define v3warnCode(code, msg) \
v3errorEnd((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
V3Error::s().m_mutex.unlock()
// Careful, you can't put () around msg, as you would in most macro definitions.
// 'V3Error::v3errorPrep(code) << msg' could be more efficient but the order of function calls in a
// single statement can be arbitrary until C++17, thus make it possible to execute
// V3Error::v3errorPrep that acquires the lock after functions in the msg may require it. So we use
// the comma operator (,) to guarantee the execution order here.
#define v3errorBuildMessage(prep, msg) \
(prep, static_cast<std::ostringstream&>(V3Error::v3errorStr() << msg))
#define v3warnCode(code, msg) v3errorEnd(v3errorBuildMessage(V3Error::v3errorPrep(code), msg))
#define v3warnCodeFatal(code, msg) \
v3errorEndFatal((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
V3Error::s().m_mutex.unlock()
v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(code), msg))
#define v3warn(code, msg) v3warnCode(V3ErrorCode::code, msg)
#define v3info(msg) v3warnCode(V3ErrorCode::EC_INFO, msg)
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
@@ -576,24 +552,21 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
#define v3fatalExit(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALEXIT, msg)
// Use this instead of fatal() to mention the source code line.
#define v3fatalSrc(msg) \
v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, \
__FILE__ << ":" << std::dec << __LINE__ << ": " << msg)
v3errorEndFatal(v3errorBuildMessage( \
V3Error::v3errorPrepFileLine(V3ErrorCode::EC_FATALSRC, __FILE__, __LINE__), msg))
// Use this when normal v3fatal is called in static method that overrides fileline.
#define v3fatalStatic(msg) \
(::v3errorEndFatal((V3ErrorLockAndCheckStopRequested, \
V3Error::s().v3errorPrep(V3ErrorCode::EC_FATAL), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr()))), \
V3Error::s().m_mutex.unlock()
::v3errorEndFatal(v3errorBuildMessage(V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), msg))
#define UINFO(level, stmsg) \
do { \
if (VL_UNCOVERABLE(debug() >= (level))) { \
cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
std::cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
} \
} while (false)
#define UINFONL(level, stmsg) \
do { \
if (VL_UNCOVERABLE(debug() >= (level))) { cout << stmsg; } \
if (VL_UNCOVERABLE(debug() >= (level))) { std::cout << stmsg; } \
} while (false)
#ifdef VL_DEBUG
+3 -1
View File
@@ -202,7 +202,9 @@ private:
new AstShiftR{fl, lhip,
new AstConst{fl, static_cast<uint32_t>(nbitsonright)},
VL_EDATASIZE}},
new AstAnd{fl, new AstConst{fl, AstConst::SizedEData{}, ~VL_MASK_E(loffset)},
new AstAnd{fl,
new AstConst{fl, AstConst::SizedEData{},
static_cast<uint32_t>(~VL_MASK_E(loffset))},
new AstShiftL{fl, llowp,
new AstConst{fl, static_cast<uint32_t>(loffset)},
VL_EDATASIZE}}};
+2 -2
View File
@@ -489,8 +489,8 @@ private:
void startFilter(const string& command) {
if (command == "") {} // Prevent Unused
#ifdef INFILTER_PIPE
int fd_stdin[2];
int fd_stdout[2];
int fd_stdin[2]; // Can't use std::array
int fd_stdout[2]; // Can't use std::array
constexpr int P_RD = 0;
constexpr int P_WR = 1;
+10 -8
View File
@@ -20,6 +20,7 @@
// clang-format off
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3Os.h"
#include "V3String.h"
#ifndef V3ERROR_NO_GLOBAL_
# include "V3Global.h"
@@ -286,12 +287,7 @@ FileLine* FileLine::copyOrSameFileLine() {
return newp;
}
string FileLine::filebasename() const VL_MT_SAFE {
string name = filename();
string::size_type pos;
if ((pos = name.rfind('/')) != string::npos) name.erase(0, pos + 1);
return name;
}
string FileLine::filebasename() const VL_MT_SAFE { return V3Os::filenameNonDir(filename()); }
string FileLine::filebasenameNoExt() const {
string name = filebasename();
@@ -383,7 +379,7 @@ bool FileLine::warnIsOff(V3ErrorCode code) const {
// cppverilator-suppress constParameter
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
VL_REQUIRES(V3Error::s().m_mutex) {
VL_RELEASE(V3Error::s().m_mutex) {
std::ostringstream nsstr;
if (lastLineno()) nsstr << this;
nsstr << sstr.str();
@@ -400,7 +396,7 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
nsstr << warnContextPrimary();
}
if (!m_waive) V3Waiver::addEntry(V3Error::s().errorCode(), filename(), sstr.str());
V3Error::s().v3errorEnd(nsstr, lstr.str());
V3Error::v3errorEnd(nsstr, lstr.str());
}
string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) {
@@ -433,6 +429,12 @@ string FileLine::source() const VL_MT_SAFE {
} // LCOV_EXCL_STOP
return m_contentp->getLine(m_contentLineno);
}
string FileLine::sourcePrefix(int toColumn) const VL_MT_SAFE {
const std::string src = source();
if (toColumn > static_cast<int>(src.length())) toColumn = static_cast<int>(src.length());
if (toColumn < 1) return "";
return src.substr(0, toColumn - 1);
}
string FileLine::prettySource() const VL_MT_SAFE {
string out = source();
// Drop ignore trailing newline
+3 -2
View File
@@ -241,6 +241,7 @@ public:
// as the parser errors etc generally make more sense pointing at the last parse point
int lineno() const VL_MT_SAFE { return m_lastLineno; }
string source() const VL_MT_SAFE;
string sourcePrefix(int toColumn) const VL_MT_SAFE;
string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines
FileLine* parent() const VL_MT_SAFE { return m_parent; }
V3LangCode language() const { return singleton().numberToLang(filenameno()); }
@@ -317,9 +318,9 @@ public:
// OPERATORS
void v3errorEnd(std::ostringstream& str, const string& extra = "")
VL_REQUIRES(V3Error::s().m_mutex);
VL_RELEASE(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& str) VL_ATTR_NORETURN
VL_REQUIRES(V3Error::s().m_mutex) {
VL_RELEASE(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
+651
View File
@@ -0,0 +1,651 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// V3Fork's Transformations:
//
// Each module:
// Look for FORKs [JOIN_NONE]/[JOIN_ANY]
// VARREF(var) -> MEMBERSEL(var->name, VARREF(dynscope)) (for write/RW refs)
// FORK(stmts) -> TASK(stmts), FORK(TASKREF(inits))
//
// FORKs that spawn tasks which might outlive their parents require those
// tasks to carry their own frames and as such they require their own
// variable scopes.
// There are two mechanisms that work together to achieve that. ForkVisitor
// moves bodies of forked prcesses into new tasks, which results in them getting their
// own scopes. The original statements get replaced with a call to the task which
// passes the required variables by value.
// The second mechanism, DynScopeVisitor, is designed to handle variables which can't be
// captured by value and instead require a reference. Those variables get moved into an
// "anonymous" object, ie. a class with appropriate fields gets generated and an object
// of this class gets instantiated in place of the original variable declarations.
// Any references to those variables are replaced with references to the object's field.
// Since objects are reference-counted this ensures that the variables are accessible
// as long as both the parent and the forked processes require them to be.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Fork.h"
#include "V3Ast.h"
#include "V3AstNodeExpr.h"
#include "V3Error.h"
#include "V3Global.h"
#include "V3MemberMap.h"
#include <algorithm>
#include <map>
#include <set>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
class ForkDynScopeInstance final {
public:
AstClass* m_classp = nullptr; // Class for holding variables of dynamic scope
AstClassRefDType* m_refDTypep = nullptr; // RefDType for the above
AstVar* m_handlep = nullptr; // Class handle for holding variables of dynamic scope
// True if the instance exists
bool initialized() const { return m_classp != nullptr; }
};
class ForkDynScopeFrame final {
private:
// MEMBERS
AstNodeModule* const m_modp; // Module to insert the scope into
AstNode* const m_procp; // Procedure/block associated with that dynscope
std::set<AstVar*> m_captures; // Variables to be moved into the dynscope
ForkDynScopeInstance m_instance; // Nodes to be injected into the AST to create the dynscope
public:
ForkDynScopeFrame(AstNodeModule* modp, AstNode* procp)
: m_modp{modp}
, m_procp{procp} {}
ForkDynScopeInstance& createInstancePrototype() {
UASSERT_OBJ(!m_instance.initialized(), m_procp, "Dynamic scope already instantiated.");
m_instance.m_classp
= new AstClass{m_procp->fileline(), generateDynScopeClassName(m_procp)};
m_instance.m_refDTypep
= new AstClassRefDType{m_procp->fileline(), m_instance.m_classp, nullptr};
v3Global.rootp()->typeTablep()->addTypesp(m_instance.m_refDTypep);
m_instance.m_handlep
= new AstVar{m_procp->fileline(), VVarType::BLOCKTEMP,
generateDynScopeHandleName(m_procp), m_instance.m_refDTypep};
m_instance.m_handlep->funcLocal(true);
m_instance.m_handlep->lifetime(VLifetime::AUTOMATIC);
return m_instance;
}
const ForkDynScopeInstance& instance() const { return m_instance; }
void captureVarInsert(AstVar* varp) { m_captures.insert(varp); }
bool captured(AstVar* varp) { return m_captures.count(varp) != 0; }
AstNode* procp() const { return m_procp; }
void populateClass() {
UASSERT_OBJ(m_instance.initialized(), m_procp, "No DynScope prototype");
// Move variables into the class
for (AstVar* varp : m_captures) {
if (varp->direction() == VDirection::INPUT) {
varp = varp->cloneTree(false);
varp->direction(VDirection::NONE);
} else {
varp->unlinkFrBack();
}
varp->funcLocal(false);
varp->varType(VVarType::MEMBER);
varp->lifetime(VLifetime::AUTOMATIC);
varp->usedLoopIdx(false); // No longer unrollable
m_instance.m_classp->addStmtsp(varp);
}
// Create class's constructor
AstFunc* const newp
= new AstFunc{m_instance.m_classp->fileline(), "new", nullptr, nullptr};
newp->isConstructor(true);
newp->classMethod(true);
newp->dtypep(newp->findVoidDType());
m_instance.m_classp->addStmtsp(newp);
}
void linkNodes(VMemberMap& memberMap) {
UASSERT_OBJ(m_instance.initialized(), m_procp, "No dynamic scope prototype");
UASSERT_OBJ(!linked(), m_instance.m_handlep, "Handle already linked");
if (VN_IS(m_procp, Fork)) {
linkNodesOfFork(memberMap);
return;
}
AstNode* stmtp = getProcStmts();
UASSERT(stmtp, "trying to instantiate dynamic scope while not under proc");
VNRelinker stmtpHandle;
stmtp->unlinkFrBackWithNext(&stmtpHandle);
// Find node after last variable declaration
AstNode* initp = stmtp;
while (initp && VN_IS(initp, Var)) initp = initp->nextp();
UASSERT(stmtp, "Procedure lacks body");
UASSERT(initp, "Procedure lacks statements besides declarations");
AstNew* const newp = new AstNew{m_procp->fileline(), nullptr};
newp->taskp(VN_AS(memberMap.findMember(m_instance.m_classp, "new"), NodeFTask));
newp->dtypep(m_instance.m_refDTypep);
newp->classOrPackagep(m_instance.m_classp);
AstNode* const asgnp = new AstAssign{
m_procp->fileline(),
new AstVarRef{m_procp->fileline(), m_instance.m_handlep, VAccess::WRITE}, newp};
AstNode* initsp = nullptr; // Arguments need to be copied
for (AstVar* varp : m_captures) {
if (varp->direction() != VDirection::INPUT) continue;
AstMemberSel* const memberselp = new AstMemberSel{
varp->fileline(),
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
varp->dtypep()};
memberselp->name(varp->name());
memberselp->varp(VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var));
AstNode* initAsgnp
= new AstAssign{varp->fileline(), memberselp,
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
initsp = AstNode::addNext(initsp, initAsgnp);
}
if (initsp) AstNode::addNext(asgnp, initsp);
if (initp != stmtp) {
initp->addHereThisAsNext(asgnp);
} else {
AstNode::addNext(asgnp, static_cast<AstNode*>(initp));
stmtp = asgnp;
}
AstNode::addNext(static_cast<AstNode*>(m_instance.m_handlep), stmtp);
stmtpHandle.relink(m_instance.m_handlep);
m_modp->addStmtsp(m_instance.m_classp);
}
bool linked() const { return m_instance.initialized() && m_instance.m_handlep->backp(); }
private:
AstAssign* instantiateDynScope(VMemberMap& memberMap) {
AstNew* const newp = new AstNew{m_procp->fileline(), nullptr};
newp->taskp(VN_AS(memberMap.findMember(m_instance.m_classp, "new"), NodeFTask));
newp->dtypep(m_instance.m_refDTypep);
newp->classOrPackagep(m_instance.m_classp);
return new AstAssign{
m_procp->fileline(),
new AstVarRef{m_procp->fileline(), m_instance.m_handlep, VAccess::WRITE}, newp};
}
void linkNodesOfFork(VMemberMap& memberMap) {
// Special case
AstFork* const forkp = VN_AS(m_procp, Fork);
VNRelinker forkHandle;
forkp->unlinkFrBack(&forkHandle);
AstBegin* const beginp = new AstBegin{
forkp->fileline(),
"_Vwrapped_" + (forkp->name().empty() ? cvtToHex(forkp) : forkp->name()),
m_instance.m_handlep, false, true};
forkHandle.relink(beginp);
AstNode* const instAsgnp = instantiateDynScope(memberMap);
beginp->stmtsp()->addNext(instAsgnp);
beginp->stmtsp()->addNext(forkp);
if (forkp->initsp()) {
forkp->initsp()->foreach([forkp](AstAssign* asgnp) {
asgnp->unlinkFrBack();
forkp->addHereThisAsNext(asgnp);
});
}
UASSERT_OBJ(!forkp->initsp(), forkp, "Leftover nodes in block_item_declaration");
m_modp->addStmtsp(m_instance.m_classp);
}
static string generateDynScopeClassName(const AstNode* fromp) {
string n = "__VDynScope__" + (!fromp->name().empty() ? (fromp->name() + "__") : "ANON__")
+ cvtToHex(fromp);
return n;
}
static string generateDynScopeHandleName(const AstNode* fromp) {
return "__VDynScope_" + (fromp->name().empty() ? cvtToHex(fromp) : fromp->name());
}
AstNode* getProcStmts() {
AstNode* stmtsp = nullptr;
if (!m_procp) return nullptr;
if (AstBegin* beginp = VN_CAST(m_procp, Begin)) {
stmtsp = beginp->stmtsp();
} else if (AstNodeFTask* taskp = VN_CAST(m_procp, NodeFTask)) {
stmtsp = taskp->stmtsp();
} else {
m_procp->v3fatalSrc("m_procp is not a begin block or a procedure");
}
return stmtsp;
}
};
//######################################################################
// Dynamic scope visitor, creates classes and objects for dynamic scoping of variables and
// replaces references to varibles that need a dynamic scope with references to object's
// members
class DynScopeVisitor final : public VNVisitor {
private:
// NODE STATE
// AstVar::user1() -> int, timing-control fork nesting level of that variable
// AstVarRef::user2() -> bool, 1 = Node is a class handle reference. The handle gets
// modified in the context of this reference.
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
// STATE
AstNodeModule* m_modp = nullptr; // Module we are currently under
AstNode* m_procp = nullptr; // Function/task/block we are currently under
std::map<AstNode*, ForkDynScopeFrame*>
m_frames; // Mapping from nodes to related DynScopeFrames
VMemberMap m_memberMap; // Class member look-up
int m_forkDepth = 0; // Number of asynchronous forks we are currently under
bool m_afterTimingControl = false; // A timing control might've be executed in the current
// process
// METHODS
ForkDynScopeFrame* frameOf(AstNode* nodep) {
auto frameIt = m_frames.find(nodep);
if (frameIt == m_frames.end()) return nullptr;
return frameIt->second;
}
const ForkDynScopeFrame* frameOf(AstNode* nodep) const {
auto frameIt = m_frames.find(nodep);
if (frameIt == m_frames.end()) return nullptr;
return frameIt->second;
}
ForkDynScopeFrame* pushDynScopeFrame(AstNode* procp) {
ForkDynScopeFrame* const framep = new ForkDynScopeFrame{m_modp, procp};
auto r = m_frames.emplace(std::make_pair(procp, framep));
UASSERT_OBJ(r.second, m_modp, "Procedure already contains a frame");
return framep;
}
void replaceWithMemberSel(AstVarRef* refp, const ForkDynScopeInstance& dynScope) {
VNRelinker handle;
refp->unlinkFrBack(&handle);
AstMemberSel* const membersel = new AstMemberSel{
refp->fileline(), new AstVarRef{refp->fileline(), dynScope.m_handlep, refp->access()},
refp->dtypep()};
membersel->name(refp->varp()->name());
if (refp->varp()->direction() == VDirection::INPUT) {
membersel->varp(
VN_AS(m_memberMap.findMember(dynScope.m_classp, refp->varp()->name()), Var));
} else {
membersel->varp(refp->varp());
}
handle.relink(membersel);
VL_DO_DANGLING(refp->deleteTree(), refp);
}
static bool hasAsyncFork(AstNode* nodep) {
bool afork = false;
nodep->foreach([&](AstFork* forkp) {
if (!forkp->joinType().join()) afork = true;
});
return afork;
}
void bindNodeToDynScope(AstNode* nodep, ForkDynScopeFrame* frame) {
m_frames.emplace(std::make_pair(nodep, frame));
}
bool needsDynScope(const AstVarRef* refp) const {
return
// Can this variable escape the scope
((m_forkDepth > refp->varp()->user1()) && refp->varp()->isFuncLocal())
&& (
// Is it mutated
(refp->varp()->isClassHandleValue() ? refp->user2() : refp->access().isWriteOrRW())
// Or is it after a timing-control event
|| m_afterTimingControl);
}
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
if (!VN_IS(nodep, Class)) m_modp = nodep;
iterateChildren(nodep);
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_procp);
m_procp = nodep;
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
iterateChildren(nodep);
}
void visit(AstBegin* nodep) override {
VL_RESTORER(m_procp);
m_procp = nodep;
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
iterateChildren(nodep);
}
void visit(AstFork* nodep) override {
VL_RESTORER(m_forkDepth);
if (!nodep->joinType().join()) ++m_forkDepth;
const bool oldAfterTimingControl = m_afterTimingControl;
ForkDynScopeFrame* framep = nullptr;
if (nodep->initsp()) framep = pushDynScopeFrame(nodep);
for (AstNode* stmtp = nodep->initsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* varp = VN_CAST(stmtp, Var)) {
// This can be probably optimized to detect cases in which dynscopes
// could be avoided
if (!framep->instance().initialized()) framep->createInstancePrototype();
framep->captureVarInsert(varp);
bindNodeToDynScope(varp, framep);
} else {
AstAssign* const asgnp = VN_CAST(stmtp, Assign);
UASSERT_OBJ(asgnp, stmtp,
"Invalid node under block item initialization part of fork");
bindNodeToDynScope(asgnp->lhsp(), framep);
iterate(asgnp->rhsp());
}
}
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
m_afterTimingControl = false;
iterate(stmtp);
}
m_afterTimingControl = oldAfterTimingControl;
if (nodep->isTimingControl()) m_afterTimingControl = true;
}
void visit(AstNodeFTaskRef* nodep) override {
visit(static_cast<AstNodeExpr*>(nodep));
// We are before V3Timing, so unfortnately we need to treat any calls as suspending,
// just to be safe. This might be improved if we could propagate suspendability
// before doing all the other timing-related stuff.
m_afterTimingControl = true;
}
void visit(AstVar* nodep) override {
nodep->user1(m_forkDepth);
ForkDynScopeFrame* const framep = frameOf(m_procp);
if (!framep) return; // Cannot be legally referenced from a fork
bindNodeToDynScope(nodep, framep);
}
void visit(AstVarRef* nodep) override {
ForkDynScopeFrame* const framep = frameOf(nodep->varp());
if (!framep) return;
if (needsDynScope(nodep)) {
if (!framep->instance().initialized()) framep->createInstancePrototype();
framep->captureVarInsert(nodep->varp());
}
bindNodeToDynScope(nodep, framep);
}
void visit(AstAssign* nodep) override {
if (VN_IS(nodep->lhsp(), VarRef) && nodep->lhsp()->isClassHandleValue()) {
nodep->lhsp()->user2(true);
}
visit(static_cast<AstNodeStmt*>(nodep));
}
void visit(AstNode* nodep) override {
if (nodep->isTimingControl()) m_afterTimingControl = true;
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit DynScopeVisitor(AstNetlist* nodep) {
// Create Dynamic scope class prototypes and objects
visit(nodep);
// Commit changes to AST
bool typesAdded = false;
for (auto frameIt : m_frames) {
ForkDynScopeFrame* frame = frameIt.second;
if (!frame->instance().initialized()) continue;
if (!frame->linked()) {
frame->populateClass();
frame->linkNodes(m_memberMap);
typesAdded = true;
}
if (AstVarRef* refp = VN_CAST(frameIt.first, VarRef)) {
if (frame->captured(refp->varp())) replaceWithMemberSel(refp, frame->instance());
}
}
if (typesAdded) v3Global.rootp()->typeTablep()->repairCache();
}
~DynScopeVisitor() override = default;
};
//######################################################################
// Fork visitor, transforms asynchronous blocks into separate tasks
class ForkVisitor final : public VNVisitor {
private:
// NODE STATE
// AstNode::user1() -> bool, 1 = Node was created as a call to an asynchronous task
// AstVarRef::user2() -> bool, 1 = Node is a class handle reference. The handle gets
// modified in the context of this reference.
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
// STATE
AstNodeModule* m_modp = nullptr; // Class/module we are currently under
int m_forkDepth = 0; // Nesting level of asynchronous forks
bool m_newProcess = false; // True if we are directly under an asynchronous fork.
AstVar* m_capturedVarsp = nullptr; // Local copies of captured variables
std::set<AstVar*> m_forkLocalsp; // Variables local to a given fork
AstArg* m_capturedVarRefsp = nullptr; // References to captured variables (as args)
// METHODS
AstVar* captureRef(AstVarRef* refp) {
AstVar* varp = nullptr;
for (varp = m_capturedVarsp; varp; varp = VN_AS(varp->nextp(), Var))
if (varp->name() == refp->name()) break;
if (!varp) {
// Create a local copy for a capture
varp = new AstVar{refp->fileline(), VVarType::BLOCKTEMP, refp->name(), refp->dtypep()};
varp->direction(VDirection::INPUT);
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC);
m_capturedVarsp = AstNode::addNext(m_capturedVarsp, varp);
// Use the original ref as an argument for call
m_capturedVarRefsp
= AstNode::addNext(m_capturedVarRefsp, new AstArg{refp->fileline(), refp->name(),
refp->cloneTree(false)});
}
return varp;
}
AstTask* makeTask(FileLine* fl, AstNode* stmtsp, string name) {
stmtsp = AstNode::addNext(static_cast<AstNode*>(m_capturedVarsp), stmtsp);
AstTask* const taskp = new AstTask{fl, name, stmtsp};
return taskp;
}
string generateTaskName(AstNode* fromp, const string& kind) {
return "__V" + kind + (!fromp->name().empty() ? (fromp->name() + "__") : "UNNAMED__")
+ cvtToHex(fromp);
}
void visitTaskifiable(AstNode* nodep) {
if (!m_newProcess || nodep->user1()) {
VL_RESTORER(m_forkDepth);
if (nodep->user1()) {
UASSERT(m_forkDepth > 0, "Wrong fork depth!");
--m_forkDepth;
}
iterateChildren(nodep);
return;
}
VL_RESTORER(m_capturedVarsp);
VL_RESTORER(m_capturedVarRefsp);
VL_RESTORER(m_newProcess);
m_capturedVarsp = nullptr;
m_capturedVarRefsp = nullptr;
m_newProcess = false;
iterateChildren(nodep);
// If there are no captures, there's no need to taskify
if (m_forkLocalsp.empty() && (m_capturedVarsp == nullptr) && !v3Global.opt.fTaskifyAll())
return;
VNRelinker handle;
AstTask* taskp = nullptr;
if (AstBegin* beginp = VN_CAST(nodep, Begin)) {
UASSERT(beginp->stmtsp(), "No stmtsp\n");
const string taskName = generateTaskName(beginp, "__FORK_BEGIN_");
taskp
= makeTask(beginp->fileline(), beginp->stmtsp()->unlinkFrBackWithNext(), taskName);
beginp->unlinkFrBack(&handle);
VL_DO_DANGLING(beginp->deleteTree(), beginp);
} else if (AstNodeStmt* stmtp = VN_CAST(nodep, NodeStmt)) {
const string taskName = generateTaskName(stmtp, "__FORK_STMT_");
taskp = makeTask(stmtp->fileline(), stmtp->unlinkFrBack(&handle), taskName);
} else if (AstFork* forkp = VN_CAST(nodep, Fork)) {
const string taskName = generateTaskName(forkp, "__FORK_NESTED_");
taskp = makeTask(forkp->fileline(), forkp->unlinkFrBack(&handle), taskName);
}
m_modp->addStmtsp(taskp);
AstTaskRef* const taskrefp
= new AstTaskRef{nodep->fileline(), taskp->name(), m_capturedVarRefsp};
taskrefp->taskp(taskp);
AstStmtExpr* const taskcallp = taskrefp->makeStmt();
// Replaced nodes will be revisited, so we don't need to "lift" the arguments
// as captures in case of nested forks.
handle.relink(taskcallp);
taskcallp->user1(true);
}
// VISITORS
void visit(AstFork* nodep) override {
bool nested = m_newProcess;
VL_RESTORER(m_forkLocalsp);
VL_RESTORER(m_newProcess);
VL_RESTORER(m_forkDepth)
if (!nodep->joinType().join()) {
++m_forkDepth;
m_newProcess = true;
m_forkLocalsp.clear();
// Nested forks get moved into separate tasks
if (nested) {
visitTaskifiable(nodep);
return;
}
} else {
m_newProcess = false;
}
iterateChildren(nodep);
}
void visit(AstBegin* nodep) override { visitTaskifiable(nodep); }
void visit(AstNodeStmt* nodep) override { visitTaskifiable(nodep); }
void visit(AstVar* nodep) override {
if (m_forkDepth) m_forkLocalsp.insert(nodep);
}
void visit(AstVarRef* nodep) override {
// VL_KEEP_THIS ensures that we hold a handle to the class
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
&& !nodep->varp()->lifetime().isStatic()) {
if (nodep->access().isWriteOrRW()
&& (!nodep->isClassHandleValue() || nodep->user2())) {
nodep->v3warn(
E_LIFETIME,
"Invalid reference: Process might outlive variable `"
<< nodep->varp()->name() << "`.\n"
<< nodep->varp()->warnMore()
<< "... Suggest use it as read-only to initialize a local copy at the "
"beginning of the process, or declare it as static. It is also "
"possible to refer by reference to objects and their members.");
return;
}
UASSERT_OBJ(
!nodep->varp()->lifetime().isNone(), nodep,
"Variable's lifetime is unknown. Can't determine if a capture is necessary.");
AstVar* const varp = captureRef(nodep);
nodep->varp(varp);
}
}
void visit(AstAssign* nodep) override {
if (VN_IS(nodep->lhsp(), VarRef) && nodep->lhsp()->isClassHandleValue()) {
nodep->lhsp()->user2(true);
}
visit(static_cast<AstNodeStmt*>(nodep));
}
void visit(AstThisRef* nodep) override { return; }
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
m_modp = nodep;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
VL_RESTORER(m_newProcess)
VL_RESTORER(m_forkDepth);
if (nodep->user1()) --m_forkDepth;
m_newProcess = false;
iterateChildren(nodep);
}
public:
// CONSTRUCTORS
explicit ForkVisitor(AstNetlist* nodep) { visit(nodep); }
~ForkVisitor() override = default;
};
//######################################################################
// Fork class functions
void V3Fork::makeDynamicScopes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DynScopeVisitor{nodep}; }
V3Global::dumpCheckGlobalTree("fork_dynscope", 0, dumpTreeLevel() >= 3);
}
void V3Fork::makeTasks(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ForkVisitor{nodep}; }
V3Global::dumpCheckGlobalTree("fork", 0, dumpTreeLevel() >= 3);
}
+38
View File
@@ -0,0 +1,38 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3FORK_H_
#define VERILATOR_V3FORK_H_
#include "config_build.h"
#include "verilatedos.h"
class AstNetlist;
//============================================================================
class V3Fork final {
public:
// Move/copy variables to "anonymous" objects if their lifetime might exceed the scope of a
// procedure that declared them. Update the references apropriately.
static void makeDynamicScopes(AstNetlist* nodep);
// Create tasks out of blocks/statments that can outlive processes in which they were forked.
// Return value: number of tasks created
static void makeTasks(AstNetlist* nodep);
};
#endif // Guard
+15 -22
View File
@@ -65,6 +65,7 @@ public:
// Support classes
class GateEitherVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(GateEitherVertex, V3GraphVertex)
AstScope* const m_scopep; // Scope vertex refers to
bool m_reducible = true; // True if this node should be able to be eliminated
bool m_dedupable = true; // True if this node should be able to be deduped
@@ -122,6 +123,7 @@ public:
};
class GateVarVertex final : public GateEitherVertex {
VL_RTTI_IMPL(GateVarVertex, GateEitherVertex)
AstVarScope* const m_varScp;
bool m_isTop = false;
bool m_isClock = false;
@@ -164,6 +166,7 @@ public:
};
class GateLogicVertex final : public GateEitherVertex {
VL_RTTI_IMPL(GateLogicVertex, GateEitherVertex)
AstNode* const m_nodep;
AstActive* const m_activep; // Under what active; nullptr is ok (under cfunc or such)
const bool m_slow; // In slow block
@@ -176,9 +179,7 @@ public:
, m_slow{slow} {}
~GateLogicVertex() override = default;
// ACCESSORS
string name() const override VL_MT_STABLE {
return (cvtToHex(m_nodep) + "@" + scopep()->prettyName());
}
string name() const override { return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); }
string dotColor() const override { return "purple"; }
FileLine* fileline() const override { return nodep()->fileline(); }
AstNode* nodep() const { return m_nodep; }
@@ -333,7 +334,6 @@ private:
AstActive* m_activep = nullptr; // Current active
bool m_activeReducible = true; // Is activation block reducible?
bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks
bool m_inExprStmt = false; // Underneath ExprStmt; don't optimize LHS vars
bool m_inSlow = false; // Inside a slow structure
std::vector<AstNode*> m_optimized; // Logic blocks optimized
@@ -499,10 +499,6 @@ private:
// the weight will increase
if (nodep->access().isWriteOrRW()) {
new V3GraphEdge{&m_graph, m_logicVertexp, vvertexp, 1};
if (m_inExprStmt) {
m_logicVertexp->clearReducibleAndDedupable("LHS var in ExprStmt");
m_logicVertexp->setConsumed("LHS var in ExprStmt");
}
}
if (nodep->access().isReadOrRW()) {
new V3GraphEdge{&m_graph, vvertexp, m_logicVertexp, 1};
@@ -518,11 +514,6 @@ private:
iterateNewStmt(nodep, "User C Function", "User C Function");
}
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
void visit(AstExprStmt* nodep) override {
VL_RESTORER(m_inExprStmt);
m_inExprStmt = true;
iterateChildren(nodep);
}
void visit(AstSenItem* nodep) override {
VL_RESTORER(m_inSenItem);
m_inSenItem = true;
@@ -580,7 +571,7 @@ public:
void GateVisitor::optimizeSignals(bool allowMultiIn) {
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp);
GateVarVertex* const vvertexp = itp->cast<GateVarVertex>();
// Consider "inlining" variables
if (!vvertexp) continue;
@@ -722,7 +713,7 @@ void GateVisitor::consumedMove() {
// We need the "usually" block logic to do a better job at this
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp;
vertexp = vertexp->verticesNextp()) {
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
if (const GateVarVertex* const vvertexp = vertexp->cast<GateVarVertex>()) {
if (!vvertexp->consumed() && !vvertexp->user()) {
UINFO(8, "Unconsumed " << vvertexp->varScp() << endl);
}
@@ -746,7 +737,7 @@ void GateVisitor::consumedMove() {
void GateVisitor::warnSignals() {
AstNode::user2ClearTree();
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (const GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (const GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
const AstVarScope* const vscp = vvertexp->varScp();
const AstNode* const sp = vvertexp->rstSyncNodep();
const AstNode* const ap = vvertexp->rstAsyncNodep();
@@ -907,6 +898,7 @@ class GateDedupeVarVisitor final : public VNVisitor {
// (Note, the IF must be the only node under the always,
// and the assign must be the only node under the if, other than the ifcond)
// Any other ordering or node type, except for an AstComment, makes it not dedupable
// AstExprStmt in the subtree of a node also makes the node not dedupable.
private:
// STATE
GateDedupeHash m_ghash; // Hash used to find dupes of rhs of assign
@@ -922,6 +914,7 @@ private:
// non-blocking statements, but erring on side of caution here
if (!m_assignp) {
m_assignp = assignp;
m_dedupable = !assignp->exists([&](AstExprStmt*) { return true; });
} else {
m_dedupable = false;
}
@@ -946,6 +939,7 @@ private:
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();
m_dedupable = !m_ifCondp->exists([&](AstExprStmt*) { return true; });
iterateAndNextNull(ifp->thensp());
} else {
m_dedupable = false;
@@ -1145,14 +1139,14 @@ void GateVisitor::dedupe() {
// Traverse starting from each of the clocks
UINFO(9, "Gate dedupe() clocks:\n");
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
if (vvertexp->isClock()) deduper.dedupeTree(vvertexp);
}
}
// Traverse starting from each of the outputs
UINFO(9, "Gate dedupe() outputs:\n");
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isWritable()) {
deduper.dedupeTree(vvertexp);
}
@@ -1196,8 +1190,7 @@ private:
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep;) {
V3GraphEdge* oldedgep = edgep;
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
if (GateLogicVertex* const lvertexp
= dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
if (GateLogicVertex* const lvertexp = oldedgep->fromp()->cast<GateLogicVertex>()) {
if (AstNodeAssign* const assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
// if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
@@ -1283,7 +1276,7 @@ void GateVisitor::mergeAssigns() {
UINFO(6, "mergeAssigns\n");
GateMergeAssignsGraphVisitor merger{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vvertexp = itp->cast<GateVarVertex>()) {
merger.mergeAssignsTree(vvertexp);
}
}
@@ -1469,7 +1462,7 @@ void GateVisitor::decomposeClkVectors() {
AstNode::user2ClearTree();
GateClkDecompGraphVisitor decomposer{&m_graph};
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (GateVarVertex* const vertp = dynamic_cast<GateVarVertex*>(itp)) {
if (GateVarVertex* const vertp = itp->cast<GateVarVertex>()) {
const AstVarScope* const vsp = vertp->varScp();
if (vsp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) {
if (vsp->varp()->width() > 1) {
+3 -3
View File
@@ -19,8 +19,8 @@
// clang-format off
#include "config_build.h"
#ifndef HAVE_CONFIG_BUILD
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't."
#ifndef HAVE_CONFIG_PACKAGE
# error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
#endif
// clang-format on
@@ -102,7 +102,7 @@ class V3Global final {
bool m_assertDTypesResolved = false; // Tree should have dtypep()'s
bool m_assertScoped = false; // Tree is scoped
bool m_constRemoveXs = false; // Const needs to strip any Xs
// Experimenting with always requiring heavy, see (#2701)
// Experimenting with always requiring heavy, see issue #2701
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
bool m_dpi = false; // Need __Dpi include files
bool m_hasEvents = false; // Design uses SystemVerilog named events
+3 -23
View File
@@ -92,26 +92,6 @@ void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
bool V3GraphVertex::inSize1() const { return !inEmpty() && !inBeginp()->inNextp(); }
bool V3GraphVertex::outSize1() const { return !outEmpty() && !outBeginp()->outNextp(); }
uint32_t V3GraphVertex::inHash() const {
// We want the same hash ignoring the order of edges.
// So we need an associative operator, like XOR.
// However with XOR multiple edges to the same source will cancel out,
// so we use ADD. (Generally call this only after removing duplicates though)
uint32_t hash = 0;
for (V3GraphEdge* edgep = this->inBeginp(); edgep; edgep = edgep->inNextp()) {
hash += cvtToHash(edgep->fromp());
}
return hash;
}
uint32_t V3GraphVertex::outHash() const {
uint32_t hash = 0;
for (V3GraphEdge* edgep = this->outBeginp(); edgep; edgep = edgep->outNextp()) {
hash += cvtToHash(edgep->top());
}
return hash;
}
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
@@ -129,7 +109,7 @@ V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVerte
}
// cppcheck-has-bug-suppress constParameter
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
std::ostringstream nsstr;
nsstr << str.str();
if (debug()) {
@@ -139,11 +119,11 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Erro
if (FileLine* const flp = fileline()) {
flp->v3errorEnd(nsstr);
} else {
V3Error::s().v3errorEnd(nsstr);
V3Error::v3errorEnd(nsstr);
}
}
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const
VL_REQUIRES(V3Error::s().m_mutex) {
VL_RELEASE(V3Error::s().m_mutex) {
v3errorEnd(str);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
+72 -4
View File
@@ -22,6 +22,7 @@
#include "V3Error.h"
#include "V3List.h"
#include "V3Rtti.h"
#include <algorithm>
@@ -173,6 +174,7 @@ public:
//============================================================================
class V3GraphVertex VL_NOT_FINAL {
VL_RTTI_IMPL_BASE(V3GraphVertex)
// Vertices may be a 'gate'/wire statement OR a variable
protected:
friend class V3Graph;
@@ -209,6 +211,40 @@ public:
void unlinkEdges(V3Graph* graphp);
void unlinkDelete(V3Graph* graphp);
// METHODS
// Return true iff of type T
template <typename T>
bool is() const {
static_assert(std::is_base_of<V3GraphVertex, T>::value,
"'T' must be a subtype of V3GraphVertex");
static_assert(std::is_same<typename std::remove_cv<T>::type,
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
"Missing VL_RTTI_IMPL(...) call in 'T'");
return this->isInstanceOfClassWithId(T::rttiClassId());
}
// Return cast to subtype T and assert of that type
template <typename T>
T* as() {
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
return static_cast<T*>(this);
}
template <typename T>
const T* as() const {
UASSERT_OBJ(is<T>(), this, "V3GraphVertex is not of expected type");
return static_cast<const T*>(this);
}
// Return cast to subtype T, else nullptr if different type
template <typename T>
T* cast() {
return is<T>() ? static_cast<T*>(this) : nullptr;
}
template <typename T>
const T* cast() const {
return is<T>() ? static_cast<const T*>(this) : nullptr;
}
// ACCESSORS
virtual string name() const { return ""; }
virtual string dotColor() const { return "black"; }
@@ -240,16 +276,14 @@ public:
V3GraphEdge* inBeginp() const { return m_ins.begin(); }
bool inEmpty() const { return inBeginp() == nullptr; }
bool inSize1() const;
uint32_t inHash() const;
V3GraphEdge* outBeginp() const { return m_outs.begin(); }
bool outEmpty() const { return outBeginp() == nullptr; }
bool outSize1() const;
uint32_t outHash() const;
V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); }
// METHODS
/// Error reporting
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
void v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
/// 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.
@@ -262,6 +296,7 @@ std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp);
//============================================================================
class V3GraphEdge VL_NOT_FINAL {
VL_RTTI_IMPL_BASE(V3GraphEdge)
// Wires/variables aren't edges. Edges have only a single to/from vertex
public:
// ENUMS
@@ -308,6 +343,39 @@ public:
}
virtual ~V3GraphEdge() = default;
// METHODS
// Return true iff of type T
template <typename T>
bool is() const {
static_assert(std::is_base_of<V3GraphEdge, T>::value,
"'T' must be a subtype of V3GraphEdge");
static_assert(std::is_same<typename std::remove_cv<T>::type,
VTypeListFront<typename T::RttiThisAndBaseClassesList>>::value,
"Missing VL_RTTI_IMPL(...) call in 'T'");
return this->isInstanceOfClassWithId(T::rttiClassId());
}
// Return cast to subtype T and assert of that type
template <typename T>
T* as() {
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
return static_cast<T*>(this);
}
template <typename T>
const T* as() const {
UASSERT(is<T>(), "V3GraphEdge is not of expected type");
return static_cast<const T*>(this);
}
// Return cast to subtype T, else nullptr if different type
template <typename T>
T* cast() {
return is<T>() ? static_cast<T*>(this) : nullptr;
}
template <typename T>
const T* cast() const {
return is<T>() ? static_cast<const T*>(this) : nullptr;
}
virtual string name() const { return m_fromp->name() + "->" + m_top->name(); }
virtual string dotLabel() const { return ""; }
virtual string dotColor() const { return cutable() ? "yellowGreen" : "red"; }
+2
View File
@@ -32,6 +32,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Break the minimal number of backward edges to make the graph acyclic
class GraphAcycVertex final : public V3GraphVertex {
VL_RTTI_IMPL(GraphAcycVertex, V3GraphVertex)
// user() is used for various sub-algorithm pieces
V3GraphVertex* const m_origVertexp; // Pointer to first vertex this represents
protected:
@@ -56,6 +57,7 @@ public:
//--------------------------------------------------------------------
class GraphAcycEdge final : public V3GraphEdge {
VL_RTTI_IMPL(GraphAcycEdge, V3GraphEdge)
// userp() is always used to point to the head original graph edge
private:
using OrigEdgeList = std::list<V3GraphEdge*>; // List of orig edges, see also GraphAcyc's decl
+1 -2
View File
@@ -52,6 +52,7 @@ public:
// Vertices and nodes
class V3GraphTestVertex VL_NOT_FINAL : public V3GraphVertex {
VL_RTTI_IMPL(V3GraphTestVertex, V3GraphVertex)
const string m_name;
public:
@@ -293,10 +294,8 @@ public:
void V3Graph::selfTest() {
// Execute all of the tests
UINFO(2, __FUNCTION__ << ": " << endl);
// clang-format off
{ V3GraphTestStrong{}.run(); }
{ V3GraphTestAcyc{}.run(); }
{ V3GraphTestVars{}.run(); }
{ V3GraphTestImport{}.run(); }
// clang-format on
}
+7 -1
View File
@@ -174,6 +174,9 @@ private:
iterateConstNull(nodep->refDTypep());
});
}
void visit(AstStreamDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
void visit(AstVoidDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {});
}
@@ -207,7 +210,7 @@ private:
iterateConstNull(nodep->varScopep());
} else {
iterateConstNull(nodep->varp());
m_hash += nodep->selfPointer();
m_hash += nodep->selfPointer().asString();
}
});
}
@@ -269,6 +272,9 @@ private:
iterateConstNull(nodep->sensesp());
});
}
void visit(AstCLocalScope* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {});
}
void visit(AstCoverInc* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateConstNull(nodep->declp());
-1
View File
@@ -384,7 +384,6 @@ private:
nodep->v3fatalSrc("Null connection?");
}
}
nodep->name(nodep->varp()->name());
}
void visit(AstVarXRef* nodep) override {
// Track what scope it was originally under so V3LinkDot can resolve it
-6
View File
@@ -147,12 +147,6 @@ private:
iterateAndNextConstNull(nodep->lsbp());
iterateAndNextConstNull(nodep->widthp());
}
void visit(AstSliceSel* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("AstSliceSel unhandled");
}
void visit(AstMemberSel* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("AstMemberSel unhandled");
}
void visit(AstConcat* nodep) override {
if (m_ignoreRemaining) return;
// Nop.
+1 -1
View File
@@ -432,7 +432,7 @@ private:
// UINFO(4, " CFUNC " << nodep << endl);
if (!m_tracingCall && !nodep->entryPoint()) return;
m_tracingCall = false;
if (nodep->dpiImportPrototype() && !nodep->pure()) {
if (nodep->dpiImportPrototype() && !nodep->dpiPure()) {
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
}
iterateChildren(nodep);
+1 -1
View File
@@ -333,7 +333,7 @@ private:
}
for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp;
mtaskVxp = mtaskVxp->verticesNextp()) {
const ExecMTask* const mtaskp = dynamic_cast<ExecMTask*>(mtaskVxp);
const ExecMTask* const mtaskp = mtaskVxp->as<ExecMTask>();
m_execMTaskp = mtaskp;
m_sequence = 0;
iterate(mtaskp->bodyp());
+4 -2
View File
@@ -52,6 +52,7 @@ public:
};
class LinkCellsVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LinkCellsVertex, V3GraphVertex)
AstNodeModule* const m_modp;
public:
@@ -69,6 +70,7 @@ public:
};
class LibraryVertex final : public V3GraphVertex {
VL_RTTI_IMPL(LibraryVertex, V3GraphVertex)
public:
explicit LibraryVertex(V3Graph* graphp)
: V3GraphVertex{graphp} {}
@@ -77,7 +79,7 @@ public:
};
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
if (const LinkCellsVertex* const vvertexp = vertexp->cast<LinkCellsVertex>()) {
vvertexp->modp()->v3warn(E_UNSUPPORTED,
"Unsupported: Recursive multiple modules (module instantiates "
"something leading back to itself): "
@@ -171,7 +173,7 @@ private:
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
m_graph.rank();
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) {
if (const LinkCellsVertex* const vvertexp = itp->cast<LinkCellsVertex>()) {
// +1 so we leave level 1 for the new wrapper we'll make in a moment
AstNodeModule* const modp = vvertexp->modp();
modp->level(vvertexp->rank() + 1);
+191 -81
View File
@@ -69,6 +69,7 @@
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3MemberMap.h"
#include "V3String.h"
#include "V3SymTable.h"
@@ -215,6 +216,7 @@ public:
VSymGraph* symsp() { return &m_syms; }
int stepNumber() const { return int(m_step); }
bool forPrimary() const { return m_step == LDS_PRIMARY; }
bool forParamed() const { return m_step == LDS_PARAMED; }
bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; }
bool forScopeCreation() const { return m_step == LDS_SCOPED; }
@@ -751,6 +753,8 @@ class LinkDotFindVisitor final : public VNVisitor {
// METHODS
void makeImplicitNew(AstClass* nodep) {
AstFunc* const newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
// If needed, super.new() call is added the 2nd pass of V3LinkDotResolve,
// because base classes are already resolved there.
newp->isConstructor(true);
nodep->addMembersp(newp);
UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl);
@@ -1117,9 +1121,9 @@ class LinkDotFindVisitor final : public VNVisitor {
m_statep->insertSym(m_curSymp, newvarp->name(), newvarp,
nullptr /*classOrPackagep*/);
}
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
iterateChildren(nodep);
m_ftaskp = nullptr;
}
}
void visit(AstClocking* nodep) override {
@@ -1347,7 +1351,7 @@ class LinkDotFindVisitor final : public VNVisitor {
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) {
foundp = m_modSymp; // Conflicts with modname?
}
AstEnumItem* const findvarp = foundp ? VN_AS(foundp->nodep(), EnumItem) : nullptr;
AstEnumItem* const findvarp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : nullptr;
bool ins = false;
if (!foundp) {
ins = true;
@@ -1371,7 +1375,7 @@ class LinkDotFindVisitor final : public VNVisitor {
<< nodep->warnContextPrimary() << '\n'
<< foundp->nodep()->warnOther()
<< "... Location of original declaration\n"
<< nodep->warnContextSecondary());
<< foundp->nodep()->warnContextSecondary());
}
ins = true;
}
@@ -1612,6 +1616,7 @@ private:
AstPin* const pinp = new AstPin{nodep->fileline(),
-1, // Pin# not relevant
nodep->name(), exprp};
pinp->param(true);
cellp->addParamsp(pinp);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
@@ -2025,6 +2030,7 @@ private:
// (except the default instances)
// They are added to the set only in linkDotPrimary.
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
bool m_explicitSuperNew = false; // Hit a "super.new" call inside a "new" function
struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution
@@ -2060,40 +2066,38 @@ private:
} m_ds; // State to preserve across recursions
// METHODS - Variables
void createImplicitVar(VSymEnt* /*lookupSymp*/, AstVarRef* nodep, AstNodeModule* modp,
VSymEnt* moduleSymp, bool noWarn) {
AstVar* createImplicitVar(VSymEnt* /*lookupSymp*/, AstParseRef* nodep, AstNodeModule* modp,
VSymEnt* moduleSymp, bool noWarn) {
// Create implicit after warning
if (!nodep->varp()) {
if (!noWarn) {
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
LinkNodeMatcherVar{});
nodep->v3error("Signal definition not found, and implicit disabled with "
"`default_nettype: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
if (!noWarn) {
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
LinkNodeMatcherVar{});
nodep->v3error("Signal definition not found, and implicit disabled with "
"`default_nettype: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
// Bypass looking for suggestions if IMPLICIT is turned off
// as there could be thousands of these suppressed in large netlists
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
LinkNodeMatcherVar{});
nodep->v3warn(IMPLICIT,
"Signal definition not found, creating implicitly: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
}
AstVar* const newp = new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(),
VFlagLogicPacked{}, 1};
newp->trace(modp->modTrace());
nodep->varp(newp);
modp->addStmtsp(newp);
// Link it to signal list, must add the variable under the module;
// current scope might be lower now
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/);
// Bypass looking for suggestions if IMPLICIT is turned off
// as there could be thousands of these suppressed in large netlists
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
LinkNodeMatcherVar{});
nodep->v3warn(IMPLICIT,
"Signal definition not found, creating implicitly: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
}
AstVar* const newp
= new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(), VFlagLogicPacked{}, 1};
newp->trace(modp->modTrace());
modp->addStmtsp(newp);
// Link it to signal list, must add the variable under the module;
// current scope might be lower now
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/);
return newp;
}
AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, VAccess access) {
// Return a variable if possible, auto converting a modport to variable
@@ -2112,6 +2116,25 @@ private:
return nullptr;
}
}
AstNodeStmt* addImplicitSuperNewCall(AstFunc* nodep) {
// Returns the added node
FileLine* const fl = nodep->fileline();
AstDot* const superNewp
= new AstDot{fl, false, new AstParseRef{fl, VParseRefExp::PX_ROOT, "super"},
new AstNew{fl, nullptr}};
AstNodeStmt* const superNewStmtp = superNewp->makeStmt();
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
// super.new shall be the first statement (section 8.15 of IEEE Std 1800-2017)
// but some nodes (such as variable declarations and typedefs) should stay before
if (VN_IS(stmtp, NodeStmt)) {
stmtp->addHereThisAsNext(superNewStmtp);
return superNewStmtp;
}
}
// There were no statements
nodep->addStmtsp(superNewStmtp);
return superNewStmtp;
}
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) {
nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ());
@@ -2120,7 +2143,7 @@ private:
void checkNoDot(AstNode* nodep) {
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
// UINFO(9, "ds=" << m_ds.ascii() << endl);
nodep->v3error("Syntax Error: Not expecting " << nodep->type() << " under a "
nodep->v3error("Syntax error: Not expecting " << nodep->type() << " under a "
<< nodep->backp()->type()
<< " in dotted expression");
m_ds.m_dotErr = true;
@@ -2223,11 +2246,30 @@ private:
}
}
void importSymbolsFromExtended(AstClass* const nodep, AstClassExtends* const cextp) {
AstClass* const classp = cextp->classp();
VSymEnt* const srcp = m_statep->getNodeSym(classp);
if (classp->isInterfaceClass()) importImplementsClass(nodep, srcp, classp);
AstClass* const baseClassp = cextp->classp();
VSymEnt* const srcp = m_statep->getNodeSym(baseClassp);
if (baseClassp->isInterfaceClass()) importImplementsClass(nodep, srcp, baseClassp);
if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp);
}
void classExtendImport(AstClass* nodep) {
// A class reference might be to a class that is later in Ast due to
// e.g. parmaeterization or referring to a "class (type T) extends T"
// Resolve it so later Class:: references into its base classes work
VL_RESTORER(m_ds);
VSymEnt* const srcp = m_statep->getNodeSym(nodep);
m_ds.init(srcp);
iterate(nodep);
}
bool checkPinRef(AstPin* pinp, VVarType refVarType) {
// In instantiations of modules/ifaces, we shouldn't connect port pins to submodule's
// parameters or vice versa
return pinp->param() == refVarType.isParam();
}
void updateVarUse(AstVar* nodep) {
// Avoid dotted.PARAM false positive when in a parameter block
// that is if ()'ed off by same dotted name as another block
if (nodep && nodep->isParam()) nodep->usedParam(true);
}
// VISITs
void visit(AstNetlist* nodep) override {
@@ -2322,7 +2364,32 @@ private:
UASSERT_OBJ(m_pinSymp, nodep, "Pin not under instance?");
VSymEnt* const foundp = m_pinSymp->findIdFlat(nodep->name());
const char* const whatp = nodep->param() ? "parameter pin" : "pin";
if (!foundp) {
bool pinCheckFail = false;
if (foundp) {
if (AstVar* const 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->prettyNameQ());
} else if (!checkPinRef(nodep, refp->varType())) {
pinCheckFail = true;
} else {
nodep->modVarp(refp);
markAndCheckPinDup(nodep, refp, whatp);
}
} else if (AstParamTypeDType* const refp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
if (!checkPinRef(nodep, refp->varType())) {
pinCheckFail = true;
} else {
nodep->modPTypep(refp);
markAndCheckPinDup(nodep, refp, whatp);
}
} else {
nodep->v3error(ucfirst(whatp) << " not found: " << nodep->prettyNameQ());
}
}
if (!foundp || pinCheckFail) {
if (nodep->name() == "__paramNumber1" && m_cellp
&& VN_IS(m_cellp->modp(), Primitive)) {
// Primitive parameter is really a delay we can just ignore
@@ -2338,19 +2405,6 @@ private:
ucfirst(whatp)
<< " not found: " << nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
} else if (AstVar* const 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->prettyNameQ());
} else {
nodep->modVarp(refp);
markAndCheckPinDup(nodep, refp, whatp);
}
} else if (AstParamTypeDType* const refp = VN_CAST(foundp->nodep(), ParamTypeDType)) {
nodep->modPTypep(refp);
markAndCheckPinDup(nodep, refp, whatp);
} else {
nodep->v3error(ucfirst(whatp) << " not found: " << nodep->prettyNameQ());
}
}
// Early return() above when deleted
@@ -2400,9 +2454,9 @@ private:
} else {
const auto cextp = classp->extendsp();
UASSERT_OBJ(cextp, nodep, "Bad super extends link");
const auto sclassp = cextp->classp();
UASSERT_OBJ(sclassp, nodep, "Bad superclass");
m_ds.m_dotSymp = m_statep->getNodeSym(sclassp);
const auto baseClassp = cextp->classp();
UASSERT_OBJ(baseClassp, nodep, "Bad superclass");
m_ds.m_dotSymp = m_statep->getNodeSym(baseClassp);
UINFO(8, " super. " << m_ds.ascii() << endl);
}
}
@@ -2533,13 +2587,15 @@ private:
string expectWhat;
bool allowScope = false;
bool allowVar = false;
bool allowFTask = false;
bool staticAccess = false;
if (m_ds.m_dotPos == DP_PACKAGE) {
// {package}::{a}
AstNodeModule* classOrPackagep = nullptr;
expectWhat = "scope/variable";
expectWhat = "scope/variable/func";
allowScope = true;
allowVar = true;
allowFTask = true;
staticAccess = true;
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(),
"Bad package link");
@@ -2620,6 +2676,13 @@ private:
<< cellp->modp()->prettyNameQ());
}
}
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
AstTaskRef* const taskrefp
= new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
nodep->replaceWith(taskrefp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
if (start) m_ds = lastStates;
return;
} else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) {
AstIfaceRefDType* const ifacerefp
= LinkDotState::ifaceRefFromArray(varp->subDTypep());
@@ -2820,11 +2883,11 @@ private:
if (checkImplicit) {
// Create if implicit, and also if error (so only complain once)
// Else if a scope is allowed, making a signal won't help error cascade
auto varp = createImplicitVar(m_curSymp, nodep, m_modp, m_modSymp, err);
AstVarRef* const newp
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ};
= new AstVarRef{nodep->fileline(), varp, VAccess::READ};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
}
}
}
@@ -2842,19 +2905,26 @@ private:
"class reference parameter not removed by V3Param");
VL_RESTORER(m_ds);
VL_RESTORER(m_pinSymp);
{
// ClassRef's have pins, so track
if (nodep->classOrPackagep()) {
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep());
}
m_ds.init(m_curSymp);
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
iterateChildren(nodep);
// ClassRef's have pins, so track
if (nodep->classOrPackagep()) {
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep());
}
if (m_statep->forPrimary() && VN_IS(nodep->classOrPackagep(), Class) && !nodep->paramsp()
AstClass* const refClassp = VN_CAST(nodep->classOrPackagep(), Class);
// Make sure any extends() are properly imported within referenced class
if (refClassp && !m_statep->forPrimary()) classExtendImport(refClassp);
m_ds.init(m_curSymp);
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
iterateChildren(nodep);
AstClass* const modClassp = VN_CAST(m_modp, Class);
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
&& nodep->classOrPackagep()->hasGParam()
// Don't warn on typedefs, which are hard to know if there's a param somewhere buried
&& VN_IS(nodep->classOrPackageNodep(), Class)) {
&& VN_IS(nodep->classOrPackageNodep(), Class)
// References to class:: within class itself are OK per IEEE (UVM does this)
&& modClassp != refClassp) {
nodep->v3error("Reference to parameterized class without #() (IEEE 1800-2017 8.25.1)\n"
<< nodep->warnMore() << "... Suggest use '"
<< nodep->classOrPackageNodep()->prettyName() << "#()'");
@@ -2873,6 +2943,7 @@ private:
if (AstVar* const varp
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr) {
nodep->varp(varp);
updateVarUse(nodep->varp());
// Generally set by parse, but might be an import
nodep->classOrPackagep(foundp->classOrPackagep());
}
@@ -2915,6 +2986,7 @@ private:
AstVar* const varp
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
nodep->varp(varp);
updateVarUse(nodep->varp());
UINFO(7, " Resolved " << nodep << endl); // Also prints varp
if (!nodep->varp()) {
nodep->v3error("Can't find definition of "
@@ -2952,6 +3024,7 @@ private:
// later optimizations to deal with VarXRef.
nodep->varp(vscp->varp());
nodep->varScopep(vscp);
updateVarUse(nodep->varp());
UINFO(7, " Resolved " << nodep << endl); // Also prints taskp
AstVarRef* const newvscp
= new AstVarRef{nodep->fileline(), vscp, nodep->access()};
@@ -3026,7 +3099,8 @@ private:
}
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
m_ds.m_dotPos = DP_SCOPE;
// Class/package :: HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotp = nullptr;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
nodep->dotted(m_ds.m_dotText); // Maybe ""
@@ -3129,7 +3203,15 @@ private:
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|| nodep->name() == "get_randstate"
|| nodep->name() == "set_randstate") {
// Resolved in V3Width
if (AstClass* const classp = VN_CAST(m_modp, Class)) {
nodep->classOrPackagep(classp);
} else {
nodep->v3error("Calling implicit class method "
<< nodep->prettyNameQ() << " without being under class");
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
} else if (nodep->dotted() == "") {
if (nodep->pli()) {
if (v3Global.opt.bboxSys()) {
@@ -3215,7 +3297,7 @@ private:
if (nodep->user3SetOnce()) return;
if (m_ds.m_dotPos
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the instance "
nodep->v3error("Syntax error: Range ':', '+:' etc are not allowed in the instance "
"part of a dotted reference");
m_ds.m_dotErr = true;
return;
@@ -3273,7 +3355,16 @@ private:
{
m_ftaskp = nodep;
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
const bool isNew = nodep->name() == "new";
if (isNew) m_explicitSuperNew = false;
iterateChildren(nodep);
if (isNew && !m_explicitSuperNew && m_statep->forParamed()) {
const AstClassExtends* const classExtendsp = VN_AS(m_modp, Class)->extendsp();
if (classExtendsp && classExtendsp->classOrNullp()) {
AstNodeStmt* const superNewp = addImplicitSuperNewCall(VN_AS(nodep, Func));
iterate(superNewp);
}
}
}
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
m_ftaskp = nullptr;
@@ -3389,6 +3480,7 @@ private:
checkNoDot(nodep);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_modp);
VL_RESTORER(m_ifClassImpNames);
VL_RESTORER(m_insideClassExtParam);
{
@@ -3426,26 +3518,28 @@ private:
}
}
if (AstClass* const classp = cextp->classOrNullp()) {
if (AstClass* const baseClassp = cextp->classOrNullp()) {
// Already converted. Update symbol table to link unlinked members.
// Base class has to be visited in a case if its extends statement
// needs to be handled. Recursive inheritance was already checked.
if (classp == nodep) {
// Must be here instead of in LinkDotParam to handle
// "class (type T) extends T".
if (baseClassp == nodep) {
cextp->v3error("Attempting to extend class " << nodep->prettyNameQ()
<< " from itself");
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
} else if (cextp->isImplements() && !baseClassp->isInterfaceClass()) {
cextp->v3error("Attempting to implement from non-interface class "
<< classp->prettyNameQ() << '\n'
<< baseClassp->prettyNameQ() << '\n'
<< "... Suggest use 'extends'");
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
&& classp->isInterfaceClass()) {
&& baseClassp->isInterfaceClass()) {
cextp->v3error("Attempting to extend from interface class "
<< classp->prettyNameQ() << '\n'
<< baseClassp->prettyNameQ() << '\n'
<< "... Suggest use 'implements'");
}
classp->isExtended(true);
baseClassp->isExtended(true);
nodep->isExtended(true);
iterate(classp);
iterate(baseClassp);
importSymbolsFromExtended(nodep, cextp);
continue;
}
@@ -3457,15 +3551,16 @@ private:
// V3Width when determines types needs to find enum values and such
// so add members pointing to appropriate enum values
{
nodep->repairCache();
VMemberMap memberMap;
for (VSymEnt::const_iterator it = m_curSymp->begin(); it != m_curSymp->end(); ++it) {
AstNode* const itemp = it->second->nodep();
if (!nodep->findMember(it->first)) {
if (!memberMap.findMember(nodep, it->first)) {
if (AstEnumItem* const aitemp = VN_CAST(itemp, EnumItem)) {
AstEnumItemRef* const newp = new AstEnumItemRef{
aitemp->fileline(), aitemp, it->second->classOrPackagep()};
UINFO(8, "Class import noderef '" << it->first << "' " << newp << endl);
nodep->addMembersp(newp);
memberMap.insert(nodep, newp);
}
}
}
@@ -3476,7 +3571,8 @@ private:
if (nodep->user3SetOnce()) return;
if (AstNode* const cpackagep = nodep->classOrPackageOpp()) {
if (AstClassOrPackageRef* const cpackagerefp = VN_CAST(cpackagep, ClassOrPackageRef)) {
if (cpackagerefp->paramsp()) {
const AstClass* const clsp = VN_CAST(cpackagerefp->classOrPackageNodep(), Class);
if (clsp && clsp->isParameterized()) {
// Unable to link before the instantiation of parameter classes.
// The class reference node has to be visited to properly link parameters.
iterate(cpackagep);
@@ -3595,6 +3691,19 @@ private:
UINFO(5, " AstCellArrayRef: " << nodep << " " << m_ds.ascii() << endl);
// No need to iterate, if we have a UnlinkedVarXRef, we're already done
}
void visit(AstStmtExpr* nodep) override {
checkNoDot(nodep);
// Check if nodep represents a super.new call;
if (VN_IS(nodep->exprp(), New)) {
// in this case it was already linked, so it doesn't have a super reference
m_explicitSuperNew = true;
} else if (const AstDot* const dotp = VN_CAST(nodep->exprp(), Dot)) {
if (dotp->lhsp()->name() == "super" && VN_IS(dotp->rhsp(), New)) {
m_explicitSuperNew = true;
}
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
checkNoDot(nodep);
@@ -3647,6 +3756,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
state.computeScopeAliases();
state.dumpSelf();
{ LinkDotResolveVisitor{rootp, &state}; }
state.dumpSelf();
}
void V3LinkDot::linkDotPrimary(AstNetlist* nodep) {
+33 -12
View File
@@ -61,13 +61,30 @@ private:
// STATE
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
AstNodeModule* m_modp = nullptr; // Module we're inside
int m_modIncrementsNum = 0; // Var name counter
InsertMode m_insMode = IM_BEFORE; // How to insert
AstNode* m_insStmtp = nullptr; // Where to insert statement
bool m_unsupportedHere = false; // Used to detect where it's not supported yet
// METHODS
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
void insertOnTop(AstNode* newp) {
// Add the thing directly under the current TFunc/Module
AstNode* stmtsp = nullptr;
if (m_ftaskp) {
stmtsp = m_ftaskp->stmtsp();
} else if (m_modp) {
stmtsp = m_modp->stmtsp();
}
UASSERT(stmtsp, "Variable not under FTASK/MODULE");
newp->addNext(stmtsp->unlinkFrBackWithNext());
if (m_ftaskp) {
m_ftaskp->addStmtsp(newp);
} else if (m_modp) {
m_modp->addStmtsp(newp);
}
}
void insertNextToStmt(AstNode* nodep, AstNode* newp) {
// Return node that must be visited, if any
if (debug() >= 9) newp->dumpTree("- newstmt: ");
UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement");
@@ -88,7 +105,9 @@ private:
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
VL_RESTORER(m_modIncrementsNum);
m_modp = nodep;
m_modIncrementsNum = 0;
iterateChildren(nodep);
}
@@ -244,15 +263,14 @@ private:
return;
}
AstNodeExpr* const readp = nodep->rhsp();
AstNodeExpr* const writep = nodep->thsp();
AstNodeExpr* const writep = nodep->thsp()->unlinkFrBack();
AstConst* const constp = VN_AS(nodep->lhsp(), Const);
UASSERT_OBJ(nodep, constp, "Expecting CONST");
const AstNode* const backp = nodep->backp();
AstConst* const newconstp = constp->cloneTree(true);
// Prepare a temporary variable
FileLine* const fl = backp->fileline();
FileLine* const fl = nodep->fileline();
const string name = string{"__Vincrement"} + cvtToStr(++m_modIncrementsNum);
AstVar* const varp = new AstVar{
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
@@ -260,7 +278,7 @@ private:
if (m_ftaskp) varp->funcLocal(true);
// Declare the variable
insertBeforeStmt(nodep, varp);
insertOnTop(varp);
// Define what operation will we be doing
AstNodeExpr* operp;
@@ -273,17 +291,20 @@ private:
if (VN_IS(nodep, PreAdd) || VN_IS(nodep, PreSub)) {
// PreAdd/PreSub operations
// Immediately after declaration - increment it by one
varp->addNextHere(new AstAssign{fl, writep->cloneTree(true),
new AstVarRef{fl, varp, VAccess::READ}});
AstAssign* const assignp
= new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp};
insertNextToStmt(nodep, assignp);
// Immediately after incrementing - assign it to the original variable
varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp});
assignp->addNextHere(
new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}});
} else {
// PostAdd/PostSub operations
// assign the original variable to the temporary one
varp->addNextHere(new AstAssign{fl, writep->cloneTree(true), operp});
// Assign the original variable to the temporary one
AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
readp->cloneTree(true)};
insertNextToStmt(nodep, assignp);
// Increment the original variable by one
varp->addNextHere(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
readp->cloneTree(true)});
assignp->addNextHere(new AstAssign{fl, writep, operp});
}
// Replace the node with the temporary
+1
View File
@@ -196,6 +196,7 @@ private:
// Spec says value is integral, if negative is ignored
AstVar* const varp
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()};
varp->lifetime(VLifetime::AUTOMATIC);
varp->usedLoopIdx(true);
m_modp->addStmtsp(varp);
AstNode* initsp = new AstAssign{
+10 -5
View File
@@ -41,7 +41,6 @@ private:
bool m_setContinuously = false; // Set that var has some continuous assignment
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
// VISITs
// Result handing
@@ -55,7 +54,8 @@ private:
// so it is needed to check only if m_setContinuously is true
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
}
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) {
if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
&& nodep->varp()->isReadOnly()) {
nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ());
}
@@ -275,9 +275,14 @@ private:
iterateAndNextNull(nodep->thsp());
}
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
void visit(AstMemberSel* nodep) override {
if (m_setRefLvalue != VAccess::NOCHANGE) {
nodep->access(m_setRefLvalue);
} else {
// It is the only place where the access is set to member select nodes.
// If it doesn't have to be set to WRITE, it means that it is READ.
nodep->access(VAccess::READ);
}
iterateChildren(nodep);
}
void visit(AstNodeFTaskRef* nodep) override {
+3 -1
View File
@@ -53,7 +53,7 @@ void V3LinkLevel::modSortByLevel() {
ModVec tops; // Top level modules
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
nodep = VN_AS(nodep->nextp(), NodeModule)) {
if (nodep->level() <= 2) tops.push_back(nodep);
if (nodep->level() <= 2 && !VN_IS(nodep, NotFoundModule)) tops.push_back(nodep);
mods.push_back(nodep);
}
if (tops.size() >= 2) {
@@ -289,6 +289,8 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
varp->trace(false);
}
if (v3Global.opt.noTraceTop() && varp->isIO()) { varp->trace(false); }
AstPin* const pinp = new AstPin{
oldvarp->fileline(), 0, varp->name(),
new AstVarRef{varp->fileline(), varp,
+111 -10
View File
@@ -76,6 +76,8 @@ private:
// without suppressing other token's messages as a side effect.
// We could have verilog.l create a new one on every token,
// but that's a lot more structures than only doing AST nodes.
// TODO: Many places copy the filename when suppressing warnings,
// perhaps audit to make consistent and this is no longer needed
if (m_filelines.find(nodep->fileline()) != m_filelines.end()) {
nodep->fileline(new FileLine{nodep->fileline()});
}
@@ -125,6 +127,58 @@ private:
&& !nodep->stmtsp()->nextp()); // Has only one item
}
void checkIndent(AstNode* nodep, AstNode* childp) {
// Try very hard to avoid false positives
AstNode* nextp = nodep->nextp();
if (!childp) return;
if (!nextp && VN_IS(nodep, While) && VN_IS(nodep->backp(), Begin))
nextp = nodep->backp()->nextp();
if (!nextp) return;
if (VN_IS(childp, Begin)) return;
FileLine* const nodeFlp = nodep->fileline();
FileLine* const childFlp = childp->fileline();
FileLine* const nextFlp = nextp->fileline();
// UINFO(0, "checkInd " << nodeFlp->firstColumn() << " " << nodep << endl);
// UINFO(0, " child " << childFlp->firstColumn() << " " << childp << endl);
// UINFO(0, " next " << nextFlp->firstColumn() << " " << nextp << endl);
// Same filename, later line numbers (no macro magic going on)
if (nodeFlp->filenameno() != childFlp->filenameno()) return;
if (nodeFlp->filenameno() != nextFlp->filenameno()) return;
if (nodeFlp->lastLineno() >= childFlp->firstLineno()) return;
if (childFlp->lastLineno() >= nextFlp->firstLineno()) return;
// This block has indent 'a'
// Child block has indent 'b' where indent('b') > indent('a')
// Next block has indent 'b'
// (note similar code below)
if (!(nodeFlp->firstColumn() < childFlp->firstColumn()
&& nextFlp->firstColumn() >= childFlp->firstColumn()))
return;
// Might be a tab difference in spaces up to the node prefix, if so
// just ignore this warning
// Note it's correct we look at nodep's column in all of these
const std::string nodePrefix = nodeFlp->sourcePrefix(nodeFlp->firstColumn());
const std::string childPrefix = childFlp->sourcePrefix(nodeFlp->firstColumn());
const std::string nextPrefix = nextFlp->sourcePrefix(nodeFlp->firstColumn());
if (childPrefix != nodePrefix) return;
if (nextPrefix != childPrefix) return;
// Some file lines start after the indentation, so make another check
// using actual file contents
const std::string nodeSource = nodeFlp->source();
const std::string childSource = childFlp->source();
const std::string nextSource = nextFlp->source();
if (!(VString::leadingWhitespaceCount(nodeSource)
< VString::leadingWhitespaceCount(childSource)
&& VString::leadingWhitespaceCount(nextSource)
>= VString::leadingWhitespaceCount(childSource)))
return;
nextp->v3warn(MISINDENT,
"Misleading indentation\n"
<< nextp->warnContextPrimary() << '\n'
<< nodep->warnOther()
<< "... Expected indentation matching this earlier statement's line:\n"
<< nodep->warnContextSecondary());
}
// VISITs
void visit(AstNodeFTask* nodep) override {
if (!nodep->user1SetOnce()) { // Process only once.
@@ -242,8 +296,16 @@ private:
nodep->v3warn(STATICVAR, "Static variable with assignment declaration declared in a "
"loop converted to automatic");
}
if (m_ftaskp && m_ftaskp->classMethod() && nodep->lifetime().isNone()) {
nodep->lifetime(VLifetime::AUTOMATIC);
if (m_ftaskp) {
bool classMethod = m_ftaskp->classMethod();
if (!classMethod) {
AstClassOrPackageRef* const pkgrefp
= VN_CAST(m_ftaskp->classOrPackagep(), ClassOrPackageRef);
if (pkgrefp && VN_IS(pkgrefp->classOrPackagep(), Class)) classMethod = true;
}
if (classMethod && nodep->lifetime().isNone()) {
nodep->lifetime(VLifetime::AUTOMATIC);
}
}
if (nodep->lifetime().isNone() && nodep->varType() != VVarType::PORT) {
nodep->lifetime(m_lifetime);
@@ -316,9 +378,11 @@ private:
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
FileLine* const newfl = new FileLine{fl};
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
auto* const assp
= new AstAssign{newfl, new AstVarRef{newfl, nodep->name(), VAccess::WRITE},
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
// Create a ParseRef to the wire. We cannot use the var as it may be deleted if
// it's a port (see t_var_set_link.v)
auto* const assp = new AstAssign{
newfl, new AstParseRef{newfl, VParseRefExp::PX_TEXT, nodep->name()},
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
if (nodep->lifetime().isAutomatic()) {
nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
} else {
@@ -327,7 +391,7 @@ private:
} // 4. Under blocks, it's an initial value to be under an assign
else {
nodep->addNextHere(
new AstAssign{fl, new AstVarRef{fl, nodep->name(), VAccess::WRITE},
new AstAssign{fl, new AstVarRef{fl, nodep, VAccess::WRITE},
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)});
}
}
@@ -476,6 +540,7 @@ private:
void visit(AstForeach* nodep) override {
// FOREACH(array, loopvars, body)
UINFO(9, "FOREACH " << nodep << endl);
cleanFileline(nodep);
// Separate iteration vars from base from variable
// Input:
// v--- arrayp
@@ -510,23 +575,39 @@ private:
iterateChildren(nodep);
}
void visit(AstRepeat* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop);
{
m_insideLoop = true;
checkIndent(nodep, nodep->stmtsp());
iterateChildren(nodep);
}
}
void visit(AstDoWhile* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop);
{
m_insideLoop = true;
iterateChildren(nodep);
}
}
void visit(AstWait* nodep) override {
cleanFileline(nodep);
iterateChildren(nodep);
if (nodep->condp()->isZero()) {
// Special case "wait(0)" we won't throw WAITCONST as user wrote
// it that way with presumed intent - UVM does this.
FileLine* const newfl = nodep->fileline();
newfl->warnOff(V3ErrorCode::WAITCONST, true);
nodep->fileline(newfl);
}
}
void visit(AstWhile* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_insideLoop);
{
m_insideLoop = true;
checkIndent(nodep, nodep->stmtsp());
iterateChildren(nodep);
}
}
@@ -580,14 +661,25 @@ private:
// The genblk name will get attached to the if true/false LOWER begin block(s)
const bool nestedIf = nestedIfBegin(nodep);
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
int assignGenBlkNum = -1;
if (nodep->genforp()) {
++m_genblkNum;
if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum));
if (nodep->name() == "") assignGenBlkNum = m_genblkNum;
} else if (nodep->generate() && nodep->name() == "" && assignGenBlkNum == -1
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
assignGenBlkNum = m_genblkAbove;
}
if (nodep->generate() && nodep->name() == ""
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
nodep->name("genblk" + cvtToStr(m_genblkAbove));
if (assignGenBlkNum != -1) {
nodep->name("genblk" + cvtToStr(assignGenBlkNum));
if (nodep->stmtsp()) {
nodep->v3warn(GENUNNAMED,
"Unnamed generate block "
<< nodep->prettyNameQ() << " (IEEE 1800-2017 27.6)"
<< nodep->warnMore()
<< "... Suggest assign a label with 'begin : gen_<label_name>'");
}
}
if (nodep->name() != "") {
VL_RESTORER(m_genblkAbove);
VL_RESTORER(m_genblkNum);
@@ -611,6 +703,7 @@ private:
}
void visit(AstGenIf* nodep) override {
cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
const bool nestedIf
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
if (nestedIf) {
@@ -655,6 +748,11 @@ private:
}
}
}
void visit(AstIf* nodep) override {
cleanFileline(nodep);
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
iterateChildren(nodep);
}
void visit(AstPrintTimeScale* nodep) override {
// Inlining may change hierarchy, so just save timescale where needed
cleanFileline(nodep);
@@ -683,6 +781,7 @@ private:
nodep->timeunit(m_modp->timeunit());
}
void visit(AstTimeUnit* nodep) override {
cleanFileline(nodep);
iterateChildren(nodep);
nodep->timeunit(m_modp->timeunit());
}
@@ -711,6 +810,7 @@ private:
}
}
void visit(AstClocking* nodep) override {
cleanFileline(nodep);
VL_RESTORER(m_defaultInSkewp);
VL_RESTORER(m_defaultOutSkewp);
// Find default input and output skews
@@ -745,6 +845,7 @@ private:
iterateChildren(nodep);
}
void visit(AstClockingItem* nodep) override {
cleanFileline(nodep);
if (nodep->direction() == VDirection::OUTPUT) {
if (!nodep->skewp()) {
if (m_defaultOutSkewp) {
+54 -2
View File
@@ -32,6 +32,7 @@
#include "V3Ast.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Task.h"
#include <algorithm>
#include <map>
@@ -45,7 +46,8 @@ class LinkResolveVisitor final : public VNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstCaseItem::user2() // bool Moved default caseitems
// AstCaseItem::user2() // bool Moved default caseitems
// AstNodeFTaskRef::user2() // bool Processing - to check for recursion
const VNUser2InUse m_inuser2;
// STATE
@@ -89,7 +91,9 @@ private:
}
}
void visit(AstNodeCoverOrAssert* nodep) override {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
if (m_assertp) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assert not allowed under another assert");
}
m_assertp = nodep;
iterateChildren(nodep);
m_assertp = nullptr;
@@ -111,6 +115,7 @@ private:
}
void visit(AstNodeFTask* nodep) override {
// Note AstLet is handled specifically elsewhere
// NodeTask: Remember its name for later resolution
if (m_underGenerate) nodep->underGenerate(true);
// Remember the existing symbol table scope
@@ -138,6 +143,47 @@ private:
}
void visit(AstNodeFTaskRef* nodep) override {
iterateChildren(nodep);
if (AstLet* letp = VN_CAST(nodep->taskp(), Let)) {
UINFO(7, "letSubstitute() " << nodep << " <- " << letp << endl);
if (letp->user2()) {
nodep->v3error("Recursive let substitution " << letp->prettyNameQ());
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
letp->user2(true);
// letp->dumpTree("-let-let ");
// nodep->dumpTree("-let-ref ");
AstStmtExpr* const letStmtp = VN_AS(letp->stmtsp(), StmtExpr);
AstNodeExpr* const newp = letStmtp->exprp()->cloneTree(false);
const V3TaskConnects tconnects = V3Task::taskConnects(nodep, letp->stmtsp());
std::map<const AstVar*, AstNodeExpr*> portToExprs;
for (const auto& tconnect : tconnects) {
const AstVar* const portp = tconnect.first;
const AstArg* const argp = tconnect.second;
AstNodeExpr* const pinp = argp->exprp();
if (!pinp) continue; // Argument error we'll find later
portToExprs.emplace(portp, pinp);
}
// Replace VarRefs of arguments with the argument values
newp->foreach([&](AstVarRef* refp) { //
const auto it = portToExprs.find(refp->varp());
if (it != portToExprs.end()) {
AstNodeExpr* const pinp = it->second;
UINFO(9, "let pin subst " << refp << " <- " << pinp << endl);
// Side effects are copied into pins, to match other simulators
refp->replaceWith(pinp->cloneTree(false));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
});
// newp->dumpTree("-let-new ");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
// Iterate to expand further now, so we can look for recursions
visit(newp);
letp->user2(false);
return;
}
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
}
@@ -168,6 +214,12 @@ private:
}
}
void visit(AstLet* nodep) override {
// Lets have been (or about to be) substituted, we can remove
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstPragma* nodep) override {
if (nodep->pragType() == VPragmaType::HIER_BLOCK) {
UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module");
+95
View File
@@ -0,0 +1,95 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Member names for classes/structs
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// For a class or struct, create a map of names of each member.
// Used for faster lookup to avoid O(n^2) processing times.
//
//*************************************************************************
#ifndef VERILATOR_V3MEMBERMAP_H_
#define VERILATOR_V3MEMBERMAP_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
//######################################################################
class VMemberMap final {
// MEMBERS
using MemberMap = std::map<std::string, AstNode*>;
using NodeMap = std::map<const AstNode*, MemberMap>;
NodeMap m_map; // Map of nodes being tracked
public:
void clear() { m_map.clear(); }
// Find 'name' under 'nodep', caching nodep's children if needed
AstNode* findMember(const AstNode* nodep, const std::string& name) {
auto nit = m_map.find(nodep);
if (VL_UNLIKELY(nit == m_map.end())) {
scan(nodep);
nit = m_map.find(nodep);
}
const auto mit = nit->second.find(name);
if (mit == nit->second.end()) return nullptr;
return mit->second;
}
// Insert under the given parent node the child's name
void insert(const AstNode* nodep, AstNode* childp) {
auto nit = m_map.find(nodep);
if (nit == m_map.end()) {
scan(nodep);
nit = m_map.find(nodep);
}
memberInsert(nit->second, childp, false);
}
private:
void scan(const AstNode* nodep) {
const auto nitPair = m_map.emplace(nodep, MemberMap{});
MemberMap& mmapr = nitPair.first->second;
if (const AstClass* const anodep = VN_CAST(nodep, Class)) {
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
if (const AstScope* const scopep = VN_CAST(itemp, Scope)) {
for (AstNode* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
if (AstClass::isCacheableChild(blockp)) memberInsert(mmapr, blockp);
}
} else {
if (AstClass::isCacheableChild(itemp)) memberInsert(mmapr, itemp);
}
}
} else if (const AstNodeUOrStructDType* const anodep
= VN_CAST(nodep, NodeUOrStructDType)) {
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
memberInsert(mmapr, itemp);
}
} else {
nodep->v3fatalSrc("Unsupported node type");
}
}
void memberInsert(MemberMap& mmapr, AstNode* childp, bool warn = true) {
const auto mitPair = mmapr.emplace(childp->name(), childp);
if (VL_UNCOVERABLE(!mitPair.second && warn)) {
// Probably an internal error, but we'll make it user friendly if happens
childp->v3error("Duplicate declaration of member name: " << childp->prettyNameQ());
}
}
};
#endif // guard
+1
View File
@@ -620,6 +620,7 @@ private:
// LCOV_EXCL_START
if (debug()) rhsp->dumpTree("Don't know how to fold expression: ");
rhsp->v3fatalSrc("Should not try to fold this during conditional merging");
return nullptr;
// LCOV_EXCL_STOP
}
+11 -2
View File
@@ -109,6 +109,10 @@ public:
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE {
return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true;
}
/// Assume that the mutex is already held. Purely for Clang thread safety analyzer.
void assumeLocked() VL_ASSERT_CAPABILITY(this) VL_MT_SAFE {}
/// Pretend that the mutex is being unlocked. Purely for Clang thread safety analyzer.
void pretendUnlock() VL_RELEASE() VL_MT_SAFE {}
/// Acquire/lock mutex and check for stop request
/// It tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex.
@@ -135,12 +139,17 @@ private:
T& m_mutexr;
public:
/// Construct and hold given mutex lock until destruction or unlock()
/// Lock given mutex and hold it for the object lifetime.
explicit V3LockGuardImp(T& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
mutexr.lock();
}
/// Destruct and unlock the mutex
/// Take already locked mutex, and and hold the lock for the object lifetime.
explicit V3LockGuardImp(T& mutexr, std::adopt_lock_t) VL_REQUIRES(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
}
/// Unlock the mutex
~V3LockGuardImp() VL_RELEASE() { m_mutexr.unlock(); }
};

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