This patch adds the new optabs and makes MIPS use them. There are probably some clean-up opportunities in mips_use_ins_ext_p after this change, but I'll look at that separately.
Tested as described in the covering note. OK to install? Richard gcc/ * doc/md.texi (extv@var{m}, extvmisalign@var{m}, extzv@var{m}) (extzvmisalign@var{m}, insv@var{m}, insvmisalign@var{m}): Document. (insv, extv, extzv): Deprecate. * optabs.def (insv_optab, extv_optab, extzv_optab) (insvmisalign_optab, extvmisalign_optab, extzvmisalign_optab): New optabs. * optabs.c (get_optab_extraction_insn): New function. (get_extraction_insn): Use it. * config/mips/mips.md (extv): Split into... (extvmisalign<mode>, extv<mode>): ...these new patterns. Rename existing extv<mode> pattern to... (*extv<mode>): ...this. (extzv): Split into... (extzvmisalign<mode>, extzv<mode>): ...these new patterns. Rename existing extzv<mode> pattern to... (*extzv<mode>): ...this. (insv): Split into... (insvmisalign<mode>, insv<mode>): ...these new patterns. Rename existing insv<mode> pattern to... (*insv<mode>): ...this. Use const_int_operand rather than immediate_operand. * config/mips/mips.c (mips_block_move_straight): Use set_mem_size to set the size of BLKmode accesses. (mips_get_unaligned_mem): Require OP0 to be a BLKmode memory, turning it from an "rtx *" to an rtx. (mips_expand_ext_as_unaligned_load): Simplify for new optab interface. Update call to mips_get_unaligned_mem. (mips_expand_ins_as_unaligned_store): Update call to mips_get_unaligned_mem. Index: gcc/doc/md.texi =================================================================== --- gcc/doc/md.texi 2012-11-02 22:43:15.315373159 +0000 +++ gcc/doc/md.texi 2012-11-02 23:33:53.633365725 +0000 @@ -5288,6 +5288,62 @@ Convert unsigned integer operand 1 of mo When overflows or underflows happen, the instruction saturates the results to the maximum or the minimum. +@cindex @code{extv@var{m}} instruction pattern +@item @samp{extv@var{m}} +Extract a bit-field from register operand 1, sign-extend it, and store +it in operand 0. Operand 2 specifies the width of the field in bits +and operand 3 the starting bit, which counts from the most significant +bit if @samp{BITS_BIG_ENDIAN} is true and from the least significant bit +otherwise. + +Operands 0 and 1 both have mode @var{m}. Operands 2 and 3 have a +target-specific mode. + +@cindex @code{extvmisalign@var{m}} instruction pattern +@item @samp{extvmisalign@var{m}} +Extract a bit-field from memory operand 1, sign extend it, and store +it in operand 0. Operand 2 specifies the width in bits and operand 3 +the starting bit. The starting bit is always somewhere in the first byte of +operand 1; it counts from the most significant bit if @samp{BITS_BIG_ENDIAN} +is true and from the least significant bit otherwise. + +Operand 0 has mode @var{m} while operand 1 has @code{BLK} mode. +Operands 2 and 3 have a target-specific mode. + +The instruction must not read beyond the last byte of the bit-field. + +@cindex @code{extzv@var{m}} instruction pattern +@item @samp{extzv@var{m}} +Like @samp{extv@var{m}} except that the bit-field value is zero-extended. + +@cindex @code{extzvmisalign@var{m}} instruction pattern +@item @samp{extzvmisalign@var{m}} +Like @samp{extvmisalign@var{m}} except that the bit-field value is +zero-extended. + +@cindex @code{insv@var{m}} instruction pattern +@item @samp{insv@var{m}} +Insert operand 3 into a bit-field of register operand 0. Operand 1 +specifies the width of the field in bits and operand 2 the starting bit, +which counts from the most significant bit if @samp{BITS_BIG_ENDIAN} +is true and from the least significant bit otherwise. + +Operands 0 and 3 both have mode @var{m}. Operands 1 and 2 have a +target-specific mode. + +@cindex @code{insvmisalign@var{m}} instruction pattern +@item @samp{insvmisalign@var{m}} +Insert operand 3 into a bit-field of memory operand 0. Operand 1 +specifies the width of the field in bits and operand 2 the starting bit. +The starting bit is always somewhere in the first byte of operand 0; +it counts from the most significant bit if @samp{BITS_BIG_ENDIAN} +is true and from the least significant bit otherwise. + +Operand 3 has mode @var{m} while operand 0 has @code{BLK} mode. +Operands 1 and 2 have a target-specific mode. + +The instruction must not read or write beyond the last byte of the bit-field. + @cindex @code{extv} instruction pattern @item @samp{extv} Extract a bit-field from operand 1 (a register or memory operand), where @@ -5303,10 +5359,16 @@ for operands 2 and 3 and the constant is The bit-field value is sign-extended to a full word integer before it is stored in operand 0. +This pattern is deprecated; please use @samp{extv@var{m}} and +@code{extvmisalign@var{m}} instead. + @cindex @code{extzv} instruction pattern @item @samp{extzv} Like @samp{extv} except that the bit-field value is zero-extended. +This pattern is deprecated; please use @samp{extzv@var{m}} and +@code{extzvmisalign@var{m}} instead. + @cindex @code{insv} instruction pattern @item @samp{insv} Store operand 3 (which must be valid for @code{word_mode}) into a @@ -5318,6 +5380,9 @@ Operands 1 and 2 must be valid for @code The RTL generation pass generates this instruction only with constants for operands 1 and 2 and the constant is never zero for operand 1. +This pattern is deprecated; please use @samp{insv@var{m}} and +@code{insvmisalign@var{m}} instead. + @cindex @code{mov@var{mode}cc} instruction pattern @item @samp{mov@var{mode}cc} Conditionally move operand 2 or operand 3 into operand 0 according to the Index: gcc/optabs.def =================================================================== --- gcc/optabs.def 2012-11-02 22:43:15.314373159 +0000 +++ gcc/optabs.def 2012-11-02 23:33:53.622365725 +0000 @@ -171,6 +171,12 @@ OPTAB_DC(mov_optab, "mov$a", SET) OPTAB_DC(movstrict_optab, "movstrict$a", STRICT_LOW_PART) OPTAB_D (movmisalign_optab, "movmisalign$a") OPTAB_D (storent_optab, "storent$a") +OPTAB_D (insv_optab, "insv$a") +OPTAB_D (extv_optab, "extv$a") +OPTAB_D (extzv_optab, "extzv$a") +OPTAB_D (insvmisalign_optab, "insvmisalign$a") +OPTAB_D (extvmisalign_optab, "extvmisalign$a") +OPTAB_D (extzvmisalign_optab, "extzvmisalign$a") OPTAB_D (push_optab, "push$a1") OPTAB_D (reload_in_optab, "reload_in$a") OPTAB_D (reload_out_optab, "reload_out$a") Index: gcc/optabs.c =================================================================== --- gcc/optabs.c 2012-11-02 23:25:16.059366992 +0000 +++ gcc/optabs.c 2012-11-02 23:36:03.255365408 +0000 @@ -8296,6 +8296,35 @@ get_traditional_extraction_insn (extract return true; } +/* Return true if an optab exists to perform an insertion or extraction + of type TYPE in mode MODE. Describe the instruction in *INSN if so. + + REG_OPTAB is the optab to use for register structures and + MISALIGN_OPTAB is the optab to use for misaligned memory structures. + POS_OP is the operand number of the bit position. */ + +static bool +get_optab_extraction_insn (struct extraction_insn *insn, + enum extraction_type type, + enum machine_mode mode, direct_optab reg_optab, + direct_optab misalign_optab, int pos_op) +{ + direct_optab optab = (type == ET_unaligned_mem ? misalign_optab : reg_optab); + enum insn_code icode = direct_optab_handler (optab, mode); + if (icode == CODE_FOR_nothing) + return false; + + const struct insn_data_d *data = &insn_data[icode]; + + insn->icode = icode; + insn->field_mode = mode; + insn->struct_mode = (type == ET_unaligned_mem ? BLKmode : mode); + insn->pos_mode = data->operand[pos_op].mode; + if (insn->pos_mode == VOIDmode) + insn->pos_mode = word_mode; + return true; +} + /* Return true if an instruction exists to perform an insertion or extraction (PATTERN says which) of type TYPE on a structure of mode MODE. Describe the instruction in *INSN if so. */ @@ -8313,21 +8342,24 @@ get_extraction_insn (extraction_insn *in && get_traditional_extraction_insn (insn, type, mode, CODE_FOR_insv, 0, 3)) return true; - return false; + return get_optab_extraction_insn (insn, type, mode, insv_optab, + insvmisalign_optab, 2); case EP_extv: if (HAVE_extv && get_traditional_extraction_insn (insn, type, mode, CODE_FOR_extv, 1, 0)) return true; - return false; + return get_optab_extraction_insn (insn, type, mode, extv_optab, + extvmisalign_optab, 3); case EP_extzv: if (HAVE_extzv && get_traditional_extraction_insn (insn, type, mode, CODE_FOR_extzv, 1, 0)) return true; - return false; + return get_optab_extraction_insn (insn, type, mode, extzv_optab, + extzvmisalign_optab, 3); default: gcc_unreachable (); Index: gcc/config/mips/mips.md =================================================================== --- gcc/config/mips/mips.md 2012-11-02 22:43:15.316373159 +0000 +++ gcc/config/mips/mips.md 2012-11-02 23:33:53.628365725 +0000 @@ -3777,11 +3777,11 @@ (define_expand "fixuns_truncsfdi2" ;; Bit field extract patterns which use lwl/lwr or ldl/ldr. -(define_expand "extv" - [(set (match_operand 0 "register_operand") - (sign_extract (match_operand 1 "nonimmediate_operand") - (match_operand 2 "const_int_operand") - (match_operand 3 "const_int_operand")))] +(define_expand "extvmisalign<mode>" + [(set (match_operand:GPR 0 "register_operand") + (sign_extract:GPR (match_operand:BLK 1 "memory_operand") + (match_operand 2 "const_int_operand") + (match_operand 3 "const_int_operand")))] "!TARGET_MIPS16" { if (mips_expand_ext_as_unaligned_load (operands[0], operands[1], @@ -3789,22 +3789,22 @@ (define_expand "extv" INTVAL (operands[3]), /*unsigned=*/ false)) DONE; - else if (register_operand (operands[1], GET_MODE (operands[0])) - && ISA_HAS_EXTS && UINTVAL (operands[2]) <= 32) - { - if (GET_MODE (operands[0]) == DImode) - emit_insn (gen_extvdi (operands[0], operands[1], operands[2], - operands[3])); - else - emit_insn (gen_extvsi (operands[0], operands[1], operands[2], - operands[3])); - DONE; - } else FAIL; }) -(define_insn "extv<mode>" +(define_expand "extv<mode>" + [(set (match_operand:GPR 0 "register_operand") + (sign_extract:GPR (match_operand:GPR 1 "register_operand") + (match_operand 2 "const_int_operand") + (match_operand 3 "const_int_operand")))] + "ISA_HAS_EXTS" +{ + if (UINTVAL (operands[2]) > 32) + FAIL; +}) + +(define_insn "*extv<mode>" [(set (match_operand:GPR 0 "register_operand" "=d") (sign_extract:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand 2 "const_int_operand" "") @@ -3814,35 +3814,35 @@ (define_insn "extv<mode>" [(set_attr "type" "arith") (set_attr "mode" "<MODE>")]) - -(define_expand "extzv" - [(set (match_operand 0 "register_operand") - (zero_extract (match_operand 1 "nonimmediate_operand") - (match_operand 2 "const_int_operand") - (match_operand 3 "const_int_operand")))] +(define_expand "extzvmisalign<mode>" + [(set (match_operand:GPR 0 "register_operand") + (zero_extract:GPR (match_operand:BLK 1 "memory_operand") + (match_operand 2 "const_int_operand") + (match_operand 3 "const_int_operand")))] "!TARGET_MIPS16" { if (mips_expand_ext_as_unaligned_load (operands[0], operands[1], INTVAL (operands[2]), INTVAL (operands[3]), - /*unsigned=*/true)) + /*unsigned=*/ true)) DONE; - else if (mips_use_ins_ext_p (operands[1], INTVAL (operands[2]), - INTVAL (operands[3]))) - { - if (GET_MODE (operands[0]) == DImode) - emit_insn (gen_extzvdi (operands[0], operands[1], operands[2], - operands[3])); - else - emit_insn (gen_extzvsi (operands[0], operands[1], operands[2], - operands[3])); - DONE; - } else FAIL; }) -(define_insn "extzv<mode>" +(define_expand "extzv<mode>" + [(set (match_operand:GPR 0 "register_operand") + (zero_extract:GPR (match_operand:GPR 1 "register_operand") + (match_operand 2 "const_int_operand") + (match_operand 3 "const_int_operand")))] + "" +{ + if (!mips_use_ins_ext_p (operands[1], INTVAL (operands[2]), + INTVAL (operands[3]))) + FAIL; +}) + +(define_insn "*extzv<mode>" [(set (match_operand:GPR 0 "register_operand" "=d") (zero_extract:GPR (match_operand:GPR 1 "register_operand" "d") (match_operand 2 "const_int_operand" "") @@ -3865,36 +3865,37 @@ (define_insn "*extzv_truncsi_exts" (set_attr "mode" "SI")]) -(define_expand "insv" - [(set (zero_extract (match_operand 0 "nonimmediate_operand") - (match_operand 1 "immediate_operand") - (match_operand 2 "immediate_operand")) - (match_operand 3 "reg_or_0_operand"))] +(define_expand "insvmisalign<mode>" + [(set (zero_extract:GPR (match_operand:BLK 0 "memory_operand") + (match_operand 1 "const_int_operand") + (match_operand 2 "const_int_operand")) + (match_operand:GPR 3 "reg_or_0_operand"))] "!TARGET_MIPS16" { if (mips_expand_ins_as_unaligned_store (operands[0], operands[3], INTVAL (operands[1]), INTVAL (operands[2]))) DONE; - else if (mips_use_ins_ext_p (operands[0], INTVAL (operands[1]), - INTVAL (operands[2]))) - { - if (GET_MODE (operands[0]) == DImode) - emit_insn (gen_insvdi (operands[0], operands[1], operands[2], - operands[3])); - else - emit_insn (gen_insvsi (operands[0], operands[1], operands[2], - operands[3])); - DONE; - } - else - FAIL; + else + FAIL; +}) + +(define_expand "insv<mode>" + [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand") + (match_operand 1 "const_int_operand") + (match_operand 2 "const_int_operand")) + (match_operand:GPR 3 "reg_or_0_operand"))] + "" +{ + if (!mips_use_ins_ext_p (operands[0], INTVAL (operands[1]), + INTVAL (operands[2]))) + FAIL; }) -(define_insn "insv<mode>" +(define_insn "*insv<mode>" [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand" "+d") - (match_operand:SI 1 "immediate_operand" "I") - (match_operand:SI 2 "immediate_operand" "I")) + (match_operand:SI 1 "const_int_operand" "") + (match_operand:SI 2 "const_int_operand" "")) (match_operand:GPR 3 "reg_or_0_operand" "dJ"))] "mips_use_ins_ext_p (operands[0], INTVAL (operands[1]), INTVAL (operands[2]))" Index: gcc/config/mips/mips.c =================================================================== --- gcc/config/mips/mips.c 2012-11-02 22:43:15.316373159 +0000 +++ gcc/config/mips/mips.c 2012-11-02 23:33:53.647365725 +0000 @@ -7101,6 +7101,7 @@ mips_block_move_straight (rtx dest, rtx else { rtx part = adjust_address (src, BLKmode, offset); + set_mem_size (part, delta); if (!mips_expand_ext_as_unaligned_load (regs[i], part, bits, 0, 0)) gcc_unreachable (); } @@ -7113,6 +7114,7 @@ mips_block_move_straight (rtx dest, rtx else { rtx part = adjust_address (dest, BLKmode, offset); + set_mem_size (part, delta); if (!mips_expand_ins_as_unaligned_store (part, regs[i], bits, 0)) gcc_unreachable (); } @@ -7364,10 +7366,8 @@ mips_expand_atomic_qihi (union mips_gen_ } /* Return true if it is possible to use left/right accesses for a - bitfield of WIDTH bits starting BITPOS bits into *OP. When - returning true, update *OP, *LEFT and *RIGHT as follows: - - *OP is a BLKmode reference to the whole field. + bitfield of WIDTH bits starting BITPOS bits into BLKmode memory OP. + When returning true, update *LEFT and *RIGHT as follows: *LEFT is a QImode reference to the first byte if big endian or the last byte if little endian. This address can be used in the @@ -7377,16 +7377,11 @@ mips_expand_atomic_qihi (union mips_gen_ can be used in the patterning right-side instruction. */ static bool -mips_get_unaligned_mem (rtx *op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos, +mips_get_unaligned_mem (rtx op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos, rtx *left, rtx *right) { rtx first, last; - /* Check that the operand really is a MEM. Not all the extv and - extzv predicates are checked. */ - if (!MEM_P (*op)) - return false; - /* Check that the size is valid. */ if (width != 32 && (!TARGET_64BIT || width != 64)) return false; @@ -7399,20 +7394,12 @@ mips_get_unaligned_mem (rtx *op, HOST_WI /* Reject aligned bitfields: we want to use a normal load or store instead of a left/right pair. */ - if (MEM_ALIGN (*op) >= width) + if (MEM_ALIGN (op) >= width) return false; - /* Create a copy of *OP that refers to the whole field. This also has - the effect of legitimizing *OP's address for BLKmode, possibly - simplifying it. */ - *op = copy_rtx (adjust_address (*op, BLKmode, 0)); - set_mem_size (*op, width / BITS_PER_UNIT); - - /* Get references to both ends of the field. We deliberately don't - use the original QImode *OP for FIRST since the new BLKmode one - might have a simpler address. */ - first = adjust_address (*op, QImode, 0); - last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1); + /* Get references to both ends of the field. */ + first = adjust_address (op, QImode, 0); + last = adjust_address (op, QImode, width / BITS_PER_UNIT - 1); /* Allocate to LEFT and RIGHT according to endianness. LEFT should correspond to the MSB and RIGHT to the LSB. */ @@ -7440,14 +7427,6 @@ mips_expand_ext_as_unaligned_load (rtx d rtx dest1 = NULL_RTX; /* If TARGET_64BIT, the destination of a 32-bit "extz" or "extzv" will - be a paradoxical word_mode subreg. This is the only case in which - we allow the destination to be larger than the source. */ - if (GET_CODE (dest) == SUBREG - && GET_MODE (dest) == DImode - && GET_MODE (SUBREG_REG (dest)) == SImode) - dest = SUBREG_REG (dest); - - /* If TARGET_64BIT, the destination of a 32-bit "extz" or "extzv" will be a DImode, create a new temp and emit a zero extend at the end. */ if (GET_MODE (dest) == DImode && REG_P (dest) @@ -7457,12 +7436,7 @@ mips_expand_ext_as_unaligned_load (rtx d dest = gen_reg_rtx (SImode); } - /* After the above adjustment, the destination must be the same - width as the source. */ - if (GET_MODE_BITSIZE (GET_MODE (dest)) != width) - return false; - - if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right)) + if (!mips_get_unaligned_mem (src, width, bitpos, &left, &right)) return false; temp = gen_reg_rtx (GET_MODE (dest)); @@ -7504,7 +7478,7 @@ mips_expand_ins_as_unaligned_store (rtx rtx left, right; enum machine_mode mode; - if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right)) + if (!mips_get_unaligned_mem (dest, width, bitpos, &left, &right)) return false; mode = mode_for_size (width, MODE_INT, 0);