Test code: register unsigned char val asm("r4"); void negate(void) { if (val) val = ~val; else val = ~val; } Code generated with -Os .global negate .type negate, @function negate: /* prologue: function */ /* frame size = 0 */ tst r4 breq .L2 com r4 ret .L2: clr r4 dec r4 ret .size negate, .-negate In the "else" branch gcc knows that r4 is zero, and that it should change to 0xff - taking this approach generates longer code than in the first branch. The same applies when operations like !, ++, -- are used.
Created attachment 16730 [details] Test case.
This happens on i386-darwin also (-m64 -O2 -fomit-frame-pointer): movl %edi, %edx testb %dil, %dil movl $-1, %eax notl %edx cmovne %edx, %eax Using the following source: unsigned char negate(unsigned char val) { if (val) val = ~val; else val = ~val; return val; } We should be able to just do ~val but this comes down to hoisting before applying VRP/DOM.
Mine. if (val_2(D) != 0) goto <bb 3>; [INV] else goto <bb 4>; [INV] <bb 3> : val_4 = ~val_2(D); <bb 4> : # val_1 = PHI <val_4(3), 255(2)>
Fixed for GCC 12 by r12-5699-gde3e5aae6c4b540e8 Specifically this part: X != C1 ? ~X : C2 simplifies to ~X when ~C1 == C2. which is the BIT_NOT_EXPR analog of the NEGATE_EXPR case.