Skip to content

Make lorina's shared regexes inline const - #706

Open
marcelwa wants to merge 1 commit into
lsils:masterfrom
marcelwa:lorina-inline-const-regexes-upstream
Open

Make lorina's shared regexes inline const#706
marcelwa wants to merge 1 commit into
lsils:masterfrom
marcelwa:lorina-inline-const-regexes-upstream

Conversation

@marcelwa

Copy link
Copy Markdown
Contributor

Hi — a small, declaration-only patch to the vendored lorina copy, offered as a courtesy.

The six lorina format headers under lib/lorina/lorina/ declare their compiled patterns static at namespace scope — aig_regex, pla_regex, verilog_regex, bench_regex, blif_regex and dimacs_regex, 29 patterns in all. This makes them inline const. No call site changes.

This same patch is open upstream at hriener/lorina#90. If you would rather take it by re-vendoring once that lands, please just close this — I opened both so the fix is available whichever route you prefer, not to pre-empt the upstream one.

static in a header duplicates per translation unit

static gives internal linkage, so every TU that includes aiger.hpp gets its own private set of nine std::regex objects, each constructed during that TU's static initialization. inline is the C++17 spelling for the single shared definition intended, and MOCKTURTLE_CXX_STANDARD already defaults to "17".

Measured on two TUs that each include lorina/aiger.hpp, linked together:

distinct aig_regex objects .bss
before 18 (9 × 2 TUs) 4224 B
after 9 3512 B

nm agrees: each pattern goes from a b (local BSS) symbol private to its object file to a u (GNU unique) symbol shared across them. mockturtle's readers are included from many TUs in a typical build, so this repeats.

const is the half that prompted this

These objects live in public namespaces under public names, and only convention stops a caller doing lorina::aig_regex::header.assign(...). Every use is std::regex_match or an sregex_iterator constructor, both taking a const std::regex& — so they are already read-only in practice, and that is exactly what makes it safe for several threads to parse at once, since std::regex's const member functions carry the standard's usual thread-safety guarantee.

The context: we maintain aigverse, which wraps mockturtle's readers for Python, and recently released the GIL around them so threaded corpus loading actually overlaps. The audit justifying that rests on the "only ever reached through a const reference" argument, which today must be re-established by reading every call site and could be silently invalidated by a caller. const makes it compiler-checked.

Testing

Compiles and links clean at C++17 across two TUs including all six patched headers. Upstream lorina's own suite passes with this change — 835 assertions in 74 test cases. End-to-end, aigverse's full suite passes against a mockturtle carrying it (426 passed, 94 skipped), exercising the AIGER binary and ASCII, PLA and Verilog readers, including a suite that parses each format from eight threads at once. It is also green across this repository's full CI matrix — g++-10/11/12, clang++-14 through 17, macOS, and both MSVC toolsets — via the equivalent change on our fork.

One behavior note

C++17 inline variables in a shared library emit STB_GNU_UNIQUE symbols, which make the .so ineligible for dlclose unloading. That holds for every C++17 inline variable and is irrelevant to header-only consumption, but it is a real difference from static and worth flagging.

Not included

The patterns themselves are untouched. Several — the AIGER header's nine optional capture groups, ^i(\d+) (.*)$ — parse space-separated integers with a backtracking engine and would likely be faster as a hand-written scanner, most of all in verilog_regex and pla_regex where the match runs per line. That is a separate change wanting a profile behind it.

Happy to adjust or drop this.

The six vendored lorina format headers declare their compiled patterns `static`
at namespace scope. `static` gives internal linkage, so every translation unit
including aiger.hpp gets its own private set of nine std::regex objects; linking
two such TUs yields 18 distinct objects where the source describes 9. `inline`
is the C++17 spelling for the single shared definition intended, and mockturtle
already builds at C++17.

`const` matters more. These live in public namespaces under public names, and
only convention stops a caller reassigning one. Every use is std::regex_match or
an sregex_iterator constructor, both taking a const reference, so they are
already read-only in practice -- which is what makes concurrent parsing safe.
const promotes that from a property of the current call sites into one the
compiler enforces.

29 patterns across aiger, pla, verilog, bench, blif and dimacs. Declaration-only:
no call site changes.
@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.06%. Comparing base (0886ebf) to head (0f6a947).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #706   +/-   ##
=======================================
  Coverage   84.06%   84.06%           
=======================================
  Files         190      190           
  Lines       29515    29515           
=======================================
  Hits        24812    24812           
  Misses       4703     4703           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant