goto-instrument --generate-function-body: bound dynamic-object allocation#9058
Open
tautschnig wants to merge 1 commit into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Improves performance and termination guarantees for goto-instrument --generate-function-body by eliminating an O(N²) symbol suffix scan and adding a hard cap on dynamic object allocations during nondet initialisation.
Changes:
- Add a per-prefix suffix “search start” cache to
symbol_table_baset::next_unused_suffix(prefix)to amortise repeated allocations under one prefix. - Add
max_dynamic_object_instancesparameter and enforce it insymbol_factoryt::gen_nondet_initto prevent runaway nondet-init expansion. - Add a new regression test to ensure deep/wide non-recursive struct hierarchies don’t hang/OOM during havoc body generation.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/util/symbol_table_base.h | Adds per-prefix suffix hint cache to avoid O(N²) repeated scans. |
| src/util/object_factory_parameters.h | Introduces max_dynamic_object_instances parameter with detailed rationale. |
| src/util/object_factory_parameters.cpp | Parses/propagates the new CLI option into object factory params. |
| src/ansi-c/c_nondet_symbol_factory.h | Adds allocation counter to enforce the new hard cap. |
| src/ansi-c/c_nondet_symbol_factory.cpp | Enforces allocation cap by nulling pointers once limit is reached. |
| src/ansi-c/ansi_c_language.h | Exposes --max-dynamic-object-instances via frontend option/help text. |
| regression/goto-instrument/generate-function-body-deep-struct-cap/test.desc | Adds regression harness invoking the new cap option. |
| regression/goto-instrument/generate-function-body-deep-struct-cap/main.c | Defines a deep/wide non-recursive struct hierarchy reproducer. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d162044 to
9d5b3c1
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #9058 +/- ##
===========================================
- Coverage 80.68% 80.68% -0.01%
===========================================
Files 1714 1714
Lines 189593 189606 +13
Branches 73 73
===========================================
+ Hits 152979 152989 +10
- Misses 36614 36617 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
9d5b3c1 to
ea8cb8a
Compare
ea8cb8a to
b5270b4
Compare
…tion The object factory's max_nondet_tree_depth cap only fires when a struct tag repeats on the pointer chain, so wide-but-non-recursive struct hierarchies still expand exponentially, making --generate-function-body emit an unboundedly large body (and, on large inputs, fail to terminate). Add object_factory_parameterst::max_dynamic_object_instances (default 1000, exposed as --max-dynamic-object-instances): once the allocation count reaches it, pointers are initialised to NULL instead of recursively expanded. A fresh symbol_factoryt is constructed per nondet-init root, so the count -- which accumulates across the whole object tree and is never reset mid-root -- enforces the cap per root. Forcing NULL on capped pointers is an under-approximation (the non-null branch is dropped, and it applies even below min_null_tree_depth, which the termination guard overrides). Because the default is a finite value rather than "unlimited", it also bounds all other C nondet initialisation that goes through c_nondet_symbol_factory (e.g. ansi_c_entry_point), not just --generate-function-body; 1000 is well above realistic harness needs. The parameter is enforced only by the C object factory (JBMC and goto-harness ignore it). Performance: on a goto binary built from Linux 5.10's crypto/algif_aead.c, `goto-instrument --generate-function-body af_alg_alloc_areq --generate-function-body-options havoc` previously did not terminate (still running after 4 hours of wall-clock time); with this cap it completes in about 2 seconds, emitting a ~10 MB goto binary. Add a regression test with a deep non-recursive struct hierarchy capped below its natural allocation count; it fails if the cap is reverted. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
b5270b4 to
9e695b9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The object factory's max_nondet_tree_depth cap only fires when a struct tag repeats on the pointer chain, so wide-but-non-recursive struct hierarchies still expand exponentially, making --generate-function-body emit an unboundedly large body (and, on large inputs, fail to terminate).
Add object_factory_parameterst::max_dynamic_object_instances (default 1000, exposed as --max-dynamic-object-instances): once the per-root allocation count reaches it, pointers are initialised to NULL instead of recursively expanded. The count is reset at the top-level gen_nondet_init entry, so the cap is enforced per nondet-init root.
Performance: on a goto binary built from Linux 5.10's crypto/algif_aead.c,
goto-instrument --generate-function-body af_alg_alloc_areq --generate-function-body-options havocpreviously did not terminate (still running after 4 hours of wall-clock time); with this cap it completes in about 2 seconds, emitting a ~10 MB goto binary.Add a regression test with a deep non-recursive struct hierarchy capped below its natural allocation count; it fails if the cap is reverted.