https://gcc.gnu.org/g:d794365c8f0dcf6c5cdd742296c64c19278de1f8

commit r17-4004-gd794365c8f0dcf6c5cdd742296c64c19278de1f8
Author: Rohith Kapelli <[email protected]>
Date:   Sun Sep 6 00:48:26 2026 +0530

    ipa-icf: compare the field, not just the offset, under OEP_ADDRESS_OF 
[PR119006]
    
    func_checker::compare_operand compares non-memory operands with a bare
    operand_equal_p and OEP_MATCH_SIDE_EFFECTS.  When the operand is an
    ADDR_EXPR, operand_equal_p recurses with OEP_ADDRESS_OF set, and for a
    COMPONENT_REF under OEP_ADDRESS_OF it accepts two different FIELD_DECLs
    whose DECL_FIELD_OFFSET and DECL_FIELD_BIT_OFFSET agree.  That is correct
    for the value of the address, but ICF is not only comparing an address.
    The surviving body carries one of the two field types into every caller,
    and get_range_strlen_tree reads TYPE_DOMAIN and TYPE_MAX_VALUE off it.
    
    In the PR two structs hold a char array as their first member, one of 11
    and one of 128 elements, with identical operator== bodies.  ICF unifies
    them, the surviving body carries char[11], and the strlen machinery then
    concludes that a 15 character string cannot be there.
    
    Pass OEP_ADDRESS_OF_SAME_FIELD, which tree-core.h already documents for
    this case: "For OEP_ADDRESS_OF of COMPONENT_REFs, only consider same
    fields as equivalent rather than also different fields with the same
    offset."  fold-const.cc honours it at the point where the two offsets
    would otherwise be accepted.
    
            PR ipa/119006
    
    gcc/ChangeLog:
    
            * ipa-icf-gimple.cc (func_checker::compare_operand): Pass
            OEP_ADDRESS_OF_SAME_FIELD to operand_equal_p.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ipa/pr119006.C: New test.
    
    Signed-off-by: Rohith Kapelli <[email protected]>

Diff:
---
 gcc/ipa-icf-gimple.cc               |  3 ++-
 gcc/testsuite/g++.dg/ipa/pr119006.C | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/gcc/ipa-icf-gimple.cc b/gcc/ipa-icf-gimple.cc
index a893a3faaa42..720a3a0ce1a0 100644
--- a/gcc/ipa-icf-gimple.cc
+++ b/gcc/ipa-icf-gimple.cc
@@ -439,7 +439,8 @@ func_checker::compare_operand (tree t1, tree t2, 
operand_access_type access)
     }
   else
     {
-      if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
+      if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS
+                                      | OEP_ADDRESS_OF_SAME_FIELD))
        return true;
       return return_false_with_msg
                 ("operand_equal_p failed");
diff --git a/gcc/testsuite/g++.dg/ipa/pr119006.C 
b/gcc/testsuite/g++.dg/ipa/pr119006.C
new file mode 100644
index 000000000000..aa975eb30042
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/pr119006.C
@@ -0,0 +1,33 @@
+// PR ipa/119006
+// { dg-do run { target c++11 } }
+// { dg-options "-O2 -fwhole-program -fdump-ipa-icf-details" }
+
+struct FixedString10 {
+    bool operator==(const char* rhs_) const { return rhs_ and not 
__builtin_strcmp(_str, rhs_); }
+    char _str[11];
+};
+struct FixedString127 {
+    bool operator==(const char* rhs_) const { return rhs_ and not 
__builtin_strcmp(_str, rhs_); }
+    bool operator!=(const char* rhs_) const { return !(*this == rhs_); }
+    char _str[128];
+};
+[[gnu::noinline,gnu::used]]
+int fixedStringUser(const FixedString10& lhs, const char* rhs) {
+    return lhs == rhs;
+}
+constexpr const char *t = "StrOverTenChars";
+[[gnu::noinline,gnu::used]]
+void checkString(FixedString127 reason_) {
+    if (reason_ != t) __builtin_trap();
+}
+
+int main(int argc, char** argv) {
+    FixedString127 fs;
+    __builtin_strcpy(fs._str, t);
+    checkString(fs);
+}
+
+// The two operator== bodies are identical but their _str members have
+// different bounds, so ICF must not treat them as equal.
+// { dg-final { scan-ipa-dump-not "Unified" "icf" } }
+// { dg-final { scan-ipa-dump "Equal symbols: 0" "icf" } }

Reply via email to