https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86106
--- Comment #4 from Kishan Parmar <kishan at gcc dot gnu.org> --- Hello @Surya For the above case, the gimple looks like: unsigned int g (unsigned int val) { _1 = val_5(D) & 2139062143u; _2 = _1 + 2139062143u; _3 = _2 | val_5(D); _4 = _3 | 2139062143u; _6 = ~_4; return _6; } I’ve attached the RTL generated by expand with both vanilla GCC and the patched GCC. In vanilla GCC, expand sees the constant 2139062143 and checks what operation is being done. It compares the operation’s cost with the cost of a set (materializing the constant into a register). If the operation’s cost is lower, it uses the constant directly as the RHS. Otherwise, it loads the constant into a register first. For AND, the cost was 8, so GCC loaded the constant into a register before doing the operation. But for PLUS and IOR, the cost was 4, equal to set, so GCC used the constant directly as RHS. Later in expand, that constant gets split into two 16-bit halves and emitted piece by piece. The end result in RTL has those “half and half” constants, which the later optimization passes can’t make much use of. Looking at the md files, I saw that for IOR, PLUS, and XOR, if the constant is larger than 16 bits, we always end up splitting it anyway — so the cost effectively becomes 8 in practice. My patch adjusts the costing in expand for these ops: (1) If the constant fits in 16 bits, keep using it directly as RHS. COSTS_N_INSNS(1) (2) If it’s larger than 16 bits, treat it as COSTS_N_INSNS (2) For e.x. after the change constant above is being optimised in CSE1, fwprop1.. pass leaving shorter asm.