https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96930
--- Comment #8 from Jakub Jelinek <jakub at gcc dot gnu.org> --- The optimization is there, but just has different conditions: /* Although it would be tempting to shorten always here, that loses on some targets, since the modulo instruction is undefined if the quotient can't be represented in the computation mode. We shorten only if unsigned or if dividing by something we know != -1. */ shorten = (TYPE_UNSIGNED (TREE_TYPE (orig_op0)) || (TREE_CODE (op1) == INTEGER_CST && !integer_all_onesp (op1))); in C, and /* When dividing two signed integers, we have to promote to int. unless we divide by a constant != -1. Note that default conversion will have been performed on the operands at this point, so we have to dig out the original type to find out if it was unsigned. */ tree stripped_op1 = tree_strip_any_location_wrapper (op1); shorten = ((TREE_CODE (op0) == NOP_EXPR && TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)))) || (TREE_CODE (stripped_op1) == INTEGER_CST && ! integer_all_onesp (stripped_op1))); So, in C++ we only try to shorten divisions (and modulo) if the first operand has been extended, rather than the second one. Anyway, if we optimize this in the middle-end, it won't be needed to change the FE.