Skip to content

smt2_solver: iterate over operands in expand_function_applications#9044

Open
tautschnig wants to merge 2 commits into
diffblue:developfrom
tautschnig:smt2-expand-fn-iterative
Open

smt2_solver: iterate over operands in expand_function_applications#9044
tautschnig wants to merge 2 commits into
diffblue:developfrom
tautschnig:smt2-expand-fn-iterative

Conversation

@tautschnig

Copy link
Copy Markdown
Collaborator

expand_function_applications() is a post-order rewrite: for every node, first rewrite the operands, then — if the current node is a function_application with a known definition — substitute it with the (already-rewritten) body. The recursive form overflowed the call stack on deeply-nested inputs produced by the iterative SMT2 parser (e.g. 50k-deep bvand chains in the try5_*.flanagansaxe.smt2 family), which turned into stack overflows in the solver phase for files that the parser now successfully accepts.

Replace the recursion with a two-pass iterative walk:

  1. gather all nodes in post-order into an explicit work list, and
  2. process them back-to-front, by which point every operand has already been rewritten.

The only residual recursion is on the inlined body of a user-defined function, which is typically shallow; this matches the pre-existing behaviour.

Regression: bf13.smt2 (the single remaining crash in the QF_ABV run with the iterative parser) now runs into the 2 s solver timeout instead of dumping core. The full QF_ABV sweep (15,148 files, 2 s timeout, 2 GB RLIMIT_AS, 32-way parallel) goes from 1 crash to 0.

  • Each commit message has a non-empty body, explaining why the change was made.
  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • n/a The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • n/a White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@tautschnig tautschnig self-assigned this Jun 17, 2026
@tautschnig tautschnig requested a review from martin-cs as a code owner June 17, 2026 17:27
Copilot AI review requested due to automatic review settings June 17, 2026 17:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SMT2 solver’s expand_function_applications() rewrite pass to avoid call-stack overflows on deeply nested expressions produced by the iterative SMT2 parser, by replacing the recursive operand walk with an explicit two-pass iterative traversal.

Changes:

  • Replaces recursive descent over expr.operands() with an iterative worklist + reverse processing to preserve post-order rewrite semantics.
  • Keeps recursion only for expanding newly inlined user-defined function bodies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/solvers/smt2/smt2_solver.cpp Outdated
Comment on lines +79 to +83
// benchmarks). We do the rewrite in two passes over an explicit stack
// of exprt pointers: first collect every node in post-order, then
// process them bottom-up. Each node's operands have already been
// rewritten by the time we get to it, matching the old recursive
// semantics.
Comment thread src/solvers/smt2/smt2_solver.cpp Outdated
Comment on lines +76 to +78
// Post-order walk, but iterative: the recursive form would overflow the
// call stack on deeply-nested expressions produced by the parser (e.g.
// chains of thousands of bvand / store operations in SMT-COMP QF_ABV
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.68%. Comparing base (321ba11) to head (07f5cba).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #9044   +/-   ##
========================================
  Coverage    80.68%   80.68%           
========================================
  Files         1714     1714           
  Lines       189501   189516   +15     
  Branches        73       73           
========================================
+ Hits        152902   152919   +17     
+ Misses       36599    36597    -2     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

tautschnig and others added 2 commits June 17, 2026 19:58
expand_function_applications() is a post-order rewrite: for every node, first
rewrite the operands, then -- if the node is a function_application with a
known definition -- substitute it with the (already-rewritten) body. The
recursive form overflowed the call stack on deeply-nested inputs produced by
the iterative SMT2 parser (e.g. chains of thousands of bvand / store
operations), turning into stack overflows in the solver phase for files that
the parser now accepts.

Use the existing iterative, mutation-safe post-order traversal
exprt::visit_post() instead of recursing over the operands by hand. The only
residual recursion is on the freshly inlined body of a user-defined function,
which is typically shallow; this matches the pre-existing behaviour.

No regression test is added here: on this branch the SMT2 parser is itself
recursive (expression() -> function_application() -> operands() ->
expression()), so a term deep enough to overflow expand_function_applications
overflows the parser first, which makes a .smt2 regression test impossible
until the iterative parser lands. The end-to-end guard is the deep-nesting
.smt2 tests that ship with that parser change; the depth-safety of the
traversal this now relies on is covered by a unit test for exprt::visit_post
(separate commit).

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
visit_post performs an iterative, mutation-safe post-order traversal; several
rewrites rely on it precisely to avoid recursing over -- and overflowing the
call stack on -- deeply nested expressions. Add a test that builds a chain far
deeper than the recursion that overflows the default (~8 MiB) unit-test stack,
walks it with visit_post, and checks every node is visited: it completes with
the iterative implementation and overflows the stack with a recursive one.

The chain is dismantled iteratively before destruction because this branch
does not include the depth-safe irept destructor.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@tautschnig tautschnig force-pushed the smt2-expand-fn-iterative branch from f2c96c7 to 07f5cba Compare June 17, 2026 19:59
@tautschnig tautschnig assigned kroening and unassigned tautschnig Jun 17, 2026
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.

3 participants