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.

Reply via email to