cassemblygccx86

Why does GCC generate different opcodes for multiplication based on a value of the constant?


I was playing around with GCC disassembler on gcc.godbolt.org and I noticed that GCC starting version 4.6 compiles multiplication differently. I have the following two functions:

unsigned m126(unsigned i)
{
    return i * 126;
}

unsigned m131(unsigned i)
{
    return i * 131;
}

m126 compiles into:

mov eax, edi
mov edx, 126
imul eax, edx
ret

And m131 compiles into:

imul eax, edi, 131
ret

Why is the difference? GCC 4.5 generates the same opcode in both cases.

A link to the actual example on GCC Explorer.


Solution

  • Found this in gcc/config/i386/i386.md (see the comment on the top):

    ;; imul $8/16bit_imm, regmem, reg is vector decoded.
    ;; Convert it into imul reg, reg
    ;; It would be better to force assembler to encode instruction using long
    ;; immediate, but there is apparently no way to do so.
    (define_peephole2
      [(parallel [(set (match_operand:SWI248 0 "register_operand")
               (mult:SWI248
                (match_operand:SWI248 1 "nonimmediate_operand")
                (match_operand:SWI248 2 "const_int_operand")))
              (clobber (reg:CC FLAGS_REG))])
       (match_scratch:SWI248 3 "r")]
      "TARGET_SLOW_IMUL_IMM8 && optimize_insn_for_speed_p ()
       && satisfies_constraint_K (operands[2])"
      [(set (match_dup 3) (match_dup 2))
       (parallel [(set (match_dup 0) (mult:SWI248 (match_dup 0) (match_dup 3)))
              (clobber (reg:CC FLAGS_REG))])]
    {
      if (!rtx_equal_p (operands[0], operands[1]))
        emit_move_insn (operands[0], operands[1]);
    })
    

    Seems like it has something to do with instruction decoding (sorry I'm not an expert)