Conversation
tautschnig
left a comment
There was a problem hiding this comment.
A few questions; I overall like this approach, but what we really need for this to move forward is a solid performance testing set up that has long been talked about.
src/util/irep.h
Outdated
There was a problem hiding this comment.
What is the rationale for this change?
There was a problem hiding this comment.
See following comment.
There was a problem hiding this comment.
Same here: keep it simple. We don't need to solve problems we don't have.
src/util/small_shared_ptr.h
Outdated
There was a problem hiding this comment.
What's the idea of the separate using directive, instead of just doing std::swap(t_, rhs.t_); ?
There was a problem hiding this comment.
In the general case, using std::swap allows you to place a custom swap for your type in a namespace other than std, because manually overloading std::swap isn't allowed. This overload can be found by Koenig lookup, but if not found will fall back to std::swap. In this case, this behaviour isn't strictly required, so I could just use std::swap directly.
src/util/small_shared_ptr.h
Outdated
There was a problem hiding this comment.
What is the use case of this one?
There was a problem hiding this comment.
Like std::make_unique and std::make_shared, this has exception-safety benefits.
In the expression my_func(small_shared_ptr<X>(new X), small_shared_ptr<Y>(new Y)), the compiler is allowed to sequence the operations like so:
new X
new Y <-- This may throw and leak previously-allocated X
construct small_shared_ptr<X>
construct small_shared_ptr<Y>
my_func
Whereas my_func(make_small_shared_ptr<X>(), make_small_shared_ptr<Y>()) cannot leak because the calls to make_small_shared_ptr won't be interleaved.
There was a problem hiding this comment.
Shouldn't that be my_func(make_small_shared_ptr<X, Y>()) ?
There was a problem hiding this comment.
No, empty parameter packs are valid. You can see at irep.cpp:30 and irep.h:242 how the function is used in practice.
There was a problem hiding this comment.
Ok, I think I just misunderstood: your argument was about the order of invoking new vs the construction of small_shared_ptr, and not the order of the small_shared_ptr constructs (which is still unspecified). Fair enough. You may wish to add a comment to the constructor of small_shared_ptr to say that make_small_shared_ptr should be used in case of a new T argument.
What would be a use case where the parameter pack is non-empty? Is this just to support types other than dt that require >= 1 argument in their constructor?
There was a problem hiding this comment.
Yes, if you have a type with a constructor like my_type(Foo, Bar, Baz) then you can create a small_shared_ptr to it using make_small_shared_ptr<my_type>(Foo(), Bar(), Baz()) (for example).
This also avoids repetition of the identifier my_type (the alternative would be small_shared_ptr<my_type>(new my_type(Foo(), Bar(), Baz()) which is needlessly repetitive).
|
As said before, this looks good to me, and includes quite a bit of long overdue cleanup. Yet I don't dare setting any approval here in absence of a performance test suite. |
|
With 22170b7 my looks-good-to-me no longer applies. |
|
Yes, sorry about that - I think the copy-on-write implementation currently in irep is buggy because it allows you to make changes which affect copies in unexpected ways: I'm guessing this is why I'll close the PR and re-open when I have something that fixes this bug. |
|
I don't necessarily think you have to go as far as closing this one. How about making |
This commit merges the best aspects of two approaches to hash-based loop identification: - Clean implementation from PR diffblue#732 (bigweaver/clone-cbmc-private-20251130-231902) - Comprehensive testing from PR diffblue#786 (bigweaver/clone-cbmc-private-20251209-144542) Core Implementation (from PR diffblue#732): - Enhanced loop_idt struct with hash support and backward compatibility - compute_loop_hashes() using AST fingerprinting approach - Hash based on source location, loop structure, and body characteristics - Uses hash_combine() and hash_finalize() utilities - Clean separation of concerns with modular design Testing Infrastructure (from PR diffblue#786): - Unit tests: unit/goto-programs/loop_hash.cpp (Catch2-based) - Basic regression: 3 test suites (types, nested, stability) - Comprehensive suite: 21 automated test cases covering: * Position independence (11 tests) * Sensitivity to changes (4 tests) * Determinism (4 tests) * Special cases (2 tests) - Multiple test frameworks: Python (basic + enhanced) and Bash - Test utilities for hash comparison and extraction Key Benefits: - Loop identifiers stable across unrelated code changes - Hashes change appropriately when loop logic changes - Backward compatible with existing loop_number system - Comprehensive test coverage (21+ test cases) - Well-documented with extensive README files Files Modified: - Core: 5 implementation files in src/goto-programs/ - Tests: 52 test files (unit + regression) - Build: unit/Makefile updated See LOOP_HASH_MERGE_SUMMARY.md for detailed documentation.
Follows up #685. Rather than using
std::shared_ptrwhich uses more memory than is strictly necessary (an extra pointer to the control block in the shared_ptr itself, plus the control block memory) we use a customsmall_shared_ptrwhich is based on boost'sintrusive_ptr.This approach uses exactly the same amount of memory as was used previously, but separating the resource management into a separate class makes the logic a bit clearer, and greatly simplifies the copy- and move-assignment operators. It also means we don't need to explicitly redeclare custom move/copy constructors/assignment-ops in
irep.h.