diff mbox series

[avr] Overhaul add and sub insns that extend one operand

Message ID 6d869528-3758-4755-9f9a-4d206ae61bdd@gjlay.de
State New
Headers show
Series [avr] Overhaul add and sub insns that extend one operand | expand

Commit Message

Georg-Johann Lay July 12, 2024, 11:01 a.m. UTC
These are insns of the forms

   (set (regA:M)
        (plus:M (extend:M (regB:L))
                (regA:M)))
and

   (set (regA:M)
        (minus:M (regA:M)
                 (extend:M (regB:L))))

where "extend" may be a sign-extend or zero-extend,
and the integer modes satisfy SImode >= M > L >= QImode.

Currently, these are represented as single insns (and splits).
This patch rewrites them in terms of mode iterators M and L
with SImode >= M > L >= QImode.

This gives 3 new insns (and splits) that cover 6 + 6 + 5 = 17 cases,
where previously there was support for just 8 cases.

The patch handles only the cases that have a plus or a sign-extend.
The minus & zero-extend case was already handled in 077f16b2.

Ok for trunk?

Johann

--

AVR: Overhaul add and sub insns that extend one operand.

These are insns of the forms

   (set (regA:M)
        (plus:M (extend:M (regB:L))
                (regA:M)))
or

   (set (regA:M)
        (minus:M (regA:M)
                 (extend:M (regB:L))))

where "extend" may be a sign-extend or zero-extend, and the
integer modes are  SImode >= M > L >= QImode.

The existing patterns are now represented in terms of insns
with mode iterators, and these new insn support all valid
combinations of M and L (which previously was not the case).

