https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128021
Bug ID: 128021
Summary: [C++26 contracts] -g changes caller-side wrapper
emission order
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: c++-contracts, c++26, compare-debug-failure
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: yqtian668 at gmail dot com
Target Milestone: ---
Host: x86_64-pc-linux-gnu
Target: x86_64-pc-linux-gnu
Build: GCC 17.0.0 (experimental),
40b414ce63a5448495df9161aea0bfb495b47c11
Created attachment 65902
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65902&action=edit
patch and test
On x86_64-pc-linux-gnu, this fails with GCC master
`40b414ce63a5448495df9161aea0bfb495b47c11`:
```c++
int f (int a) pre (a > 2) { return a; }
int g (int a) pre (a > 3) { return a; }
int h (int a) pre (a > 4) { return a; }
int main () { return f (1) + g (1) + h (1); }
```
```text
$ g++ -std=c++26 -fcontracts -fcontracts-client-check=all \
-O0 -g -fcompare-debug -S source.C
g++: error: source.C: '-fcompare-debug' failure
```
With this compiler, that test fails the comparison at `-O0`, `-O1`, `-O2`
and `-Og`. The reduced source above fails at `-O0`, `-O1` and `-O2`.
The generated code differs in order rather than behavior. At `-O0`, the
caller-side `*.contract_wrapper` functions are emitted in a different order.
At `-O1` and above, the `.Lcontract_violationN` objects are numbered and
placed in `.rodata` in a different order. The instructions in each function
and the contents of the referenced violation objects remain the same.
The apparent cause is `emit_contract_wrapper_func` in
`gcc/cp/contracts.cc`. Contract wrappers are stored in
`decl_wrapper_fn`, a `hash_map<tree, tree>` keyed by `FUNCTION_DECL`, and
are later defined by traversing that map. The default `tree` hash depends on
the pointer value. Enabling debug information causes the front end to allocate
additional trees, which changes the key addresses and therefore the map
traversal order.
A candidate fix keeps the existing GTY-rooted map for lookup, records wrapper
keys in insertion order in a `vec<tree>`, and defines the wrappers in that
order. With that change, both the reduced source and GCC's existing test pass
the `-fcompare-debug` checks at all four optimization levels, and both `-g`
and `-g0` emit the wrappers in declaration order. A forced-GGC run with
`--param=ggc-min-expand=0 --param=ggc-min-heapsize=0` also passes. The patch
passes `contrib/check_GNU_style.sh`, but a full bootstrap and regression
comparison has not yet been run.
I searched Bugzilla for `contract compare-debug`, `contracts wrapper order`,
`emit_contract_wrapper_func`, `contract_violation order`, and `callerside
contract`, and did not find a matching report. PR125574 and PR127281 appear
unrelated.
This issue was found by a non-AI automated testing tool. AI assisted with the
analysis and development of the candidate fix.
Would keeping a separate insertion-order vector alongside the GTY-rooted map be
an appropriate fix here, or would a GC-rooted container be preferred?
I would also appreciate advice on the preferred regression-test strategy, since
a test based only on three pointer-hashed keys could pass accidentally on some
host configurations.