[Bug ipa/119006] New: IPA ICF and LTO cause strcmp condition to be omitted

2025-02-24 Thread jeff-gcc at caffeinated dot me.uk via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119006

Bug ID: 119006
   Summary: IPA ICF and LTO cause strcmp condition to be omitted
   Product: gcc
   Version: 14.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: ipa
  Assignee: unassigned at gcc dot gnu.org
  Reporter: jeff-gcc at caffeinated dot me.uk
  Target Milestone: ---

When compiled with -O2 -flto  (or more minimally: -O -finline
-fipa-icf-functions -foptimize-strlen -ftree-dse -ftree-vrp -flto), GCC
miscompiles the following code:

template struct FixedString {
FixedString(const char* str_) { *this = str_; }
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[N + 1];
};
int fixedStringUser(const FixedString<10>& lhs, const char* rhs) {
return lhs == rhs;
}
void checkString(FixedString<127> reason_) {
if (reason_ != "StrOverTenChars") asm volatile("nop");
}
int main(int argc, char** argv) {
FixedString<127> fs = argv[1];
if (argc > 2) checkString(fs);
}

During LTO WPA, ICF replaces FixedString<127>::operator== with
FixedString<10>::operator==, and then LTRANS reasons that strcmp of a buffer
which can hold a string up to length 10 cannot be equal to a string longer than
10 characters, forgetting that the LHS is actually max length 127. This results
in `checkString` being reduced to the body of the `if` condition, i.e. just the
`asm volatile("nop")`.

Testing on godbolt (https://gcc.godbolt.org/z/bv3YqzTG5) shows that this has
been present since GCC11, and is still present in trunk.

[Bug ipa/119006] IPA ICF and LTO cause strcmp condition to be omitted

2025-02-24 Thread jeff-gcc at caffeinated dot me.uk via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119006

--- Comment #2 from Jeff Snyder  ---
Further simplified:

template struct FixedString {
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[N + 1];
};
int fixedString10User(const FixedString<10>& lhs, const char* rhs) {
return lhs == rhs;
}
void checkString(FixedString<127> reason_) {
if (reason_ != "StrOverTenChars") asm volatile("nop");
}
int main() {}


I also had to build with `-rdynamic` to repro without `checkString` being
omitted entirely. Observed on x86-64.