gcc/
	* config/avr/avr.cc (avr_out_minus): Assimilate into...
	(avr_out_plus_ext): ...this new function.
	(avr_adjust_insn_length) [ADJUST_LEN_PLUS_EXT]: Handle case.
	(avr_rtx_costs_1) [PLUS, MINUS]: Adjust RTX costs.
	* config/avr/avr.md (adjust_len) <plus_ext>: Add new attribute value.
	(PSISI): New mode iterator.
	(*addpsi3_zero_extend.hi_split): Assimilate...
	(*addpsi3_zero_extend.qi_split): Assimilate...
	(*addsi3_zero_extend_split): Assimilate...
	(*addsi3_zero_extend.hi_split): Assimilate...
	(*add<PSISI:mode>3.zero_extend.<QIPSI:mode>_split): ...into this
	new insn-and-split.
	(*addpsi3_zero_extend.hi): Assimilate...
	(*addpsi3_zero_extend.qi): Assimilate...
	(*addsi3_zero_extend): Assimilate...
	(*addsi3_zero_extend.hi): Assimilate...
	(*add<PSISI:mode>3.zero_extend.<QIPSI:mode>): ...into this new insn.
	(*addpsi3_sign_extend.hi_split): Assimilate...
	(*addhi3.sign_extend1_split): Assimilate...
	(*add<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
	insn-and-split.
	(*addpsi3_sign_extend.hi): Assimilate...
	(*addhi3.sign_extend1): Assimilate...
	(*add<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
	(*subpsi3_sign_extend.hi_split): Assimilate...
	(*subhi3.sign_extend2_split): Assimilate...
	(*sub<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
	insn-and-split.
	(*subpsi3_sign_extend.hi): Assimilate...
	(*subhi3.sign_extend2): Assimilate...
	(*sub<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
	(*sub<HISI:mode>3.zero_extend.<QIPSI:mode>): Use avr_out_plus_ext
	for asm out.
	* config/avr/avr-protos.h (avr_out_minus): Remove.
	(avr_out_plus_ext): New proto.
gcc/testsuite/
	* gcc.target/avr/torture/add-extend.c: New test.
	* gcc.target/avr/torture/sub-extend.c: New test.

Comments

Jeff Law July 16, 2024, 5:08 p.m. UTC | #1
On 7/12/24 5:01 AM, Georg-Johann Lay wrote:
> These are insns of the forms
> 
>    (set (regA:M)
>         (plus:M (extend:M (regB:L))
>                 (regA:M)))
> and
> 
>    (set (regA:M)
>         (minus:M (regA:M)
>                  (extend:M (regB:L))))
> 
> where "extend" may be a sign-extend or zero-extend,
> and the integer modes satisfy SImode >= M > L >= QImode.
> 
> Currently, these are represented as single insns (and splits).
> This patch rewrites them in terms of mode iterators M and L
> with SImode >= M > L >= QImode.
> 
> This gives 3 new insns (and splits) that cover 6 + 6 + 5 = 17 cases,
> where previously there was support for just 8 cases.
> 
> The patch handles only the cases that have a plus or a sign-extend.
> The minus & zero-extend case was already handled in 077f16b2.
> 
> Ok for trunk?
> 
> Johann
> 
> -- 
> 
> AVR: Overhaul add and sub insns that extend one operand.
> 
> These are insns of the forms
> 
>    (set (regA:M)
>         (plus:M (extend:M (regB:L))
>                 (regA:M)))
> or
> 
>    (set (regA:M)
>         (minus:M (regA:M)
>                  (extend:M (regB:L))))
> 
> where "extend" may be a sign-extend or zero-extend, and the
> integer modes are  SImode >= M > L >= QImode.
> 
> The existing patterns are now represented in terms of insns
> with mode iterators, and these new insn support all valid
> combinations of M and L (which previously was not the case).
> 
> gcc/
>      * config/avr/avr.cc (avr_out_minus): Assimilate into...
>      (avr_out_plus_ext): ...this new function.
>      (avr_adjust_insn_length) [ADJUST_LEN_PLUS_EXT]: Handle case.
>      (avr_rtx_costs_1) [PLUS, MINUS]: Adjust RTX costs.
>      * config/avr/avr.md (adjust_len) <plus_ext>: Add new attribute value.
>      (PSISI): New mode iterator.
>      (*addpsi3_zero_extend.hi_split): Assimilate...
>      (*addpsi3_zero_extend.qi_split): Assimilate...
>      (*addsi3_zero_extend_split): Assimilate...
>      (*addsi3_zero_extend.hi_split): Assimilate...
>      (*add<PSISI:mode>3.zero_extend.<QIPSI:mode>_split): ...into this
>      new insn-and-split.
>      (*addpsi3_zero_extend.hi): Assimilate...
>      (*addpsi3_zero_extend.qi): Assimilate...
>      (*addsi3_zero_extend): Assimilate...
>      (*addsi3_zero_extend.hi): Assimilate...
>      (*add<PSISI:mode>3.zero_extend.<QIPSI:mode>): ...into this new insn.
>      (*addpsi3_sign_extend.hi_split): Assimilate...
>      (*addhi3.sign_extend1_split): Assimilate...
>      (*add<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
>      insn-and-split.
>      (*addpsi3_sign_extend.hi): Assimilate...
>      (*addhi3.sign_extend1): Assimilate...
>      (*add<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
>      (*subpsi3_sign_extend.hi_split): Assimilate...
>      (*subhi3.sign_extend2_split): Assimilate...
>      (*sub<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
>      insn-and-split.
>      (*subpsi3_sign_extend.hi): Assimilate...
>      (*subhi3.sign_extend2): Assimilate...
>      (*sub<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
>      (*sub<HISI:mode>3.zero_extend.<QIPSI:mode>): Use avr_out_plus_ext
>      for asm out.
>      * config/avr/avr-protos.h (avr_out_minus): Remove.
>      (avr_out_plus_ext): New proto.
> gcc/testsuite/
>      * gcc.target/avr/torture/add-extend.c: New test.
>      * gcc.target/avr/torture/sub-extend.c: New test.
OK.  A bit hard to follow due to the prior ordering of patterns to make 
sure nothing got lost, but I think it all got covered.

Something to consider, I've found having an any_extend code iterator 
useful for this kind of pattern consolidation when all that differs is 
sign vs zero extend in the pattern.

jeff
Georg-Johann Lay July 16, 2024, 5:23 p.m. UTC | #2
Am 16.07.24 um 19:08 schrieb Jeff Law:
> 
> 
> On 7/12/24 5:01 AM, Georg-Johann Lay wrote:
>> These are insns of the forms
>>
>>    (set (regA:M)
>>         (plus:M (extend:M (regB:L))
>>                 (regA:M)))
>> and
>>
>>    (set (regA:M)
>>         (minus:M (regA:M)
>>                  (extend:M (regB:L))))
>>
>> where "extend" may be a sign-extend or zero-extend,
>> and the integer modes satisfy SImode >= M > L >= QImode.
>>
>> Currently, these are represented as single insns (and splits).
>> This patch rewrites them in terms of mode iterators M and L
>> with SImode >= M > L >= QImode.
>>
>> This gives 3 new insns (and splits) that cover 6 + 6 + 5 = 17 cases,
>> where previously there was support for just 8 cases.
>>
>> The patch handles only the cases that have a plus or a sign-extend.
>> The minus & zero-extend case was already handled in 077f16b2.
>>
>> Ok for trunk?
>>
>> Johann
>>
>> -- 
>>
>> AVR: Overhaul add and sub insns that extend one operand.
>>
>> These are insns of the forms
>>
>>    (set (regA:M)
>>         (plus:M (extend:M (regB:L))
>>                 (regA:M)))
>> or
>>
>>    (set (regA:M)
>>         (minus:M (regA:M)
>>                  (extend:M (regB:L))))
>>
>> where "extend" may be a sign-extend or zero-extend, and the
>> integer modes are  SImode >= M > L >= QImode.
>>
>> The existing patterns are now represented in terms of insns
>> with mode iterators, and these new insn support all valid
>> combinations of M and L (which previously was not the case).
>>
>> gcc/
>>      * config/avr/avr.cc (avr_out_minus): Assimilate into...
>>      (avr_out_plus_ext): ...this new function.
>>      (avr_adjust_insn_length) [ADJUST_LEN_PLUS_EXT]: Handle case.
>>      (avr_rtx_costs_1) [PLUS, MINUS]: Adjust RTX costs.
>>      * config/avr/avr.md (adjust_len) <plus_ext>: Add new attribute 
>> value.
>>      (PSISI): New mode iterator.
>>      (*addpsi3_zero_extend.hi_split): Assimilate...
>>      (*addpsi3_zero_extend.qi_split): Assimilate...
>>      (*addsi3_zero_extend_split): Assimilate...
>>      (*addsi3_zero_extend.hi_split): Assimilate...
>>      (*add<PSISI:mode>3.zero_extend.<QIPSI:mode>_split): ...into this
>>      new insn-and-split.
>>      (*addpsi3_zero_extend.hi): Assimilate...
>>      (*addpsi3_zero_extend.qi): Assimilate...
>>      (*addsi3_zero_extend): Assimilate...
>>      (*addsi3_zero_extend.hi): Assimilate...
>>      (*add<PSISI:mode>3.zero_extend.<QIPSI:mode>): ...into this new insn.
>>      (*addpsi3_sign_extend.hi_split): Assimilate...
>>      (*addhi3.sign_extend1_split): Assimilate...
>>      (*add<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
>>      insn-and-split.
>>      (*addpsi3_sign_extend.hi): Assimilate...
>>      (*addhi3.sign_extend1): Assimilate...
>>      (*add<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
>>      (*subpsi3_sign_extend.hi_split): Assimilate...
>>      (*subhi3.sign_extend2_split): Assimilate...
>>      (*sub<HISI:mode>3.sign_extend.<QIPSI:mode>_split): ...into this new
>>      insn-and-split.
>>      (*subpsi3_sign_extend.hi): Assimilate...
>>      (*subhi3.sign_extend2): Assimilate...
>>      (*sub<HISI:mode>3.sign_extend.<QIPSI:mode>): ...into this new insn.
>>      (*sub<HISI:mode>3.zero_extend.<QIPSI:mode>): Use avr_out_plus_ext
>>      for asm out.
>>      * config/avr/avr-protos.h (avr_out_minus): Remove.
>>      (avr_out_plus_ext): New proto.
>> gcc/testsuite/
>>      * gcc.target/avr/torture/add-extend.c: New test.
>>      * gcc.target/avr/torture/sub-extend.c: New test.
> OK.  A bit hard to follow due to the prior ordering of patterns to make 
> sure nothing got lost, but I think it all got covered.
> 
> Something to consider, I've found having an any_extend code iterator 
> useful for this kind of pattern consolidation when all that differs is 
> sign vs zero extend in the pattern.
> 
> jeff

Ok, applied with any_extend which already exists:

https://gcc.gnu.org/r15-2068

This shrank the patterns by yet another 50%.

Johann
diff mbox series

Patch

diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 6e02161759c..568c33a7bbd 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -95,7 +95,7 @@  extern void avr_output_addr_vec (rtx_insn*, rtx);
 extern const char *avr_out_sbxx_branch (rtx_insn *insn, rtx operands[]);
 extern const char* avr_out_bitop (rtx, rtx*, int*);
 extern const char* avr_out_plus (rtx, rtx*, int* =NULL, bool =true);
-extern const char* avr_out_minus (rtx*);
+extern const char* avr_out_plus_ext (rtx_insn*, rtx*, int*);
 extern const char* avr_out_round (rtx_insn *, rtx*, int* =NULL);
 extern const char* avr_out_addto_sp (rtx*, int*);
 extern const char* avr_out_xload (rtx_insn *, rtx*, int*);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 4a7cbd0e7bc..3478c9d461a 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -8843,30 +8843,90 @@  lshrsi3_out (rtx_insn *insn, rtx operands[], int *len)
 }
 
 
-/* Output subtraction of integer registers XOP[0] and XOP[2] and return ""
+/* Output addition of registers YOP[0] and YOP[1]
 
-      XOP[0] = XOP[0] - XOP[2]
+      YOP[0] += extend (YOP[1])
 
-   where the mode of XOP[0] is in { HI, PSI, SI }, and the mode of
-   XOP[2] is in { QI, HI, PSI }.  When the mode of XOP[0] is larger
-   than the mode of XOP[2], then the latter is zero-extended on the fly.
-   The number of instructions will be the mode size of XOP[0].  */
+   or subtraction of registers YOP[0] and YOP[2]
+
+      YOP[0] -= extend (YOP[2])
+
+   where the integer modes satisfy  SI >= YOP[0].mode > YOP[1/2].mode >= QI,
+   and the extension may be sign- or zero-extend.  Returns "".
+
+   If PLEN == NULL output the instructions.
+   If PLEN != NULL set *PLEN to the length of the sequence in words.  */
 
 const char *
-avr_out_minus (rtx *xop)
+avr_out_plus_ext (rtx_insn *insn, rtx *yop, int *plen)
 {
-  int n_bytes0 = GET_MODE_SIZE (GET_MODE (xop[0]));
-  int n_bytes2 = GET_MODE_SIZE (GET_MODE (xop[2]));
+  rtx regs[2];
+
+  const rtx src = SET_SRC (single_set (insn));
+  const RTX_CODE add = GET_CODE (src);
+  gcc_assert (GET_CODE (src) == PLUS || GET_CODE (src) == MINUS);
+
+  // Use XOP[] in the remainder with XOP[0] = YOP[0] and XOP[1] = YOP[1/2].
+  rtx xop[2] = { yop[0], yop[add == PLUS ? 1 : 2] };
+  const rtx xreg = XEXP (src, add == PLUS ? 1 : 0);
+  const rtx xext = XEXP (src, add == PLUS ? 0 : 1);
+  const RTX_CODE ext = GET_CODE (xext);
+
+  gcc_assert (REG_P (xreg)
+	      && (ext == ZERO_EXTEND || ext == SIGN_EXTEND));
+
+  const int n_bytes0 = GET_MODE_SIZE (GET_MODE (xop[0]));
+  const int n_bytes1 = GET_MODE_SIZE (GET_MODE (xop[1]));
+  rtx msb1 = all_regs_rtx[n_bytes1 - 1 + REGNO (xop[1])];
+
+  const char *const s_ADD = add == PLUS ? "add %0,%1" : "sub %0,%1";
+  const char *const s_ADC = add == PLUS ? "adc %0,%1" : "sbc %0,%1";
+  const char *const s_DEC = add == PLUS
+    ? "adc %0,__zero_reg__"  CR_TAB  "sbrc %1,7"  CR_TAB  "dec %0"
+    : "sbc %0,__zero_reg__"  CR_TAB  "sbrc %1,7"  CR_TAB  "inc %0";
+
+  // A register that containts 8 copies of $1.msb.
+  rtx ext_reg = ext == ZERO_EXTEND ? zero_reg_rtx : NULL_RTX;
+
+  if (plen)
+    *plen = 0;
+
+  if (ext == SIGN_EXTEND
+      && (n_bytes0 > 1 + n_bytes1
+	  || reg_overlap_mentioned_p (msb1, xop[0])))
+    {
+      // Sign-extending more than one byte: Set tmp_reg to 0 or -1
+      // depending on $1.msb. Same for the pathological case where
+      // $0 and $1 overlap.
+
+      regs[0] = ext_reg = tmp_reg_rtx;
+      regs[1] = msb1;
+
+      avr_asm_len ("mov %0,%1"  CR_TAB
+		   "lsl %0"     CR_TAB
+		   "sbc %0,%0", regs, plen, 3);
+    }
 
-  output_asm_insn ("sub %0,%2", xop);
+  // Adding the bytes of $1 is just plain additions / subtractions.
+  // Same for the extended bytes when we have ext_reg.
+
+  avr_asm_len (s_ADD, xop, plen, 1);
 
   for (int i = 1; i < n_bytes0; ++i)
     {
-      rtx op[2];
-      op[0] = all_regs_rtx[i + REGNO (xop[0])];
-      op[1] = (i < n_bytes2) ? all_regs_rtx[i + REGNO (xop[2])] : zero_reg_rtx;
+      regs[0] = all_regs_rtx[i + REGNO (xop[0])];
+      regs[1] = i < n_bytes1 ? all_regs_rtx[i + REGNO (xop[1])] : ext_reg;
+
+      if (! regs[1])
+	{
+	  // Extending just 1 byte:  This is one instruction shorter
+	  // than sign-extending $1.msb to tmp_reg.
 
-      output_asm_insn ("sbc %0,%1", op);
+	  regs[1] = msb1;
+	  avr_asm_len (s_DEC, regs, plen, 3);
+	}
+      else
+	avr_asm_len (s_ADC, regs, plen, 1);
     }
 
   return "";
@@ -11010,6 +11070,7 @@  avr_adjust_insn_length (rtx_insn *insn, int len)
     case ADJUST_LEN_INSV: avr_out_insv (insn, op, &len); break;
 
     case ADJUST_LEN_PLUS: avr_out_plus (insn, op, &len); break;
+    case ADJUST_LEN_PLUS_EXT: avr_out_plus_ext (insn, op, &len); break;
     case ADJUST_LEN_ADDTO_SP: avr_out_addto_sp (op, &len); break;
 
     case ADJUST_LEN_MOV8:  output_movqi (insn, op, &len); break;
@@ -12647,6 +12708,8 @@  avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code,
 	  return true;
 	}
 
+      // *add<PSISI:mode>3.zero_extend.<QIPSI:mode>
+      // *addhi3_zero_extend
       if (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
 	  && REG_P (XEXP (x, 1)))
 	{
@@ -12660,6 +12723,16 @@  avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code,
 	  return true;
 	}
 
+      // *add<HISI:mode>3.sign_extend.<QIPSI:mode>
+      if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+	  && REG_P (XEXP (x, 1)))
+	{
+	  int size2 = GET_MODE_SIZE (GET_MODE (XEXP (XEXP (x, 0), 0)));
+	  *total = COSTS_N_INSNS (2 + GET_MODE_SIZE (mode)
+				  + (GET_MODE_SIZE (mode) > 1 + size2));
+	  return true;
+	}
+
       switch (mode)
 	{
 	case E_QImode:
@@ -12754,11 +12827,13 @@  avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code,
 	  *total = COSTS_N_INSNS (GET_MODE_SIZE (mode));
 	  return true;
 	}
-      // *sub<mode>3.sign_extend2
+      // *sub<HISI:mode>3.sign_extend.<QIPSI:mode>
       if (REG_P (XEXP (x, 0))
 	  && GET_CODE (XEXP (x, 1)) == SIGN_EXTEND)
 	{
-	  *total = COSTS_N_INSNS (2 + GET_MODE_SIZE (mode));
+	  int size2 = GET_MODE_SIZE (GET_MODE (XEXP (XEXP (x, 1), 0)));
+	  *total = COSTS_N_INSNS (2 + GET_MODE_SIZE (mode)
+				  + (GET_MODE_SIZE (mode) > 1 + size2));
 	  return true;
 	}
 
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 8c3e55a91ee..fc2eb061170 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -161,7 +161,7 @@  (define_attr "length" ""
 ;; Otherwise do special processing depending on the attribute.
 
 (define_attr "adjust_len"
-  "out_bitop, plus, addto_sp, sext, extr, extr_not,
+  "out_bitop, plus, addto_sp, sext, extr, extr_not, plus_ext,
    tsthi, tstpsi, tstsi, compare, compare64, call,
    mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
    ufract, sfract, round,
@@ -261,6 +261,7 @@  (define_mode_iterator QISI  [QI HI PSI SI])
 (define_mode_iterator QIDI  [QI HI PSI SI DI])
 (define_mode_iterator QIPSI [QI HI PSI])
 (define_mode_iterator HISI  [HI PSI SI])
+(define_mode_iterator PSISI [PSI SI])
 
 ;; Ordered integral and fixed-point modes of specific sizes.
 (define_mode_iterator ALL1 [QI QQ UQQ])
@@ -1595,33 +1596,6 @@  (define_insn "*addhi3_zero_extend1"
   "add %A0,%2\;adc %B0,__zero_reg__"
   [(set_attr "length" "2")])
 
-(define_insn_and_split "*addhi3.sign_extend1_split"
-  [(set (match_operand:HI 0 "register_operand"                         "=r")
-        (plus:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "r"))
-                 (match_operand:HI 2 "register_operand"                 "0")))]
-  ""
-  "#"
-  "&& reload_completed"
-  [(parallel [(set (match_dup 0)
-                   (plus:HI (sign_extend:HI (match_dup 1))
-                            (match_dup 2)))
-              (clobber (reg:CC REG_CC))])])
-
-
-(define_insn "*addhi3.sign_extend1"
-  [(set (match_operand:HI 0 "register_operand"                         "=r")
-        (plus:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "r"))
-                 (match_operand:HI 2 "register_operand"                 "0")))
-   (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  {
-    return reg_overlap_mentioned_p (operands[0], operands[1])
-      ? "mov __tmp_reg__,%1\;add %A0,%1\;adc %B0,__zero_reg__\;sbrc __tmp_reg__,7\;dec %B0"
-      : "add %A0,%1\;adc %B0,__zero_reg__\;sbrc %1,7\;dec %B0";
-  }
-  [(set (attr "length")
-        (symbol_ref ("4 + reg_overlap_mentioned_p (operands[0], operands[1])")))])
-
 (define_insn_and_split "*addhi3_zero_extend.const_split"
   [(set (match_operand:HI 0 "register_operand"                         "=d")
         (plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "0"))
@@ -1877,110 +1851,108 @@  (define_insn "*add<mode>3"
   [(set_attr "length" "4")
    (set_attr "adjust_len" "plus")])
 
-(define_insn_and_split "*addpsi3_zero_extend.qi_split"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (zero_extend:PSI (match_operand:QI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))]
-  ""
-  "#"
-  "&& reload_completed"
-  [(parallel [(set (match_dup 0)
-                   (plus:PSI (zero_extend:PSI (match_dup 1))
-                             (match_dup 2)))
-              (clobber (reg:CC REG_CC))])])
-
-(define_insn "*addpsi3_zero_extend.qi"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (zero_extend:PSI (match_operand:QI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))
-   (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "add %A0,%A1\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__"
-  [(set_attr "length" "3")])
 
-(define_insn_and_split "*addpsi3_zero_extend.hi_split"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (zero_extend:PSI (match_operand:HI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))]
-  ""
+;; "*addpsi3.zero_extend.qi_split"  "*addpsi3.zero_extend.hi_split"
+;; "*addsi3.zero_extend.qi_split"   "*addsi3.zero_extend.hi_split"
+;; "*addsi3.zero_extend.psi_split"
+;; The HI case is treated in an own insn as it can more than just "r,r,0".
+(define_insn_and_split "*add<PSISI:mode>3.zero_extend.<QIPSI:mode>_split"
+  [(set (match_operand:PSISI 0 "register_operand"                               "=r")
+        (plus:PSISI (zero_extend:PSISI (match_operand:QIPSI 1 "register_operand" "r"))
+                    (match_operand:PSISI 2 "register_operand"                    "0")))]
+  "GET_MODE_SIZE (<PSISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
   "#"
   "&& reload_completed"
   [(parallel [(set (match_dup 0)
-                   (plus:PSI (zero_extend:PSI (match_dup 1))
-                             (match_dup 2)))
+                   (plus:PSISI (zero_extend:PSISI (match_dup 1))
+                               (match_dup 2)))
               (clobber (reg:CC REG_CC))])])
 
-(define_insn "*addpsi3_zero_extend.hi"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (zero_extend:PSI (match_operand:HI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))
+;; "*addpsi3.zero_extend.qi"  "*addpsi3.zero_extend.hi"
+;; "*addsi3.zero_extend.qi"   "*addsi3.zero_extend.hi"
+;; "*addsi3.zero_extend.psi"
+(define_insn "*add<PSISI:mode>3.zero_extend.<QIPSI:mode>"
+  [(set (match_operand:PSISI 0 "register_operand"                               "=r")
+        (plus:PSISI (zero_extend:PSISI (match_operand:QIPSI 1 "register_operand" "r"))
+                    (match_operand:PSISI 2 "register_operand"                    "0")))
    (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "add %A0,%A1\;adc %B0,%B1\;adc %C0,__zero_reg__"
-  [(set_attr "length" "3")])
-
-(define_insn_and_split "*addpsi3_sign_extend.hi_split"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (sign_extend:PSI (match_operand:HI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))]
-  ""
-  "#"
-  "&& reload_completed"
-  [(parallel [(set (match_dup 0)
-                   (plus:PSI (sign_extend:PSI (match_dup 1))
-                             (match_dup 2)))
-              (clobber (reg:CC REG_CC))])])
+  "reload_completed
+   && GET_MODE_SIZE (<PSISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
+  {
+    return avr_out_plus_ext (insn, operands, nullptr);
+  }
+  [(set_attr "length" "<PSISI:SIZE>")])
 
-(define_insn "*addpsi3_sign_extend.hi"
-  [(set (match_operand:PSI 0 "register_operand"                          "=r")
-        (plus:PSI (sign_extend:PSI (match_operand:HI 1 "register_operand" "r"))
-                  (match_operand:PSI 2 "register_operand"                 "0")))
-   (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "add %A0,%1\;adc %B0,%B1\;adc %C0,__zero_reg__\;sbrc %B1,7\;dec %C0"
-  [(set_attr "length" "5")])
 
-(define_insn_and_split "*addsi3_zero_extend_split"
-  [(set (match_operand:SI 0 "register_operand"                         "=r")
-        (plus:SI (zero_extend:SI (match_operand:QI 1 "register_operand" "r"))
-                 (match_operand:SI 2 "register_operand"                 "0")))]
-  ""
+;; "*addhi3.sign_extend.qi_split"
+;; "*addpsi3.sign_extend.qi_split"  "*addpsi3.sign_extend.hi_split"
+;; "*addsi3.sign_extend.qi_split"   "*addsi3.sign_extend.hi_split"
+;; "*addsi3.sign_extend.psi_split"
+(define_insn_and_split "*add<HISI:mode>3.sign_extend.<QIPSI:mode>_split"
+  [(set (match_operand:HISI 0 "register_operand"                              "=r")
+        (plus:HISI (sign_extend:HISI (match_operand:QIPSI 1 "register_operand" "r"))
+                   (match_operand:HISI 2 "register_operand"                    "0")))]
+  "GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
   "#"
   "&& reload_completed"
   [(parallel [(set (match_dup 0)
-                   (plus:SI (zero_extend:SI (match_dup 1))
-                            (match_dup 2)))
+                   (plus:HISI (sign_extend:HISI (match_dup 1))
+                              (match_dup 2)))
               (clobber (reg:CC REG_CC))])])
 
-(define_insn "*addsi3_zero_extend"
-  [(set (match_operand:SI 0 "register_operand"                         "=r")
-        (plus:SI (zero_extend:SI (match_operand:QI 1 "register_operand" "r"))
-                 (match_operand:SI 2 "register_operand"                 "0")))
+;; "*addhi3.sign_extend.qi"
+;; "*addpsi3.sign_extend.qi"  "*addpsi3.sign_extend.hi"
+;; "*addsi3.sign_extend.qi"   "*addsi3.sign_extend.hi"
+;; "*addsi3.sign_extend.psi"
+(define_insn "*add<HISI:mode>3.sign_extend.<QIPSI:mode>"
+  [(set (match_operand:HISI 0 "register_operand"                              "=r")
+        (plus:HISI (sign_extend:HISI (match_operand:QIPSI 1 "register_operand" "r"))
+                   (match_operand:HISI 2 "register_operand"                    "0")))
    (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "add %A0,%1\;adc %B0,__zero_reg__\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__"
-  [(set_attr "length" "4")])
-
-(define_insn_and_split "*addsi3_zero_extend.hi_split"
-  [(set (match_operand:SI 0 "register_operand"                         "=r")
-        (plus:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "r"))
-                 (match_operand:SI 2 "register_operand"                 "0")))]
-  ""
+  "reload_completed
+   && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
+  {
+    return avr_out_plus_ext (insn, operands, nullptr);
+  }
+  [(set (attr "length")
+        (symbol_ref "3 + <HISI:SIZE>"))
+   (set_attr "adjust_len" "plus_ext")])
+
+
+;; "*subhi3.sign_extend.qi_split"
+;; "*subpsi3.sign_extend.qi_split"  "*subpsi3.sign_extend.hi_split"
+;; "*subsi3.sign_extend.qi_split"   "*subsi3.sign_extend.hi_split"
+;; "*subsi3.sign_extend.psi_split"
+(define_insn_and_split "*sub<HISI:mode>3.sign_extend.<QIPSI:mode>_split"
+  [(set (match_operand:HISI 0 "register_operand"                               "=r")
+        (minus:HISI (match_operand:HISI 1 "register_operand"                    "0")
+                    (sign_extend:HISI (match_operand:QIPSI 2 "register_operand" "r"))))]
+  "GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
   "#"
   "&& reload_completed"
   [(parallel [(set (match_dup 0)
-                   (plus:SI (zero_extend:SI (match_dup 1))
-                            (match_dup 2)))
+                   (minus:HISI (match_dup 1)
+                               (sign_extend:HISI (match_dup 2))))
               (clobber (reg:CC REG_CC))])])
 
-(define_insn "*addsi3_zero_extend.hi"
-  [(set (match_operand:SI 0 "register_operand"                         "=r")
-        (plus:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "r"))
-                 (match_operand:SI 2 "register_operand"                 "0")))
+;; "*subhi3.sign_extend.qi"
+;; "*subpsi3.sign_extend.qi"  "*subpsi3.sign_extend.hi"
+;; "*subsi3.sign_extend.qi"   "*subsi3.sign_extend.hi"
+;; "*subsi3.sign_extend.psi"
+(define_insn "*sub<HISI:mode>3.sign_extend.<QIPSI:mode>"
+  [(set (match_operand:HISI 0 "register_operand"                               "=r")
+        (minus:HISI (match_operand:HISI 1 "register_operand"                    "0")
+                    (sign_extend:HISI (match_operand:QIPSI 2 "register_operand" "r"))))
    (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "add %A0,%1\;adc %B0,%B1\;adc %C0,__zero_reg__\;adc %D0,__zero_reg__"
-  [(set_attr "length" "4")])
+  "reload_completed
+   && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
+  {
+    return avr_out_plus_ext (insn, operands, nullptr);
+  }
+  [(set (attr "length")
+        (symbol_ref "3 + <HISI:SIZE>"))
+   (set_attr "adjust_len" "plus_ext")])
+
 
 (define_insn_and_split "addpsi3"
   [(set (match_operand:PSI 0 "register_operand"         "=??r,d ,d,r")
@@ -2031,27 +2003,6 @@  (define_insn "*subpsi3"
   [(set_attr "length" "3")])
 
 
-(define_insn_and_split "*subpsi3_sign_extend.hi_split"
-  [(set (match_operand:PSI 0 "register_operand"                           "=r")
-        (minus:PSI (match_operand:PSI 1 "register_operand"                 "0")
-                   (sign_extend:PSI (match_operand:HI 2 "register_operand" "r"))))]
-  ""
-  "#"
-  "&& reload_completed"
-  [(parallel [(set (match_dup 0)
-                   (minus:PSI (match_dup 1)
-                              (sign_extend:PSI (match_dup 2))))
-              (clobber (reg:CC REG_CC))])])
-
-(define_insn "*subpsi3_sign_extend.hi"
-  [(set (match_operand:PSI 0 "register_operand"                           "=r")
-        (minus:PSI (match_operand:PSI 1 "register_operand"                 "0")
-                   (sign_extend:PSI (match_operand:HI 2 "register_operand" "r"))))
-   (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  "sub %A0,%A2\;sbc %B0,%B2\;sbc %C0,__zero_reg__\;sbrc %B2,7\;inc %C0"
-  [(set_attr "length" "5")])
-
 ;-----------------------------------------------------------------------------
 ; sub bytes
 
@@ -2114,33 +2065,6 @@  (define_insn "*sub<mode>3"
   [(set_attr "adjust_len" "plus")])
 
 
-(define_insn_and_split "*subhi3.sign_extend2_split"
-  [(set (match_operand:HI 0 "register_operand"                          "=r")
-        (minus:HI (match_operand:HI 1 "register_operand"                 "0")
-                  (sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
-  ""
-  "#"
-  "&& reload_completed"
-  [(parallel [(set (match_dup 0)
-                   (minus:HI (match_dup 1)
-                             (sign_extend:HI (match_dup 2))))
-              (clobber (reg:CC REG_CC))])])
-
-
-(define_insn "*subhi3.sign_extend2"
-  [(set (match_operand:HI 0 "register_operand"                          "=r")
-        (minus:HI (match_operand:HI 1 "register_operand"                 "0")
-                  (sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))
-   (clobber (reg:CC REG_CC))]
-  "reload_completed"
-  {
-    return reg_overlap_mentioned_p (operands[0], operands[2])
-      ? "mov __tmp_reg__,%2\;sub %A0,%2\;sbc %B0,__zero_reg__\;sbrc __tmp_reg__,7\;inc %B0"
-      : "sub %A0,%2\;sbc %B0,__zero_reg__\;sbrc %2,7\;inc %B0";
-  }
-  [(set (attr "length")
-        (symbol_ref ("4 + reg_overlap_mentioned_p (operands[0], operands[2])")))])
-
 ;; "subsi3"
 ;; "subsq3" "subusq3"
 ;; "subsa3" "subusa3"
@@ -2199,7 +2123,7 @@  (define_insn "*sub<HISI:mode>3.zero_extend.<QIPSI:mode>"
   "reload_completed
    && GET_MODE_SIZE (<HISI:MODE>mode) > GET_MODE_SIZE (<QIPSI:MODE>mode)"
   {
-    return avr_out_minus (operands);
+    return avr_out_plus_ext (insn, operands, nullptr);
   }
   [(set_attr "length" "<HISI:SIZE>")])
 
diff --git a/gcc/testsuite/gcc.target/avr/torture/add-extend.c b/gcc/testsuite/gcc.target/avr/torture/add-extend.c
new file mode 100644
index 00000000000..42445e837e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/add-extend.c
@@ -0,0 +1,113 @@ 
+/* { dg-do run } */
+
+typedef __UINT8_TYPE__ u8;
+typedef __UINT16_TYPE__ u16;
+typedef __uint24 u24;
+typedef __UINT32_TYPE__ u32;
+
+typedef __INT8_TYPE__ s8;
+typedef __INT16_TYPE__ s16;
+typedef __int24 s24;
+typedef __INT32_TYPE__ s32;
+
+#define NI __attribute__((noinline,noclone,no_icf))
+
+NI u32 addu_32_8  (u32 a, u8 b)  { return a + b; }
+NI u32 addu_32_16 (u32 a, u16 b) { return a + b; }
+NI u32 addu_32_24 (u32 a, u24 b) { return a + b; }
+
+NI u24 addu_24_8  (u24 a, u8 b)  { return a + b; }
+NI u24 addu_24_16 (u24 a, u16 b) { return a + b; }
+
+NI u16 addu_16_8  (u16 a, u8 b)  { return a + b; }
+
+/************************/
+
+NI s32 adds_32_8  (s32 a, s8 b)  { return a + b; }
+NI s32 adds_32_16 (s32 a, s16 b) { return a + b; }
+NI s32 adds_32_24 (s32 a, s24 b) { return a + b; }
+
+NI s24 adds_24_8  (s24 a, s8 b)  { return a + b; }
+NI s24 adds_24_16 (s24 a, s16 b) { return a + b; }
+
+NI s16 adds_16_8  (s16 a, s8 b)  { return a + b; }
+
+/************************/
+
+NI u32 addu_32 (u32 a, u32 b)    { return a + b; }
+NI u24 addu_24 (u24 a, u24 b)    { return a + b; }
+NI u16 addu_16 (u16 a, u16 b)    { return a + b; }
+
+NI s32 adds_32 (s32 a, s32 b)    { return a + b; }
+NI s24 adds_24 (s24 a, s24 b)    { return a + b; }
+NI s16 adds_16 (s16 a, s16 b)    { return a + b; }
+
+/************************/
+
+NI u8 next (void *p0, u8 n_bytes)
+{
+  u8 *p = (u8*) p0;
+
+  u8 n;
+  for (n = 0; n < n_bytes; ++n)
+    {
+      u8 val = *p;
+
+      switch (val)
+	{
+	case 0x00: val = 0x01; break;
+	case 0x01: val = 0xfe; break;
+	case 0xfe: val = 0xff; break;
+	case 0xff: val = 0x00; break;
+	default: ++val; break;
+	}
+      *p++ = val;
+      if (val)
+	return 1;
+    }
+
+  return 0;
+}
+
+#define MK_TEST(A, B)							\
+NI void test_##A##_##B (void)						\
+{									\
+  u##A a = 0;								\
+  do									\
+    {									\
+      u##B b = 0;							\
+      do								\
+	{								\
+	  if (addu_##A##_##B (a, b) != addu_##A (a, b))			\
+	    __builtin_exit (11);					\
+	  								\
+	  if (adds_##A##_##B (a, b) != adds_##A ((s##A) a, (s##B) b))	\
+	    __builtin_exit (13);					\
+	  								\
+	} while (next (&b, sizeof (b)));				\
+    } while (next (&a, sizeof (a)));					\
+}
+
+MK_TEST (16, 8)
+
+MK_TEST (24, 8)
+MK_TEST (24, 16)
+
+MK_TEST (32, 8)
+MK_TEST (32, 16)
+MK_TEST (32, 24)
+
+
+int main (void)
+{
+  test_16_8 ();
+
+  test_24_8 ();
+  test_24_16 ();
+
+  test_32_8 ();
+  test_32_16 ();
+  test_32_24 ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sub-extend.c b/gcc/testsuite/gcc.target/avr/torture/sub-extend.c
new file mode 100644
index 00000000000..91b65c07ccf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sub-extend.c
@@ -0,0 +1,113 @@ 
+/* { dg-do run } */
+
+typedef __UINT8_TYPE__ u8;
+typedef __UINT16_TYPE__ u16;
+typedef __uint24 u24;
+typedef __UINT32_TYPE__ u32;
+
+typedef __INT8_TYPE__ s8;
+typedef __INT16_TYPE__ s16;
+typedef __int24 s24;
+typedef __INT32_TYPE__ s32;
+
+#define NI __attribute__((noinline,noclone,no_icf))
+
+NI u32 subu_32_8  (u32 a, u8 b)  { return a - b; }
+NI u32 subu_32_16 (u32 a, u16 b) { return a - b; }
+NI u32 subu_32_24 (u32 a, u24 b) { return a - b; }
+
+NI u24 subu_24_8  (u24 a, u8 b)  { return a - b; }
+NI u24 subu_24_16 (u24 a, u16 b) { return a - b; }
+
+NI u16 subu_16_8  (u16 a, u8 b)  { return a - b; }
+
+/************************/
+
+NI s32 subs_32_8  (s32 a, s8 b)  { return a - b; }
+NI s32 subs_32_16 (s32 a, s16 b) { return a - b; }
+NI s32 subs_32_24 (s32 a, s24 b) { return a - b; }
+
+NI s24 subs_24_8  (s24 a, s8 b)  { return a - b; }
+NI s24 subs_24_16 (s24 a, s16 b) { return a - b; }
+
+NI s16 subs_16_8  (s16 a, s8 b)  { return a - b; }
+
+/************************/
+
+NI u32 subu_32 (u32 a, u32 b)    { return a - b; }
+NI u24 subu_24 (u24 a, u24 b)    { return a - b; }
+NI u16 subu_16 (u16 a, u16 b)    { return a - b; }
+
+NI s32 subs_32 (s32 a, s32 b)    { return a - b; }
+NI s24 subs_24 (s24 a, s24 b)    { return a - b; }
+NI s16 subs_16 (s16 a, s16 b)    { return a - b; }
+
+/************************/
+
+NI u8 next (void *p0, u8 n_bytes)
+{
+  u8 *p = (u8*) p0;
+
+  u8 n;
+  for (n = 0; n < n_bytes; ++n)
+    {
+      u8 val = *p;
+
+      switch (val)
+	{
+	case 0x00: val = 0x01; break;
+	case 0x01: val = 0xfe; break;
+	case 0xfe: val = 0xff; break;
+	case 0xff: val = 0x00; break;
+	default: ++val; break;
+	}
+      *p++ = val;
+      if (val)
+	return 1;
+    }
+
+  return 0;
+}
+
+#define MK_TEST(A, B)							\
+NI void test_##A##_##B (void)						\
+{									\
+  u##A a = 0;								\
+  do									\
+    {									\
+      u##B b = 0;							\
+      do								\
+	{								\
+	  if (subu_##A##_##B (a, b) != subu_##A (a, b))			\
+	    __builtin_exit (11);					\
+	  								\
+	  if (subs_##A##_##B (a, b) != subs_##A ((s##A) a, (s##B) b))	\
+	    __builtin_exit (13);					\
+	  								\
+	} while (next (&b, sizeof (b)));				\
+    } while (next (&a, sizeof (a)));					\
+}
+
+MK_TEST (16, 8)
+
+MK_TEST (24, 8)
+MK_TEST (24, 16)
+
+MK_TEST (32, 8)
+MK_TEST (32, 16)
+MK_TEST (32, 24)
+
+
+int main (void)
+{
+  test_16_8 ();
+
+  test_24_8 ();
+  test_24_16 ();
+
+  test_32_8 ();
+  test_32_16 ();
+  test_32_24 ();
+
+  return 0;
+}