1: Sample code that reproduces the problem.
_Static_assert(0x10000LL != 0, "high bits");
_Static_assert(0x20000000000000ULL != 0x20000000000001ULL, "distinct values");
_Static_assert(65436U != -100LL, "unsigned value differs from negative value");
2: Exact command used to run SDCC on this sample code.
sdcc -mz80 --std-c11 -S longlong-equality.c
3: SDCC version tested (type "sdcc -v" to find it).
SDCC 4.6.4 #0 (Linux), built from SVN r16960 sources.
4: Copy of the error message or incorrect output, or a clear description of the observed versus expected behavior.
longlong-equality.c:1: warning 215: static assertion failed: "high bits"
longlong-equality.c:2: warning 215: static assertion failed: "distinct values"
longlong-equality.c:3: warning 215: static assertion failed: "unsigned value differs from negative value"
All three assertions are true. The first comparison must account for bit 16. The second compares two distinct integers above the range in which double can represent every integer exactly. In the third, 65436U converts to long long and remains positive, so it differs from -100LL.
valCompare can truncate equality comparisons involving long long constants to unsigned int. Separately, isAstEqual compares literal values after conversion to double, which can round distinct large integers to the same value.
The same failures occur on mcs51 (default, medium, large and stack-auto), ds390, z180, r2k, r4k, sm83, tlcs90, hc08, s08, stm8 and pdk13/14/15. They also occur with Z80 in C23 mode.
GCC 16.2.0 and Clang 23.1.0 accept the example without diagnostics: https://godbolt.org/z/nna4fKed9
While working on [bugs:#3952], I noticed a workaround in
stdckdint.hwhich avoids using_Genericin the way intended by its designer (to select a function before calling it), for performance reasons relating to feature request 880. In turn, that workaround required me to add casts that I would rather not have added because they undermine type safety.My candidate implementation of feature request 880 exposes this bug in checked integer arithmetic, so I believe this bug needs to be fixed first. In any case, it is clearly behaviour that is incompatible with Clang and GCC.
Related
Bugs:
#3952Last edit: Christopher Bazley 2026-10-05
Please find a candidate patch to fix this bug attached.
As the comment in valCompare about missing long long support states, there probably is still a problem for relational operators (I guess when precision in the lower bits is lost as a value is converted to floating-point format).
The fix (both yours and my deduplicated variant above) make a test fail:
Last edit: Philipp Klaus Krause 2026-10-06
Hi Philipp, please find a revised candidate patch attached.
It shares
valComparebetween AST and iCode constant folding for all six comparison operators, removing the duplicated equality handling and fixing relational comparisons. This significantly reduces the amount of code in SDCCicode.c.Integer operands undergo integer promotion and common-type conversion before comparison, preserving wide values and signed ordering. The unsigned
> 0simplification now checks the full constant value.The failing
longlong_test_bitassertion exposed incorrect iCode bitwise constant folding. UsingvalBitwisefor&,|and^avoids rounding operands through double or truncating them to 32 bits.The added tests cover wide comparisons, mixed signedness, checked subtraction into a narrower result type, and agreement between constant-folded and runtime bitwise operations.
This fix is a prerequisite for the patch that implements [feature-requests:#880].
Last edit: Christopher Bazley 2026-10-06
Most of this looks good to me. I do have a question on the changes in
isAstEqualthough: the changes clearly fix false positives for long long comparisons, but I think they introduce new false negatives for mixed sign. That might not cause real bugs, since looking at the uses ofisAstEqual, I think false negatives just result in some missed optimizations. Still it feels like the false negatives should either be fixed, or clearly stated in a comment.Hi Philipp, thanks for the review.
Unfortunately, I think that some attractive-looking optimisations that SDCC currently performs are unsafe. For example, consider the following function:
It might seem as though the two subexpressions are equal, but in fact the righthand expression is equivalent to
(unsigned long)x / 2UL, which produces a huge quotient ifxis negative. For example, ifx == -4Lthen(unsigned long)xisULONG_MAX - 4L + 1and the righthand side is(ULONG_MAX - 4L + 1) / 2UL. In contrast, the first subexpression would be-4L / 2L, which equals-2L. When converted to the common type,-2LisULONG_MAX - 2L + 1, which is much greater than(ULONG_MAX - 4L + 1) / 2UL.SDCC trunk outputs (for 6502):
Whereas GCC 16.1.0 outputs (for ARM):
See https://godbolt.org/z/d1zKMheKE
Somehow the AST feels like the wrong place to do optimizations anyway. But these are old optimizations that have been there for a long time, and removing them might result in code size regressions, unless we have other optimizations (on the iCode) to replace them.
I guess we can go with your patch for now, and just consider singed vs. unsigned different AST (though that also feels odd when signed char is still the same as signed long long, and unsigned char the same as unsigned long long).
Last edit: Philipp Klaus Krause 2026-10-06
The attached patch only adds comments and tests for the optimisations that were previously done despite not being strictly correct.
Last edit: Christopher Bazley 2026-10-06
Fixed in [r16974] via your patch.
Related
Commit: [r16974]
If you consider this bug closed, please reopen a new one for the unsafe optimisation. It silently generates wrong code.
It no longer does with the patch in (despite the patch originally intended to fix the bug reported here)?