Skip to content

Fix unnecessary irept copies found by check-irep-moves#8978

Open
tautschnig wants to merge 8 commits into
diffblue:developfrom
tautschnig:irept-copies
Open

Fix unnecessary irept copies found by check-irep-moves#8978
tautschnig wants to merge 8 commits into
diffblue:developfrom
tautschnig:irept-copies

Conversation

@tautschnig

Copy link
Copy Markdown
Collaborator

Address 8 findings from the clang-based irept copy checker:

  • 1 const reference (shadow_memory_util.cpp: unmodified copy)
  • 7 std::move (string_abstraction, goto_convert_side_effect, string_instrumentation, value_set, boolbv_extractbit, boolbv_index: source not used after copy)
  • 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).
  • n/a 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 Apr 27, 2026
Copilot AI review requested due to automatic review settings April 27, 2026 10:46

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@codecov

codecov Bot commented Apr 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.59%. Comparing base (4ed6b6d) to head (8e87f7a).

Files with missing lines Patch % Lines
.../goto-instrument/accelerate/acceleration_utils.cpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #8978      +/-   ##
===========================================
- Coverage    80.59%   80.59%   -0.01%     
===========================================
  Files         1713     1713              
  Lines       189403   189403              
  Branches        73       73              
===========================================
- Hits        152647   152644       -3     
- Misses       36756    36759       +3     

☔ 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.

std::size_t index_width =
std::max(address_bits(src_bv_width), index_bv_width);
unsignedbv_typet index_type(index_width);
unsignedbv_typet index_type(std::move(index_width));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There is no need to std::move an integer.

Comment thread src/solvers/flattening/boolbv_index.cpp Outdated
ns);

byte_extract_exprt be = byte_extract_expr;
byte_extract_exprt be = std::move(byte_extract_expr);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

byte_extract_expr is const

if(!new_comp.empty())
{
struct_union_typet abstracted_type = struct_union_type;
struct_union_typet abstracted_type = std::move(struct_union_type);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

struct_union_type is const

if(result_is_used)
{
exprt tmp = op;
exprt tmp = std::move(op);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

op is const

tautschnig and others added 8 commits June 11, 2026 19:12
Address 8 findings from the clang-based irept copy checker:
- 1 const reference (shadow_memory_util.cpp: unmodified copy)
- 7 std::move (string_abstraction, goto_convert_side_effect,
  string_instrumentation, value_set, boolbv_extractbit,
  boolbv_index: source not used after copy)

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Heuristic grep-based checker that flags local variables initialized
by copying an irept-derived type, where the variable is subsequently
modified and the source appears unused after the copy. These are
candidates for std::move to avoid unnecessary copy-on-write detach.

Found 22 candidates in goto-symex, 50 across the hot paths.
Each needs manual review — some are false positives (const ref
sources, loop-reused sources).

Usage: python3 scripts/check_irep_copies.py src/goto-symex/*.cpp

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
The check-irep-moves clang-tooling driver intentionally lives outside
the CBMC module hierarchy and uses upstream clang/llvm conventions
(using-directives, llvm/clang headers, classes that don't end in 't').
Mirror the existing miniz exclusion to keep the file out of the
cpplint scope.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
The check-irep-copies job links the tool against -lclang-cpp, which
on Ubuntu 24.04 is provided by libclang-cpp18-dev (not by
libclang-18-dev, which only ships the C library libclang.so).

Without this the link step fails with
  /usr/bin/ld: cannot find -lclang-cpp: No such file or directory

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Running the checker on develop's hot-paths produced seven findings:
the four reviewer-flagged \`std::move\`-of-const-source false
positives, plus three more (one converting constructor, one MemberExpr-
sourced unmodified copy, one loop-reused source) that are similar in
character.  All seven are spurious -- they would either silently
copy-construct anyway, or break correctness if a developer trusted
the suggestion.

Tighten the heuristic so the checker stops emitting them:

  * In the unnecessary-copy path:
      - require the chosen constructor to be a copy constructor
        (rejects converting one-arg ctors like
        \`unsignedbv_typet(std::size_t)\`);
      - look through references when checking \`isConstQualified\` on
        the source's type, so \`const auto &x = ...\` is recognised
        as a const source;
      - skip when the copy is inside a loop whose enclosing scope
        does not contain the source's declaration -- the source
        will be copied (and would be moved) once per iteration.

  * In the unmodified-copy path:
      - skip MemberExpr sources, because the existing
        MutableUseChecker only tracks mutations of the destination
        and so cannot detect a \`this->member = ...\` between the
        copy and a later use of the destination;
      - skip reference-typed sources for the same reason: a
        \`T &alias\` is often introduced precisely to be reseated/
        mutated, and \`const T &\` aliases would observe the
        post-mutation value rather than the snapshot the caller
        wants.

After these changes the checker reports zero findings on the hot
paths covered by run_irep_copy_check.sh, including the four sites
the previous commit had to revert from \`std::move\` back to plain
copies.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
The CI job was named check-irep-copies, but the tool it built was
check-irep-moves (from scripts/check_irep_moves.cpp) and an orphaned
check_irep_copies.py prototype was left alongside it. Standardise on the
"irep-copies" term: rename the source to check_irep_copies.cpp, the
binary to check-irep-copies and the runner to run_irep_copies_check.sh,
update the workflow and the cpplint exclusion in run_diff.sh to match,
and drop the superseded Python prototype.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Replace the hand-picked hot-path list in run_irep_copies_check.sh with a scan
of every translation unit in compile_commands.json, excluding only the unit/
and regression/ test trees (which deliberately copy ireps to exercise sharing
semantics). This catches unnecessary copies anywhere in the code base,
including jbmc.

Findings that are genuine false positives of the "copy-initialized but never
modified" heuristic -- where the copy is required for correctness and a
const reference would change behaviour -- are grandfathered in a baseline file
so the check fails only on new findings. Pin the on-demand build of the checker
to clang/LLVM 18 to match the version installed by the workflow.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Add regression/check-irep-copies/ with a self-contained fixture exercising both
detectors: two positive cases (an unmodified copy that should be a const
reference, and a last-use copy that should be a std::move) and negative cases
for const, loop-reused, member-expression, converting-constructor and
non-irept sources, plus a copy that is modified and whose source is reused.
run_self_test.sh builds the checker on demand and diffs its findings against
expected.txt, and the CI job runs it before scanning the tree so heuristic
regressions are caught.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
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