OSDN Git Service

gcc/
authorgjl <gjl@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 15 Sep 2012 15:52:28 +0000 (15:52 +0000)
committergjl <gjl@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 15 Sep 2012 15:52:28 +0000 (15:52 +0000)
PR target/54222
* config/avr/avr-fixed.md (ALL2S, ALL4S, ALL24S, ALL124S,
ALL124U): New mode iterators.
(<code_stdname><mode>3): New insns for SS_PLUS, SS_MINUS.
(<code_stdname><mode>3): New insns for US_PLUS, US_MINUS.
(usneg<mode>2): New insns.
(<code_stdname><mode>2): New expanders for SS_NEG, SS_ABS.
(*<code_stdname><mode>2): New insns for SS_NEG, SS_ABS.
* config/avr/avr-dimode.md (ALL8U, ALL8S): New mode iterators.
(avr_out_plus64, avr_out_minus64): Use avr_out_plus instead.
(<code_stdname><mode>3): New expanders for SS_PLUS, SS_MINUS.
(<code_stdname><mode>3): New expanders for US_PLUS, US_MINUS.
(<code_stdname><mode>3_insn): New insns.
(<code_stdname><mode>3_const_insn): New insns.
* config/avr/avr.md (cc): Add: plus. Remove: out_plus,
out_plus_noclobber, minus.
(length): Add: plus.  Remove: out_plus, out_plus_noclobber,
plus64, minus, minus64.
(abelian): New code_attr.
(code_stdname): Handle: ss_plus, ss_minus, ss_neg, ss_abs,
us_plus, us_minus, us_neg.
(*add<mode>3, add<mode>3_clobber, add<mode>3, addpsi3, sub<mode>3):
Use avr_out_plus to output.
* config/avr/avr-protos.h (avr_out_plus): Change prototype.
(avr_out_plus_noclobber, avr_out_minus): Remove.
(avr_out_plus64, avr_out_minus64): Remove.
* config/avr/avr.c (avr_out_plus_1): Add new default arguments
code_sat, sign.  Saturate after operation if code_sat != UNKNOWN.
(avr_out_plus_symbol): New static function.
(avr_out_plus): Rewrite.
(adjust_insn_length): Handle: ADJUST_LEN_PLUS.  Remove handling
of: ADJUST_LEN_OUT_PLUS, ADJUST_LEN_PLUS64, ADJUST_LEN_MINUS,
ADJUST_LEN_MINUS64, ADJUST_LEN_OUT_PLUS_NOCLOBBER.
(notice_update_cc): Handle: CC_PLUS.  Remove handling of: CC_MINUS,
CC_OUT_PLUS, CC_OUT_PLUS_NOCLOBBER
(avr_out_plus_noclobber, avr_out_minus): Remove.
(avr_out_plus64, avr_out_minus64): Remove.
(avr_print_operand): Print raw REGNO if 'r' is used with REG.

libgcc/
PR target/54222
* config/avr/lib1funcs-fixed.S (__ssneg_2, __ssabs_2, __ssneg_4,
__ssabs_4, __clr_8, __ssneg_8, __ssabs_8,
__usadd_8, __ussub_8, __ssadd_8, __sssub_8): New functions.
(__divsa3): Use __negsi2 to negate r_quoL.
* config/avr/lib1funcs.S (FALIAS): New macro.
(__divmodsi4): Break out and use __divmodsi4_neg1 as...
(__negsi2): ...this new function.
* config/avr/t-avr (LIB1ASMFUNCS): Add _negsi2, _clr_8,
_ssneg_2, _ssneg_4, _ssneg_8, _ssabs_2, _ssabs_4,
_ssabs_8, _ssadd_8, _sssub_8, _usadd_8, _ussub_8.
(LIB2FUNCS_EXCLUDE): Fix typo for _add _sub.
Add: _ssadd*, _sssub*, _ssneg*, _ssabs* for signed fixed modes.
Add: _usadd*, _ussub*, _usneg* for unsigned fixed modes.

gcc/testsuite/
PR target/54222
* gcc.target/avr/torture/fix-types.h: New.
* gcc.target/avr/torture/vals-hr.def: New.
* gcc.target/avr/torture/vals-r.def: New.
* gcc.target/avr/torture/vals-k.def: New.
* gcc.target/avr/torture/vals-ur.def: New.
* gcc.target/avr/torture/vals-uk.def: New.
* gcc.target/avr/torture/vals-uhr.def: New.
* gcc.target/avr/torture/vals-llk.def: New.
* gcc.target/avr/torture/vals-ullk.def: New.
* gcc.target/avr/torture/sat-hr-plus-minus.c: New.
* gcc.target/avr/torture/sat-r-plus-minus.c: New.
* gcc.target/avr/torture/sat-k-plus-minus.c: New.
* gcc.target/avr/torture/sat-ur-plus-minus.c: New.
* gcc.target/avr/torture/sat-uk-plus-minus.c: New.
* gcc.target/avr/torture/sat-uhr-plus-minus.c: New.
* gcc.target/avr/torture/sat-llk-plus-minus.c: New.
* gcc.target/avr/torture/sat-ullk-plus-minus.c: New.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@191345 138bc75d-0d04-0410-961f-82ee72b054a4

28 files changed:
gcc/ChangeLog
gcc/config/avr/avr-dimode.md
gcc/config/avr/avr-fixed.md
gcc/config/avr/avr-protos.h
gcc/config/avr/avr.c
gcc/config/avr/avr.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/avr/torture/fix-types.h [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-hr.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-k.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-llk.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-r.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-uhr.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-uk.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-ullk.def [new file with mode: 0644]
gcc/testsuite/gcc.target/avr/torture/vals-ur.def [new file with mode: 0644]
libgcc/ChangeLog
libgcc/config/avr/lib1funcs-fixed.S
libgcc/config/avr/lib1funcs.S
libgcc/config/avr/t-avr

index ae8bf34..a5e3018 100644 (file)
@@ -1,3 +1,44 @@
+2012-09-15  Georg-Johann Lay  <avr@gjlay.de>
+
+       PR target/54222
+       * config/avr/avr-fixed.md (ALL2S, ALL4S, ALL24S, ALL124S,
+       ALL124U): New mode iterators.
+       (<code_stdname><mode>3): New insns for SS_PLUS, SS_MINUS.
+       (<code_stdname><mode>3): New insns for US_PLUS, US_MINUS.
+       (usneg<mode>2): New insns.
+       (<code_stdname><mode>2): New expanders for SS_NEG, SS_ABS.
+       (*<code_stdname><mode>2): New insns for SS_NEG, SS_ABS.
+       * config/avr/avr-dimode.md (ALL8U, ALL8S): New mode iterators.
+       (avr_out_plus64, avr_out_minus64): Use avr_out_plus instead.
+       (<code_stdname><mode>3): New expanders for SS_PLUS, SS_MINUS.
+       (<code_stdname><mode>3): New expanders for US_PLUS, US_MINUS.
+       (<code_stdname><mode>3_insn): New insns.
+       (<code_stdname><mode>3_const_insn): New insns.
+       * config/avr/avr.md (cc): Add: plus. Remove: out_plus,
+       out_plus_noclobber, minus.
+       (length): Add: plus.  Remove: out_plus, out_plus_noclobber,
+       plus64, minus, minus64.
+       (abelian): New code_attr.
+       (code_stdname): Handle: ss_plus, ss_minus, ss_neg, ss_abs,
+       us_plus, us_minus, us_neg.
+       (*add<mode>3, add<mode>3_clobber, add<mode>3, addpsi3, sub<mode>3):
+       Use avr_out_plus to output.
+       * config/avr/avr-protos.h (avr_out_plus): Change prototype.
+       (avr_out_plus_noclobber, avr_out_minus): Remove.
+       (avr_out_plus64, avr_out_minus64): Remove.
+       * config/avr/avr.c (avr_out_plus_1): Add new default arguments
+       code_sat, sign.  Saturate after operation if code_sat != UNKNOWN.
+       (avr_out_plus_symbol): New static function.
+       (avr_out_plus): Rewrite.
+       (adjust_insn_length): Handle: ADJUST_LEN_PLUS.  Remove handling
+       of: ADJUST_LEN_OUT_PLUS, ADJUST_LEN_PLUS64, ADJUST_LEN_MINUS, 
+       ADJUST_LEN_MINUS64, ADJUST_LEN_OUT_PLUS_NOCLOBBER.
+       (notice_update_cc): Handle: CC_PLUS.  Remove handling of: CC_MINUS,
+       CC_OUT_PLUS, CC_OUT_PLUS_NOCLOBBER
+       (avr_out_plus_noclobber, avr_out_minus): Remove.
+       (avr_out_plus64, avr_out_minus64): Remove.
+       (avr_print_operand): Print raw REGNO if 'r' is used with REG.
+
 2012-09-15  Oleg Endo  <olegendo@gcc.gnu.org>
 
        * config/sh/sh.c (sh_rtx_costs): Add handling of MEM, SIGN_EXTEND,
index ed57523..31527db 100644 (file)
    (ACC_B      10)])
 
 ;; Supported modes that are 8 bytes wide
-(define_mode_iterator ALL8 [(DI "")
-                            (DQ "") (UDQ "")
-                            (DA "") (UDA "")
-                            (TA "") (UTA "")])
+(define_mode_iterator ALL8 [DI DQ UDQ DA UDA TA UTA])
+
+(define_mode_iterator ALL8U [UDQ UDA UTA])
+(define_mode_iterator ALL8S [ DQ  DA  TA])
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Addition
   "avr_have_dimode
    && !s8_operand (operands[0], VOIDmode)"
   {
-    return avr_out_plus64 (operands[0], NULL);
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "adjust_len" "plus64")
+  [(set_attr "adjust_len" "plus")
    (set_attr "cc" "clobber")])
 
 
                     (match_operand:ALL8 0 "const_operand" "n Ynn")))]
   "avr_have_dimode"
   {
-    return avr_out_minus64 (operands[0], NULL);
+    return avr_out_plus (insn, operands);
+  }
+  [(set_attr "adjust_len" "plus")
+   (set_attr "cc" "clobber")])
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Signed Saturating Addition and Subtraction
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define_expand "<code_stdname><mode>3"
+  [(set (match_operand:ALL8S 0 "general_operand" "")
+        (ss_addsub:ALL8S (match_operand:ALL8S 1 "general_operand" "")
+                         (match_operand:ALL8S 2 "general_operand" "")))]
+  "avr_have_dimode"
+  {
+    rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
+
+    emit_move_insn (acc_a, operands[1]);
+
+    if (const_operand (operands[2], GET_MODE (operands[2])))
+      {
+        emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2]));
+      }
+    else
+      {
+        emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
+        emit_insn (gen_<code_stdname><mode>3_insn ());
+      }
+
+    emit_move_insn (operands[0], acc_a);
+    DONE;
+  })
+
+(define_insn "<code_stdname><mode>3_insn"
+  [(set (reg:ALL8S ACC_A)
+        (ss_addsub:ALL8S (reg:ALL8S ACC_A)
+                         (reg:ALL8S ACC_B)))]
+  "avr_have_dimode"
+  "%~call __<code_stdname><mode>3"
+  [(set_attr "adjust_len" "call")
+   (set_attr "cc" "clobber")])
+
+(define_insn "<code_stdname><mode>3_const_insn"
+  [(set (reg:ALL8S ACC_A)
+        (ss_addsub:ALL8S (reg:ALL8S ACC_A)
+                         (match_operand:ALL8S 0 "const_operand" "n Ynn")))]
+  "avr_have_dimode"
+  {
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "adjust_len" "minus64")
+  [(set_attr "adjust_len" "plus")
+   (set_attr "cc" "clobber")])
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Unsigned Saturating Addition and Subtraction
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define_expand "<code_stdname><mode>3"
+  [(set (match_operand:ALL8U 0 "general_operand" "")
+        (us_addsub:ALL8U (match_operand:ALL8U 1 "general_operand" "")
+                         (match_operand:ALL8U 2 "general_operand" "")))]
+  "avr_have_dimode"
+  {
+    rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
+
+    emit_move_insn (acc_a, operands[1]);
+
+    if (const_operand (operands[2], GET_MODE (operands[2])))
+      {
+        emit_insn (gen_<code_stdname><mode>3_const_insn (operands[2]));
+      }
+    else
+      {
+        emit_move_insn (gen_rtx_REG (<MODE>mode, ACC_B), operands[2]);
+        emit_insn (gen_<code_stdname><mode>3_insn ());
+      }
+
+    emit_move_insn (operands[0], acc_a);
+    DONE;
+  })
+
+(define_insn "<code_stdname><mode>3_insn"
+  [(set (reg:ALL8U ACC_A)
+        (us_addsub:ALL8U (reg:ALL8U ACC_A)
+                         (reg:ALL8U ACC_B)))]
+  "avr_have_dimode"
+  "%~call __<code_stdname><mode>3"
+  [(set_attr "adjust_len" "call")
    (set_attr "cc" "clobber")])
 
+(define_insn "<code_stdname><mode>3_const_insn"
+  [(set (reg:ALL8U ACC_A)
+        (us_addsub:ALL8U (reg:ALL8U ACC_A)
+                         (match_operand:ALL8U 0 "const_operand" "n Ynn")))]
+  "avr_have_dimode"
+  {
+    return avr_out_plus (insn, operands);
+  }
+  [(set_attr "adjust_len" "plus")
+   (set_attr "cc" "clobber")])
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Negation
index bfbdaec..5a99499 100644 (file)
                               (HA "") (UHA "")])
 (define_mode_iterator ALL4A [(SA "") (USA "")])
 
+(define_mode_iterator ALL2S [HQ HA])
+(define_mode_iterator ALL4S [SA SQ])
+(define_mode_iterator ALL24S  [    HQ   HA  SA  SQ])
+(define_mode_iterator ALL124S [ QQ HQ   HA  SA  SQ])
+(define_mode_iterator ALL124U [UQQ UHQ UHA USA USQ])
+
 ;;; Conversions
 
 (define_mode_iterator FIXED_A
    (set_attr "adjust_len" "ufract")])
 
 ;******************************************************************************
+;** Saturated Addition and Subtraction
+;******************************************************************************
+
+;; Fixme:  It would be nice if we could expand the 32-bit versions to a
+;;    transparent libgcc call if $2 is a REG.  Problem is that it is
+;;    not possible to describe that addition is commutative.
+;;    And defining register classes/constraintrs for the involved hard
+;;    registers and let IRA do the work, yields inacceptable bloated code.
+;;    Thus, we have to live with the up to 11 instructions that are output
+;;    for these 32-bit saturated operations.
+
+;; "ssaddqq3"  "ssaddhq3"  "ssaddha3"  "ssaddsq3"  "ssaddsa3"
+;; "sssubqq3"  "sssubhq3"  "sssubha3"  "sssubsq3"  "sssubsa3"
+(define_insn "<code_stdname><mode>3"
+  [(set (match_operand:ALL124S 0 "register_operand"                          "=??d,d")
+        (ss_addsub:ALL124S (match_operand:ALL124S 1 "register_operand" "<abelian>0,0")
+                           (match_operand:ALL124S 2 "nonmemory_operand"         "r,Ynn")))]
+  ""
+  {
+    return avr_out_plus (insn, operands);
+  }
+  [(set_attr "cc" "clobber")
+   (set_attr "adjust_len" "plus")])
+
+;; "usadduqq3"  "usadduhq3"  "usadduha3" "usaddusq3"  "usaddusa3"
+;; "ussubuqq3"  "ussubuhq3"  "ussubuha3" "ussubusq3"  "ussubusa3"
+(define_insn "<code_stdname><mode>3"
+  [(set (match_operand:ALL124U 0 "register_operand"                          "=??r,d")
+        (us_addsub:ALL124U (match_operand:ALL124U 1 "register_operand" "<abelian>0,0")
+                           (match_operand:ALL124U 2 "nonmemory_operand"         "r,Ynn")))]
+  ""
+  {
+    return avr_out_plus (insn, operands);
+  }
+  [(set_attr "cc" "clobber")
+   (set_attr "adjust_len" "plus")])
+
+;******************************************************************************
+;** Saturated Negation and Absolute Value
+;******************************************************************************
+
+;; Fixme: This will always result in 0.  Dunno why simplify-rtx.c says
+;;   "unknown" on how to optimize this.  libgcc call would be in order,
+;;   but the performance is *PLAIN* *HORROR* because the optimizers don't
+;;   manage to optimize out MEMCPY that's sprincled all over fixed-bit.c  */
+
+(define_expand "usneg<mode>2"
+  [(parallel [(match_operand:ALL124U 0 "register_operand" "")
+              (match_operand:ALL124U 1 "nonmemory_operand" "")])]
+  ""
+  {
+    emit_move_insn (operands[0], CONST0_RTX (<MODE>mode));
+    DONE;
+  })
+
+(define_insn "ssnegqq2"
+  [(set (match_operand:QQ 0 "register_operand"            "=r")
+        (ss_neg:QQ (match_operand:QQ 1 "register_operand"  "0")))]
+  ""
+  "neg %0\;brvc 0f\;dec %0\;0:"
+  [(set_attr "cc" "clobber")
+   (set_attr "length" "3")])
+
+(define_insn "ssabsqq2"
+  [(set (match_operand:QQ 0 "register_operand"            "=r")
+        (ss_abs:QQ (match_operand:QQ 1 "register_operand"  "0")))]
+  ""
+  "sbrc %0,7\;neg %0\;sbrc %0,7\;dec %0"
+  [(set_attr "cc" "clobber")
+   (set_attr "length" "4")])
+
+;; "ssneghq2"  "ssnegha2"  "ssnegsq2"  "ssnegsa2"
+;; "ssabshq2"  "ssabsha2"  "ssabssq2"  "ssabssa2"
+(define_expand "<code_stdname><mode>2"
+  [(set (match_dup 2)
+        (match_operand:ALL24S 1 "register_operand" ""))
+   (set (match_dup 2)
+        (ss_abs_neg:ALL24S (match_dup 2)))
+   (set (match_operand:ALL24S 0 "register_operand" "")
+        (match_dup 2))]
+  ""
+  {
+    operands[2] = gen_rtx_REG (<MODE>mode, 26 - GET_MODE_SIZE (<MODE>mode));
+  })
+
+;; "*ssneghq2"  "*ssnegha2"
+;; "*ssabshq2"  "*ssabsha2"
+(define_insn "*<code_stdname><mode>2"
+  [(set (reg:ALL2S 24)
+        (ss_abs_neg:ALL2S (reg:ALL2S 24)))]
+  ""
+  "%~call __<code_stdname>_2"
+  [(set_attr "type" "xcall")
+   (set_attr "cc" "clobber")])
+
+;; "*ssnegsq2"  "*ssnegsa2"
+;; "*ssabssq2"  "*ssabssa2"
+(define_insn "*<code_stdname><mode>2"
+  [(set (reg:ALL4S 22)
+        (ss_abs_neg:ALL4S (reg:ALL4S 22)))]
+  ""
+  "%~call __<code_stdname>_4"
+  [(set_attr "type" "xcall")
+   (set_attr "cc" "clobber")])
+
+;******************************************************************************
 ; mul
 
 ;; "mulqq3" "muluqq3"
index 5d6fabb..690c89b 100644 (file)
@@ -91,12 +91,8 @@ extern int avr_starting_frame_offset (void);
 extern void avr_output_addr_vec_elt (FILE *stream, int value);
 extern const char *avr_out_sbxx_branch (rtx insn, rtx operands[]);
 extern const char* avr_out_bitop (rtx, rtx*, int*);
-extern const char* avr_out_plus (rtx*, int*, int*);
-extern const char* avr_out_plus_noclobber (rtx*, int*, int*);
-extern const char* avr_out_plus64 (rtx, int*);
+extern const char* avr_out_plus (rtx, rtx*, int* =NULL, int* =NULL);
 extern const char* avr_out_addto_sp (rtx*, int*);
-extern const char* avr_out_minus (rtx*, int*, int*);
-extern const char* avr_out_minus64 (rtx, int*);
 extern const char* avr_out_xload (rtx, rtx*, int*);
 extern const char* avr_out_movmem (rtx, rtx*, int*);
 extern const char* avr_out_insert_bits (rtx*, int*);
index e69e35a..c46f488 100644 (file)
@@ -2069,9 +2069,11 @@ avr_print_operand (FILE *file, rtx x, int code)
   else if (REG_P (x))
     {
       if (x == zero_reg_rtx)
-       fprintf (file, "__zero_reg__");
+        fprintf (file, "__zero_reg__");
+      else if (code == 'r' && REGNO (x) < 32)
+        fprintf (file, "%d", (int) REGNO (x));
       else
-       fprintf (file, reg_names[true_regnum (x) + abcd]);
+        fprintf (file, reg_names[REGNO (x) + abcd]);
     }
   else if (CONST_INT_P (x))
     {
@@ -2172,7 +2174,7 @@ avr_print_operand (FILE *file, rtx x, int code)
       /* Use normal symbol for direct address no linker trampoline needed */
       output_addr_const (file, x);
     }
-  else if (GET_CODE (x) == CONST_FIXED)
+  else if (CONST_FIXED_P (x))
     {
       HOST_WIDE_INT ival = INTVAL (avr_to_int_mode (x));
       if (code != 0)
@@ -2213,9 +2215,7 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
     default:
       break;
 
-    case CC_OUT_PLUS:
-    case CC_OUT_PLUS_NOCLOBBER:
-    case CC_MINUS:
+    case CC_PLUS:
     case CC_LDI:
       {
         rtx *op = recog_data.operand;
@@ -2229,18 +2229,8 @@ notice_update_cc (rtx body ATTRIBUTE_UNUSED, rtx insn)
           default:
             gcc_unreachable();
             
-          case CC_OUT_PLUS:
-            avr_out_plus (op, &len_dummy, &icc);
-            cc = (enum attr_cc) icc;
-            break;
-            
-          case CC_OUT_PLUS_NOCLOBBER:
-            avr_out_plus_noclobber (op, &len_dummy, &icc);
-            cc = (enum attr_cc) icc;
-            break;
-
-          case CC_MINUS:
-            avr_out_minus (op, &len_dummy, &icc);
+          case CC_PLUS:
+            avr_out_plus (insn, op, &len_dummy, &icc);
             cc = (enum attr_cc) icc;
             break;
 
@@ -4246,7 +4236,7 @@ avr_out_compare (rtx insn, rtx *xop, int *plen)
   /* Map fixed mode operands to integer operands with the same binary
      representation.  They are easier to handle in the remainder.  */
 
-  if (CONST_FIXED == GET_CODE (xval))
+  if (CONST_FIXED_P (xval))
     {
       xreg = avr_to_int_mode (xop[0]);
       xval = avr_to_int_mode (xop[1]);
@@ -5987,19 +5977,32 @@ lshrsi3_out (rtx insn, rtx operands[], int *len)
 }
 
 
-/* Output addition of register XOP[0] and compile time constant XOP[2]:
-
+/* Output addition of register XOP[0] and compile time constant XOP[2].
+   CODE == PLUS:  perform addition by using ADD instructions or
+   CODE == MINUS: perform addition by using SUB instructions:
+   
       XOP[0] = XOP[0] + XOP[2]
+      
+   Or perform addition/subtraction with register XOP[2] depending on CODE:
+   
+      XOP[0] = XOP[0] +/- XOP[2]
 
-   and return "".  If PLEN == NULL, print assembler instructions to perform the
-   addition; otherwise, set *PLEN to the length of the instruction sequence (in
-   words) printed with PLEN == NULL.  XOP[3] is an 8-bit scratch register.
-   CODE == PLUS:  perform addition by using ADD instructions.
-   CODE == MINUS: perform addition by using SUB instructions.
-   Set *PCC to effect on cc0 according to respective CC_* insn attribute.  */
+   If PLEN == NULL, print assembler instructions to perform the operation;
+   otherwise, set *PLEN to the length of the instruction sequence (in words)
+   printed with PLEN == NULL.  XOP[3] is an 8-bit scratch register or NULL_RTX.
+   Set *PCC to effect on cc0 according to respective CC_* insn attribute.
+
+   CODE_SAT == UNKNOWN: Perform ordinary, non-saturating operation.
+   CODE_SAT != UNKNOWN: Perform operation and saturate according to CODE_SAT.
+   If  CODE_SAT != UNKNOWN  then SIGN contains the sign of the summand resp.
+   the subtrahend in the original insn, provided it is a compile time constant.
+   In all other cases, SIGN is 0.
+
+   Return "".  */
 
 static void
-avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
+avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc,
+                enum rtx_code code_sat = UNKNOWN, int sign = 0)
 {
   /* MODE of the operation.  */
   enum machine_mode mode = GET_MODE (xop[0]);
@@ -6026,6 +6029,41 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
   /* Value to add.  There are two ways to add VAL: R += VAL and R -= -VAL.  */
   rtx xval = xop[2];
 
+  /* Output a BRVC instruction.  Only needed with saturation.  */
+  bool out_brvc = true;
+
+  if (plen)
+    *plen = 0;
+
+  if (REG_P (xop[2]))
+    {
+      *pcc = MINUS == code ? (int) CC_SET_CZN : (int) CC_SET_N;
+
+      for (i = 0; i < n_bytes; i++)
+        {
+          /* We operate byte-wise on the destination.  */
+          op[0] = simplify_gen_subreg (QImode, xop[0], mode, i);
+          op[1] = simplify_gen_subreg (QImode, xop[2], mode, i);
+
+          if (i == 0)
+            avr_asm_len (code == PLUS ? "add %0,%1" : "sub %0,%1",
+                         op, plen, 1);
+          else
+            avr_asm_len (code == PLUS ? "adc %0,%1" : "sbc %0,%1",
+                         op, plen, 1);
+        }
+
+      if (reg_overlap_mentioned_p (xop[0], xop[2]))
+        {
+          gcc_assert (REGNO (xop[0]) == REGNO (xop[2]));
+          
+          if (MINUS == code)
+            return;
+        }
+
+      goto saturate;
+    }
+
   /* Except in the case of ADIW with 16-bit register (see below)
      addition does not set cc0 in a usable way.  */
   
@@ -6034,13 +6072,39 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
   if (CONST_FIXED_P (xval))
     xval = avr_to_int_mode (xval);
 
+  /* Adding/Subtracting zero is a no-op.  */
+  
+  if (xval == const0_rtx)
+    {
+      *pcc = CC_NONE;
+      return;
+    }
+
   if (MINUS == code)
     xval = simplify_unary_operation (NEG, imode, xval, imode);
 
   op[2] = xop[3];
 
-  if (plen)
-    *plen = 0;
+  if (SS_PLUS == code_sat && MINUS == code
+      && sign < 0
+      && 0x80 == (INTVAL (simplify_gen_subreg (QImode, xval, imode, n_bytes-1))
+                  & GET_MODE_MASK (QImode)))
+    {
+      /* We compute x + 0x80 by means of SUB instructions.  We negated the
+         constant subtrahend above and are left with  x - (-128)  so that we
+         need something like SUBI r,128 which does not exist because SUBI sets
+         V according to the sign of the subtrahend.  Notice the only case
+         where this must be done is when NEG overflowed in case [2s] because
+         the V computation needs the right sign of the subtrahend.  */
+      
+      rtx msb = simplify_gen_subreg (QImode, xop[0], mode, n_bytes-1);
+
+      avr_asm_len ("subi %0,128" CR_TAB
+                   "brmi 0f", &msb, plen, 2);
+      out_brvc = false;
+
+      goto saturate;
+    }
 
   for (i = 0; i < n_bytes; i++)
     {
@@ -6082,7 +6146,7 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
                                op, plen, 1);
 
                   if (n_bytes == 2 && PLUS == code)
-                      *pcc = CC_SET_ZN;
+                    *pcc = CC_SET_ZN;
                 }
 
               i++;
@@ -6099,6 +6163,7 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
           continue;
         }
       else if ((val8 == 1 || val8 == 0xff)
+               && UNKNOWN == code_sat
                && !started
                && i == n_bytes - 1)
         {
@@ -6111,7 +6176,17 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
         {
         case PLUS:
 
-          gcc_assert (plen != NULL || REG_P (op[2]));
+          gcc_assert (plen != NULL || (op[2] && REG_P (op[2])));
+
+          if (plen != NULL && UNKNOWN != code_sat)
+            {
+              /* This belongs to the x + 0x80 corner case.  The code with
+                 ADD instruction is not smaller, thus make this case
+                 expensive so that the caller won't pick it.  */
+
+              *plen += 10;
+              break;
+            }
 
           if (clobber_val != (int) val8)
             avr_asm_len ("ldi %2,%1", op, plen, 1);
@@ -6147,133 +6222,369 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc)
 
     } /* for all sub-bytes */
 
-  /* No output doesn't change cc0.  */
-  
-  if (plen && *plen == 0)
-    *pcc = CC_NONE;
-}
+ saturate:
 
+  if (UNKNOWN == code_sat)
+    return;
 
-/* Output addition of register XOP[0] and compile time constant XOP[2]:
+  *pcc = (int) CC_CLOBBER;
 
-      XOP[0] = XOP[0] + XOP[2]
+  /* Vanilla addition/subtraction is done.  We are left with saturation.
+     
+     We have to compute  A = A <op> B  where  A  is a register and
+     B is a register or a non-zero compile time constant CONST.
+     A is register class "r" if unsigned && B is REG.  Otherwise, A is in "d".
+     B stands for the original operand $2 in INSN.  In the case of B = CONST
+     SIGN in { -1, 1 } is the sign of B.  Otherwise, SIGN is 0.
+     
+     CODE is the instruction flavor we use in the asm sequence to perform <op>.
+     
+     
+     unsigned
+     operation        |  code |  sat if  |    b is      | sat value |  case
+     -----------------+-------+----------+--------------+-----------+-------
+     +  as  a + b     |  add  |  C == 1  |  const, reg  | u+ = 0xff |  [1u]
+     +  as  a - (-b)  |  sub  |  C == 0  |  const       | u+ = 0xff |  [2u]
+     -  as  a - b     |  sub  |  C == 1  |  const, reg  | u- = 0    |  [3u]
+     -  as  a + (-b)  |  add  |  C == 0  |  const       | u- = 0    |  [4u]
+     
+     
+     signed
+     operation        |  code |  sat if  |    b is      | sat value |  case
+     -----------------+-------+----------+--------------+-----------+-------
+     +  as  a + b     |  add  |  V == 1  |  const, reg  | s+        |  [1s]
+     +  as  a - (-b)  |  sub  |  V == 1  |  const       | s+        |  [2s]
+     -  as  a - b     |  sub  |  V == 1  |  const, reg  | s-        |  [3s]
+     -  as  a + (-b)  |  add  |  V == 1  |  const       | s-        |  [4s]
+     
+     s+  =  b < 0  ?  -0x80 :  0x7f
+     s-  =  b < 0  ?   0x7f : -0x80
+     
+     The cases a - b actually perform  a - (-(-b))  if B is CONST.
+  */
 
-   and return "".  If PLEN == NULL, print assembler instructions to perform the
-   addition; otherwise, set *PLEN to the length of the instruction sequence (in
-   words) printed with PLEN == NULL.
-   If PCC != 0 then set *PCC to the the instruction sequence's effect on the
-   condition code (with respect to XOP[0]).  */
+  op[0] = simplify_gen_subreg (QImode, xop[0], mode, n_bytes-1);
+  op[1] = n_bytes > 1
+    ? simplify_gen_subreg (QImode, xop[0], mode, n_bytes-2)
+    : NULL_RTX;
 
-const char*
-avr_out_plus (rtx *xop, int *plen, int *pcc)
-{
-  int len_plus, len_minus;
-  int cc_plus, cc_minus, cc_dummy;
+  if (!plen && flag_print_asm_name)
+    avr_fdump (asm_out_file, ";; %C (%C)\n", code_sat, code);
 
-  if (!pcc)
-    pcc = &cc_dummy;
-                                   
-  /* Work out if  XOP[0] += XOP[2]  is better or  XOP[0] -= -XOP[2].  */
+  bool need_copy = true;
+  int len_call = 1 + AVR_HAVE_JMP_CALL;
   
-  avr_out_plus_1 (xop, &len_plus, PLUS, &cc_plus);
-  avr_out_plus_1 (xop, &len_minus, MINUS, &cc_minus);
+  switch (code_sat)
+    {
+    default:
+      gcc_unreachable();
+
+    case SS_PLUS:
+    case SS_MINUS:
+      if (!plen && flag_print_asm_name)
+        avr_fdump (asm_out_file, ";; %s = %r\n", sign < 0 ? "neg" : "pos",
+                   xop[2]);
 
-  /* Prefer MINUS over PLUS if size is equal because it sets cc0.  */
+      if (out_brvc)
+        avr_asm_len ("brvc 0f", op, plen, 1);
+
+      if (reg_overlap_mentioned_p (xop[0], xop[2]))
+        {
+          /* [1s,reg] */
+
+          if (n_bytes == 1)
+            avr_asm_len ("ldi %0,0x7f" CR_TAB
+                         "adc %0,__zero_reg__", op, plen, 2);
+          else
+            avr_asm_len ("ldi %0,0x7f" CR_TAB
+                         "ldi %1,0xff" CR_TAB
+                         "adc %1,__zero_reg__" CR_TAB
+                         "adc %0,__zero_reg__", op, plen, 4);
+        }
+      else if (sign == 0 && PLUS == code)
+        {
+          /* [1s,reg] */
+
+          op[2] = simplify_gen_subreg (QImode, xop[2], mode, n_bytes-1);
+
+          if (n_bytes == 1)
+            avr_asm_len ("ldi %0,0x80" CR_TAB
+                         "sbrs %2,7"   CR_TAB
+                         "dec %0", op, plen, 3);
+          else
+            avr_asm_len ("ldi %0,0x80" CR_TAB
+                         "cp %2,%0"    CR_TAB
+                         "sbc %1,%1"   CR_TAB
+                         "sbci %0,0", op, plen, 4);
+        }
+      else if (sign == 0 && MINUS == code)
+        {
+          /* [3s,reg] */
+
+          op[2] = simplify_gen_subreg (QImode, xop[2], mode, n_bytes-1);
+
+          if (n_bytes == 1)
+            avr_asm_len ("ldi %0,0x7f" CR_TAB
+                         "sbrs %2,7"   CR_TAB
+                         "inc %0", op, plen, 3);
+          else
+            avr_asm_len ("ldi %0,0x7f" CR_TAB
+                         "cp %0,%2"    CR_TAB
+                         "sbc %1,%1"   CR_TAB
+                         "sbci %0,-1", op, plen, 4);
+        }
+      else if ((sign < 0) ^ (SS_MINUS == code_sat))
+        {
+          /* [1s,const,B < 0] [2s,B < 0] */
+          /* [3s,const,B > 0] [4s,B > 0] */
+
+          if (n_bytes == 8)
+            {
+              avr_asm_len ("%~call __clr_8", op, plen, len_call);
+              need_copy = false;
+            }
+
+          avr_asm_len ("ldi %0,0x80", op, plen, 1);
+          if (n_bytes > 1 && need_copy)
+            avr_asm_len ("clr %1", op, plen, 1);
+        }
+      else if ((sign > 0) ^ (SS_MINUS == code_sat))
+        {
+          /* [1s,const,B > 0] [2s,B > 0] */
+          /* [3s,const,B < 0] [4s,B < 0] */
+
+          if (n_bytes == 8)
+            {
+              avr_asm_len ("sec" CR_TAB
+                           "%~call __sbc_8", op, plen, 1 + len_call);
+              need_copy = false;
+            }
+
+          avr_asm_len ("ldi %0,0x7f", op, plen, 1);
+          if (n_bytes > 1 && need_copy)
+            avr_asm_len ("ldi %1,0xff", op, plen, 1);
+        }
+      else
+        gcc_unreachable();
+      
+      break;
+
+    case US_PLUS:
+      /* [1u] : [2u] */
+      
+      avr_asm_len (PLUS == code ? "brcc 0f" : "brcs 0f", op, plen, 1);
+      
+      if (n_bytes == 8)
+        {
+          if (MINUS == code)
+            avr_asm_len ("sec", op, plen, 1);
+          avr_asm_len ("%~call __sbc_8", op, plen, len_call);
+
+          need_copy = false;
+        }
+      else
+        {
+          if (MINUS == code && !test_hard_reg_class (LD_REGS, op[0]))
+            avr_asm_len ("sec" CR_TAB "sbc %0,%0", op, plen, 2);
+          else
+            avr_asm_len (PLUS == code ? "sbc %0,%0" : "ldi %0,0xff",
+                         op, plen, 1);
+        }
+      break; /* US_PLUS */
+      
+    case US_MINUS:
+      /* [4u] : [3u] */
+
+      avr_asm_len (PLUS == code ? "brcs 0f" : "brcc 0f", op, plen, 1);
+
+      if (n_bytes == 8)
+        {
+          avr_asm_len ("%~call __clr_8", op, plen, len_call);
+          need_copy = false;
+        }
+      else
+        avr_asm_len ("clr %0", op, plen, 1);
+      
+      break;
+    }
+
+  /* We set the MSB in the unsigned case and the 2 MSBs in the signed case.
+     Now copy the right value to the LSBs.  */
   
-  if (plen)
+  if (need_copy && n_bytes > 1)
     {
-      *plen = (len_minus <= len_plus) ? len_minus : len_plus;
-      *pcc  = (len_minus <= len_plus) ? cc_minus : cc_plus;
+      if (US_MINUS == code_sat || US_PLUS == code_sat)
+        {
+          avr_asm_len ("mov %1,%0", op, plen, 1);
+
+          if (n_bytes > 2)
+            {
+              op[0] = xop[0];
+              if (AVR_HAVE_MOVW)
+                avr_asm_len ("movw %0,%1", op, plen, 1);
+              else
+                avr_asm_len ("mov %A0,%1" CR_TAB
+                             "mov %B0,%1", op, plen, 2);
+            }
+        }
+      else if (n_bytes > 2)
+        {
+          op[0] = xop[0];
+          avr_asm_len ("mov %A0,%1" CR_TAB
+                       "mov %B0,%1", op, plen, 2);
+        }
     }
-  else if (len_minus <= len_plus)
-    avr_out_plus_1 (xop, NULL, MINUS, pcc);
-  else
-    avr_out_plus_1 (xop, NULL, PLUS, pcc);
 
-  return "";
+  if (need_copy && n_bytes == 8)
+    {
+      if (AVR_HAVE_MOVW)
+        avr_asm_len ("movw %r0+2,%0" CR_TAB
+                     "movw %r0+4,%0", xop, plen, 2);
+      else
+        avr_asm_len ("mov %r0+2,%0" CR_TAB
+                     "mov %r0+3,%0" CR_TAB
+                     "mov %r0+4,%0" CR_TAB
+                     "mov %r0+5,%0", xop, plen, 4);
+    }
+
+  avr_asm_len ("0:", op, plen, 0);
 }
 
 
-/* Same as above but XOP has just 3 entries.
-   Supply a dummy 4th operand.  */
+/* Output addition/subtraction of register XOP[0] and a constant XOP[2] that
+   is ont a compile-time constant:
 
-const char*
-avr_out_plus_noclobber (rtx *xop, int *plen, int *pcc)
+      XOP[0] = XOP[0] +/- XOP[2]
+
+   This is a helper for the function below.  The only insns that need this
+   are additions/subtraction for pointer modes, i.e. HImode and PSImode.  */
+
+static const char*
+avr_out_plus_symbol (rtx *xop, enum rtx_code code, int *plen, int *pcc)
 {
-  rtx op[4];
+  enum machine_mode mode = GET_MODE (xop[0]);
+  int n_bytes = GET_MODE_SIZE (mode);
 
-  op[0] = xop[0];
-  op[1] = xop[1];
-  op[2] = xop[2];
-  op[3] = NULL_RTX;
+  /* Only pointer modes want to add symbols.  */
+  
+  gcc_assert (mode == HImode || mode == PSImode);
 
-  return avr_out_plus (op, plen, pcc);
+  *pcc = MINUS == code ? (int) CC_SET_CZN : (int) CC_SET_N;
+
+  avr_asm_len (PLUS == code
+               ? "subi %A0,lo8(-(%2))" CR_TAB "sbci %B0,hi8(-(%2))"
+               : "subi %A0,lo8(%2)"    CR_TAB "sbci %B0,hi8(%2)",
+               xop, plen, -2);
+
+  if (3 == n_bytes)
+    avr_asm_len (PLUS == code
+                 ? "sbci %C0,hlo8((-%2))"
+                 : "sbci %C0,hlo8(%2)", xop, plen, 1);
+  return "";
 }
 
 
-/* Output subtraction of register XOP[0] and compile time constant XOP[2]:
+/* Prepare operands of addition/subtraction to be used with avr_out_plus_1.
+   
+   INSN is a single_set insn with a binary operation as SET_SRC that is
+   one of:  PLUS, SS_PLUS, US_PLUS, MINUS, SS_MINUS, US_MINUS.
+
+   XOP are the operands of INSN.  In the case of 64-bit operations with
+   constant XOP[] has just one element:  The summand/subtrahend in XOP[0].
+   The non-saturating insns up to 32 bits may or may not supply a "d" class
+   scratch as XOP[3].
+
+   If PLEN == NULL output the instructions.
+   If PLEN != NULL set *PLEN to the length of the sequence in words.
 
-      XOP[0] = XOP[0] - XOP[2]
+   PCC is a pointer to store the instructions' effect on cc0.
+   PCC may be NULL.
 
-   This is basically the same as `avr_out_plus' except that we subtract.
-   It's needed because (minus x const) is not mapped to (plus x -const)
-   for the fixed point modes.  */
+   PLEN and PCC default to NULL.
+
+   Return ""  */
 
 const char*
-avr_out_minus (rtx *xop, int *plen, int *pcc)
+avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc)
 {
+  int cc_plus, cc_minus, cc_dummy;
+  int len_plus, len_minus;
   rtx op[4];
+  rtx xdest = SET_DEST (single_set (insn));
+  enum machine_mode mode = GET_MODE (xdest);
+  enum machine_mode imode = int_mode_for_mode (mode);
+  int n_bytes = GET_MODE_SIZE (mode);
+  enum rtx_code code_sat = GET_CODE (SET_SRC (single_set (insn)));
+  enum rtx_code code
+    = (PLUS == code_sat || SS_PLUS == code_sat || US_PLUS == code_sat
+       ? PLUS : MINUS);
 
-  if (pcc)
-    *pcc = (int) CC_SET_CZN;
-
-  if (REG_P (xop[2]))
-    return avr_asm_len ("sub %A0,%A2" CR_TAB
-                        "sbc %B0,%B2", xop, plen, -2);
+  if (!pcc)
+    pcc = &cc_dummy;
 
-  if (!CONST_INT_P (xop[2])
-      && !CONST_FIXED_P (xop[2]))
-    return avr_asm_len ("subi %A0,lo8(%2)" CR_TAB
-                        "sbci %B0,hi8(%2)", xop, plen, -2);
+  /* PLUS and MINUS don't saturate:  Use modular wrap-around.  */
   
-  op[0] = avr_to_int_mode (xop[0]);
-  op[1] = avr_to_int_mode (xop[1]);
-  op[2] = gen_int_mode (-INTVAL (avr_to_int_mode (xop[2])),
-                        GET_MODE (op[0]));
-  op[3] = xop[3];
+  if (PLUS == code_sat || MINUS == code_sat)
+    code_sat = UNKNOWN;
 
-  return avr_out_plus (op, plen, pcc);
-}
+  if (n_bytes <= 4 && REG_P (xop[2]))
+    {
+      avr_out_plus_1 (xop, plen, code, pcc, code_sat);
+      return "";
+    }
 
+  if (8 == n_bytes)
+    {
+      op[0] = gen_rtx_REG (DImode, ACC_A);
+      op[1] = gen_rtx_REG (DImode, ACC_A);
+      op[2] = avr_to_int_mode (xop[0]);
+    }
+  else
+    {
+      if (!REG_P (xop[2])
+          && !CONST_INT_P (xop[2])
+          && !CONST_FIXED_P (xop[2]))
+        {
+          return avr_out_plus_symbol (xop, code, plen, pcc);
+        }
+      
+      op[0] = avr_to_int_mode (xop[0]);
+      op[1] = avr_to_int_mode (xop[1]);
+      op[2] = avr_to_int_mode (xop[2]);
+    }
 
-/* Prepare operands of adddi3_const_insn to be used with avr_out_plus_1.  */
+  /* Saturations and 64-bit operations don't have a clobber operand.
+     For the other cases, the caller will provide a proper XOP[3].  */
+  
+  op[3] = PARALLEL == GET_CODE (PATTERN (insn)) ? xop[3] : NULL_RTX;
 
-const char*
-avr_out_plus64 (rtx addend, int *plen)
-{
-  int cc_dummy;
-  rtx op[4];
+  /* Saturation will need the sign of the original operand.  */
 
-  op[0] = gen_rtx_REG (DImode, 18);
-  op[1] = op[0];
-  op[2] = addend;
-  op[3] = NULL_RTX;
+  rtx xmsb = simplify_gen_subreg (QImode, op[2], imode, n_bytes-1);
+  int sign = INTVAL (xmsb) < 0 ? -1 : 1;
 
-  avr_out_plus_1 (op, plen, MINUS, &cc_dummy);
+  /* If we subtract and the subtrahend is a constant, then negate it
+     so that avr_out_plus_1 can be used.  */
 
-  return "";
-}
+  if (MINUS == code)
+    op[2] = simplify_unary_operation (NEG, imode, op[2], imode);
 
+  /* Work out the shortest sequence.  */
 
-/* Prepare operands of subdi3_const_insn to be used with avr_out_plus64.  */
+  avr_out_plus_1 (op, &len_minus, MINUS, &cc_plus, code_sat, sign);
+  avr_out_plus_1 (op, &len_plus, PLUS, &cc_minus, code_sat, sign);
 
-const char*
-avr_out_minus64 (rtx subtrahend, int *plen)
-{
-  rtx xneg = avr_to_int_mode (subtrahend);
-  xneg = simplify_unary_operation (NEG, DImode, xneg, DImode);
+  if (plen)
+    {
+      *plen = (len_minus <= len_plus) ? len_minus : len_plus;
+      *pcc  = (len_minus <= len_plus) ? cc_minus : cc_plus;
+    }
+  else if (len_minus <= len_plus)
+    avr_out_plus_1 (op, NULL, MINUS, pcc, code_sat, sign);
+  else
+    avr_out_plus_1 (op, NULL, PLUS, pcc, code_sat, sign);
 
-  return avr_out_plus64 (xneg, plen);
+  return "";
 }
 
 
@@ -7004,13 +7315,7 @@ adjust_insn_length (rtx insn, int len)
       
     case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break;
       
-    case ADJUST_LEN_OUT_PLUS: avr_out_plus (op, &len, NULL); break;
-    case ADJUST_LEN_PLUS64: avr_out_plus64 (op[0], &len); break;
-    case ADJUST_LEN_MINUS: avr_out_minus (op, &len, NULL); break;
-    case ADJUST_LEN_MINUS64: avr_out_minus64 (op[0], &len); break;
-    case ADJUST_LEN_OUT_PLUS_NOCLOBBER:
-      avr_out_plus_noclobber (op, &len, NULL); break;
-
+    case ADJUST_LEN_PLUS: avr_out_plus (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;
@@ -8897,8 +9202,8 @@ avr_rtx_costs (rtx x, int codearg, int outer_code,
 
 static int
 avr_address_cost (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED,
-                 addr_space_t as ATTRIBUTE_UNUSED,
-                 bool speed ATTRIBUTE_UNUSED)
+                  addr_space_t as ATTRIBUTE_UNUSED,
+                  bool speed ATTRIBUTE_UNUSED)
 {
   int cost = 4;
   
index 6146fe6..8b13010 100644 (file)
@@ -91,7 +91,7 @@
 
 ;; Condition code settings.
 (define_attr "cc" "none,set_czn,set_zn,set_n,compare,clobber,
-                   out_plus, out_plus_noclobber,ldi,minus"
+                   plus,ldi"
   (const_string "none"))
 
 (define_attr "type" "branch,branch1,arith,xcall"
 ;; Otherwise do special processing depending on the attribute.
 
 (define_attr "adjust_len"
-  "out_bitop, out_plus, out_plus_noclobber, plus64, addto_sp,
-   minus, minus64,
+  "out_bitop, plus, addto_sp,
    tsthi, tstpsi, tstsi, compare, compare64, call,
    mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
    ufract, sfract,
 (define_code_iterator xior [xor ior])
 (define_code_iterator eqne [eq ne])
 
+(define_code_iterator ss_addsub [ss_plus ss_minus])
+(define_code_iterator us_addsub [us_plus us_minus])
+(define_code_iterator ss_abs_neg [ss_abs ss_neg])
+
 ;; Define code attributes
 (define_code_attr extend_su
   [(sign_extend "s")
   [(zero_extend "r")
    (sign_extend "d")])
 
+(define_code_attr abelian
+  [(ss_minus "") (us_minus "")
+   (ss_plus "%") (us_plus "%")])
+
 ;; Map RTX code to its standard insn name
 (define_code_attr code_stdname
   [(ashift   "ashl")
    (lshiftrt "lshr")
    (ior      "ior")
    (xor      "xor")
-   (rotate   "rotl")])
+   (rotate   "rotl")
+   (ss_plus  "ssadd")  (ss_minus "sssub")  (ss_neg "ssneg")  (ss_abs "ssabs")
+   (us_plus  "usadd")  (us_minus "ussub")  (us_neg "usneg")
+   ])
 
 ;;========================================================================
 ;; The following is used by nonlocal_goto and setjmp.
                    (match_operand:ALL2 2 "nonmemory_or_const_operand" "r,s,IJ YIJ,n Ynn")))]
   ""
   {
-    if (REG_P (operands[2]))
-      return "add %A0,%A2\;adc %B0,%B2";
-    else if (CONST_INT_P (operands[2])
-             || CONST_FIXED == GET_CODE (operands[2]))
-      return avr_out_plus_noclobber (operands, NULL, NULL);
-    else
-      return "subi %A0,lo8(-(%2))\;sbci %B0,hi8(-(%2))";
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "length" "2,2,2,2")
-   (set_attr "adjust_len" "*,*,out_plus_noclobber,out_plus_noclobber")
-   (set_attr "cc" "set_n,set_czn,out_plus_noclobber,out_plus_noclobber")])
+  [(set_attr "length" "2")
+   (set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 ;; Adding a constant to NO_LD_REGS might have lead to a reload of
 ;; that constant to LD_REGS.  We don't add a scratch to *addhi3
    (clobber (match_scratch:QI 3                             "=X     ,X    ,&d"))]
   ""
   {
-    gcc_assert (REGNO (operands[0]) == REGNO (operands[1]));
-    
-    return avr_out_plus (operands, NULL, NULL);
+    return avr_out_plus (insn, operands);
   }
   [(set_attr "length" "4")
-   (set_attr "adjust_len" "out_plus")
-   (set_attr "cc" "out_plus")])
+   (set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 
 ;; "addsi3"
    (clobber (match_scratch:QI 3                             "=X,X ,&d"))]
   ""
   {
-    if (REG_P (operands[2]))
-      return "add %A0,%A2\;adc %B0,%B2\;adc %C0,%C2\;adc %D0,%D2";
-
-    return avr_out_plus (operands, NULL, NULL);
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "length" "4,4,8")
-   (set_attr "adjust_len" "*,out_plus,out_plus")
-   (set_attr "cc" "set_n,out_plus,out_plus")])
+  [(set_attr "length" "4")
+   (set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 (define_insn "*addpsi3_zero_extend.qi"
   [(set (match_operand:PSI 0 "register_operand"                          "=r")
    (clobber (match_scratch:QI 3                           "=X,X ,X,&d"))]
   ""
   {
-    static const char * const asm_code[] =
-      {
-        "add %A0,%A2\;adc %B0,%B2\;adc %C0,%C2",
-        "subi %0,lo8(-(%2))\;sbci %B0,hi8(-(%2))\;sbci %C0,hlo8(-(%2))",
-        "",
-        ""
-      };
-
-    if (*asm_code[which_alternative])
-      return asm_code[which_alternative];
-
-    return avr_out_plus (operands, NULL, NULL);
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "length" "3,3,3,6")
-   (set_attr "adjust_len" "*,*,out_plus,out_plus")
-   (set_attr "cc" "set_n,set_czn,out_plus,out_plus")])
+  [(set_attr "length" "3")
+   (set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 (define_insn "subpsi3"
   [(set (match_operand:PSI 0 "register_operand"           "=r")
    (clobber (match_scratch:QI 3                                       "=X,X    ,&d"))]
   ""
   {
-    return avr_out_minus (operands, NULL, NULL);
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "adjust_len" "minus")
-   (set_attr "cc" "minus")])
+  [(set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 (define_insn "*subhi3_zero_extend1"
   [(set (match_operand:HI 0 "register_operand"                          "=r")
    (clobber (match_scratch:QI 3                                       "=X,X    ,&d"))]
   ""
   {
-    if (REG_P (operands[2]))
-      return "sub %0,%2\;sbc %B0,%B2\;sbc %C0,%C2\;sbc %D0,%D2";
-    
-    return avr_out_minus (operands, NULL, NULL);
+    return avr_out_plus (insn, operands);
   }
-  [(set_attr "length" "4")
-   (set_attr "adjust_len" "*,minus,minus")
-   (set_attr "cc" "set_czn")])
+  [(set_attr "adjust_len" "plus")
+   (set_attr "cc" "plus")])
 
 (define_insn "*subsi3_zero_extend"
   [(set (match_operand:SI 0 "register_operand"                          "=r")
index 9a2ef73..89b65e0 100644 (file)
@@ -1,3 +1,24 @@
+2012-09-15  Georg-Johann Lay  <avr@gjlay.de>
+
+       PR target/54222
+       * gcc.target/avr/torture/fix-types.h: New.
+       * gcc.target/avr/torture/vals-hr.def: New.
+       * gcc.target/avr/torture/vals-r.def: New.
+       * gcc.target/avr/torture/vals-k.def: New.
+       * gcc.target/avr/torture/vals-ur.def: New.
+       * gcc.target/avr/torture/vals-uk.def: New.
+       * gcc.target/avr/torture/vals-uhr.def: New.
+       * gcc.target/avr/torture/vals-llk.def: New.
+       * gcc.target/avr/torture/vals-ullk.def: New.
+       * gcc.target/avr/torture/sat-hr-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-r-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-k-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-ur-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-uk-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-uhr-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-llk-plus-minus.c: New.
+       * gcc.target/avr/torture/sat-ullk-plus-minus.c: New.
+
 2012-09-14  Dehao Chen  <dehao@google.com>
 
        * g++.dg/debug/dwarf2/deallocator.C: New test.
diff --git a/gcc/testsuite/gcc.target/avr/torture/fix-types.h b/gcc/testsuite/gcc.target/avr/torture/fix-types.h
new file mode 100644 (file)
index 0000000..f6a2aeb
--- /dev/null
@@ -0,0 +1,134 @@
+typedef __INT8_TYPE__  int_hr_t;
+typedef __UINT8_TYPE__ int_uhr_t;
+
+typedef __INT16_TYPE__  int_hk_t;
+typedef __UINT16_TYPE__ int_uhk_t;
+typedef __INT16_TYPE__  int_r_t;
+typedef __UINT16_TYPE__ int_ur_t;
+
+typedef __INT32_TYPE__  int_k_t;
+typedef __UINT32_TYPE__ int_uk_t;
+typedef __INT32_TYPE__  int_lr_t;
+typedef __UINT32_TYPE__ int_ulr_t;
+
+typedef __INT64_TYPE__  int_lk_t;
+typedef __UINT64_TYPE__ int_ulk_t;
+typedef __INT64_TYPE__  int_llr_t;
+typedef __UINT64_TYPE__ int_ullr_t;
+typedef __INT64_TYPE__  int_llk_t;
+typedef __UINT64_TYPE__ int_ullk_t;
+
+typedef __INT16_TYPE__  xint_hr_t;
+typedef __UINT16_TYPE__ xint_uhr_t;
+
+typedef __INT32_TYPE__  xint_hk_t;
+typedef __UINT32_TYPE__ xint_uhk_t;
+typedef __INT32_TYPE__  xint_r_t;
+typedef __UINT32_TYPE__ xint_ur_t;
+
+typedef __INT64_TYPE__  xint_k_t;
+typedef __UINT64_TYPE__ xint_uk_t;
+typedef __INT64_TYPE__  xint_lr_t;
+typedef __UINT64_TYPE__ xint_ulr_t;
+
+#define INThr_MAX __INT8_MAX__
+#define INThr_MIN (-__INT8_MAX__-1)
+#define INTuhr_MAX __UINT8_MAX__
+
+#define INTr_MAX __INT16_MAX__
+#define INTr_MIN (-__INT16_MAX__-1)
+#define INTur_MAX __UINT16_MAX__
+
+#define INThk_MAX __INT16_MAX__
+#define INThk_MIN (-__INT16_MAX__-1)
+#define INTuhk_MAX __UINT16_MAX__
+
+#define INTlr_MAX __INT32_MAX__
+#define INTlr_MIN (-__INT32_MAX__-1)
+#define INTulr_MAX __UINT32_MAX__
+
+#define INTk_MAX __INT32_MAX__
+#define INTk_MIN (-__INT32_MAX__-1)
+#define INTuk_MAX __UINT32_MAX__
+
+#define INTlk_MAX __INT64_MAX__
+#define INTlk_MIN (-__INT64_MAX__-1)
+#define INTulk_MAX __UINT64_MAX__
+
+#define INTllk_MAX __INT64_MAX__
+#define INTllk_MIN (-__INT64_MAX__-1)
+#define INTullk_MAX __UINT64_MAX__
+
+#define SS_FUN(NAME, OP, T, FX)                 \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    xint_##FX##_t ic;                           \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (xint_##FX##_t) ia OP ib;              \
+    if (ic > INT##FX##_MAX)                     \
+      ic = INT##FX##_MAX;                       \
+    else if (ic < INT##FX##_MIN)                \
+      ic = INT##FX##_MIN;                       \
+    ia = (int_##FX##_t) ic;                     \
+    __builtin_memcpy (&fa, &ia, sizeof (ia));   \
+    return fa;                                  \
+  }
+
+#define US_FUN(NAME, OP, T, FX)                 \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    xint_##FX##_t ic;                           \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (xint_##FX##_t) ia OP ib;              \
+    if (ic > INT##FX##_MAX)                     \
+      ic = INT##FX##_MAX;                       \
+    else if (ic < 0)                            \
+      ic = 0;                                   \
+    ia = (int_##FX##_t) ic;                     \
+    __builtin_memcpy (&fa, &ia, sizeof (ia));   \
+    return fa;                                  \
+  }
+
+#define SS_LFUN(NAME, OP, T, FX, CMP)           \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    int_##FX##_t ic;                            \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (int_##FX##_t) ia OP ib;               \
+    if (ic < ia && ib CMP 0)                    \
+      ic = INT##FX##_MAX;                       \
+    else if (ic > ia && 0 CMP ib)               \
+      ic = INT##FX##_MIN;                       \
+    __builtin_memcpy (&fa, &ic, sizeof (ic));   \
+    return fa;                                  \
+  }
+
+#define US_LFUN(NAME, OP, T, FX, CMP)           \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    int_##FX##_t ic;                            \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (int_##FX##_t) ia OP ib;               \
+    if (ia CMP ic && 1 CMP 0)                   \
+      ic = INT##FX##_MAX;                       \
+    if (ia CMP ic && 0 CMP 1)                   \
+      ic = 0;                                   \
+    __builtin_memcpy (&fa, &ic, sizeof (ic));   \
+    return fa;                                  \
+  }
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c
new file mode 100644 (file)
index 0000000..1e6215e
--- /dev/null
@@ -0,0 +1,98 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef short _Fract fx_t;
+typedef short _Sat _Fract satfx_t;
+typedef char intfx_t;
+
+SS_FUN (ss_add, +, fx_t, hr)
+SS_FUN (ss_sub, -, fx_t, hr)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_add2_##N (satfx_t a)             \
+    {                                           \
+        return ss_add_hr (a, X##P##-##7hr);     \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##7hr;                \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_sub2_##N (satfx_t a)             \
+    {                                           \
+        return ss_sub_hr (a, X##P##-##7hr);     \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##7hr;                \
+    }
+#include "vals-hr.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_hr (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __FRACT_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_hr (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __FRACT_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-hr.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, 1, 2, 0x7f, 0x80, 0x81, 0xff,
+    0x40, 0x3e, 0x3f, 0xbf, 0xc0, 0xc1
+  };
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c
new file mode 100644 (file)
index 0000000..8a26ffe
--- /dev/null
@@ -0,0 +1,108 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef _Accum fx_t;
+typedef _Sat _Accum satfx_t;
+typedef long intfx_t;
+
+SS_FUN (ss_add, +, fx_t, k)
+SS_FUN (ss_sub, -, fx_t, k)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_k (a, X##P##-##16k);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##16k;                    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_k (a, X##P##-##16k);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##16k;                    \
+  }
+#include "vals-k.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_k (a, __ACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __ACCUM_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_k (a, __ACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __ACCUM_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-k.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x40000000, 0x3e800000, 0x3f800000,
+    0x7ffffffe, 0x7fffffff, 0x7f800000,
+    0x7f7f7f7f, 0x7f810080, 0x7f008000,
+    0x7f000001,
+    0x80000000, 0x80000001, 0x80808080,
+    0x80810000, 0x80ffffff, 0x80fffffe,
+    0x81000000, 0x81800000, 0x81800000,
+    0xff000000, 0xffffff01, 0xffffff80,
+    0xffffff7f, 0xff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c
new file mode 100644 (file)
index 0000000..e81cbb1
--- /dev/null
@@ -0,0 +1,108 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef long long _Accum fx_t;
+typedef long long _Sat _Accum satfx_t;
+typedef long long intfx_t;
+
+SS_LFUN (ss_add, +, fx_t, llk, >)
+SS_LFUN (ss_sub, -, fx_t, llk, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_llk (a, X##P##-##48llk);      \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##48llk;                  \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_llk (a, X##P##-##48llk);      \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##48llk;                  \
+  }
+#include "vals-llk.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_llk (a, __LLACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __LLACCUM_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_llk (a, __LLACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __LLACCUM_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-llk.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000000000000000, 0x3e80000000000000, 0x3f80000000000000,
+    0x7ffffffffffffffe, 0x7fffffffffffffff, 0x7f80000000000000,
+    0x7f7f7f7f7f7f7f7f, 0x7f81000000000080, 0x7f00000080000000,
+    0x7f00000000000001,
+    0x8000000000000000, 0x8000000000000001, 0x8080808080808080,
+    0x8081000000000000, 0x80ffffffffffffff, 0x80fffffffffffffe,
+    0x8100000000000000, 0x8180000000000000, 0x818000000000000,
+    0xff00000000000000, 0xffffffffffffff01, 0xffffffffffffff80,
+    0xffffffffffffff7f, 0xff80ff80ff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c
new file mode 100644 (file)
index 0000000..e59bcf6
--- /dev/null
@@ -0,0 +1,107 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef _Fract fx_t;
+typedef _Sat _Fract satfx_t;
+typedef int intfx_t;
+
+SS_FUN (ss_add, +, fx_t, r)
+SS_FUN (ss_sub, -, fx_t, r)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_r (a, X##P##-##15r);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##15r;                    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_r (a, X##P##-##15r);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##15r;                    \
+  }
+#include "vals-r.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_r (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __FRACT_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_r (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __FRACT_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-r.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000, 0x3e80, 0x3f80,
+    0x7ffe, 0x7fff,
+    0x7f7f, 0x7f81, 0x7f80,
+    0x7f01,
+    0x8000, 0x8001, 0x8080,
+    0x8081, 0x80ff, 0x80fe,
+    0x8100, 0x8180, 0x817f,
+    0xff00, 0xff01, 0xff01,
+    0xff7f, 0xff80
+  };
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c
new file mode 100644 (file)
index 0000000..6dd191f
--- /dev/null
@@ -0,0 +1,73 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned short _Fract fx_t;
+typedef unsigned short _Sat _Fract satfx_t;
+typedef unsigned char intfx_t;
+
+US_LFUN (us_add, +, fx_t, uhr, >)
+US_LFUN (us_sub, -, fx_t, uhr, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add2_##N (satfx_t a)               \
+  {                                             \
+    return us_add_uhr (a, X##P##-##8uhr);       \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##8uhr;                   \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub2_##N (satfx_t a)               \
+  {                                             \
+    return us_sub_uhr (a, X##P##-##8uhr);       \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##8uhr;                   \
+  }
+#include "vals-uhr.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-uhr.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, 1, 2, 0x7f, 0x80, 0x81, 0xff,
+    0x40, 0x3e, 0x3f, 0xbf, 0xc0, 0xc1
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c
new file mode 100644 (file)
index 0000000..c9a7cd6
--- /dev/null
@@ -0,0 +1,82 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned _Accum fx_t;
+typedef unsigned _Sat _Accum satfx_t;
+typedef unsigned long intfx_t;
+
+US_LFUN (us_add, +, fx_t, uk, >)
+US_LFUN (us_sub, -, fx_t, uk, <)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add2_##N (satfx_t a)             \
+    {                                           \
+        return us_add_uk (a, X##P##-##16uk);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##16uk;               \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub2_##N (satfx_t a)             \
+    {                                           \
+        return us_sub_uk (a, X##P##-##16uk);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##16uk;               \
+    }
+#include "vals-uk.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-uk.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x40000000, 0x3e800000, 0x3f800000,
+    0x7ffffffe, 0x7fffffff, 0x7f800000,
+    0x7f7f7f7f, 0x7f810080, 0x7f008000,
+    0x7f000001,
+    0x80000000, 0x80000001, 0x80808080,
+    0x80810000, 0x80ffffff, 0x80fffffe,
+    0x81000000, 0x81800000, 0x81800000,
+    0xff000000, 0xffffff01, 0xffffff80,
+    0xffffff7f, 0xff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c
new file mode 100644 (file)
index 0000000..22ebb8a
--- /dev/null
@@ -0,0 +1,82 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned long long _Accum fx_t;
+typedef unsigned long long _Sat _Accum satfx_t;
+typedef unsigned long long intfx_t;
+
+US_LFUN (us_add, +, fx_t, ullk, >)
+US_LFUN (us_sub, -, fx_t, ullk, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add2_##N (satfx_t a)               \
+  {                                             \
+    return us_add_ullk (a, X##P##-##48ullk);    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##48ullk;                 \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub2_##N (satfx_t a)               \
+  {                                             \
+    return us_sub_ullk (a, X##P##-##48ullk);    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##48ullk;                 \
+  }
+#include "vals-ullk.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-ullk.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000000000000000, 0x3e80000000000000, 0x3f80000000000000,
+    0x7ffffffffffffffe, 0x7fffffffffffffff, 0x7f80000000000000,
+    0x7f7f7f7f7f7f7f7f, 0x7f81000000000080, 0x7f00000080000000,
+    0x7f00000000000001,
+    0x8000000000000000, 0x8000000000000001, 0x8080808080808080,
+    0x8081000000000000, 0x80ffffffffffffff, 0x80fffffffffffffe,
+    0x8100000000000000, 0x8180000000000000, 0x818000000000000,
+    0xff00000000000000, 0xffffffffffffff01, 0xffffffffffffff80,
+    0xffffffffffffff7f, 0xff80ff80ff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c b/gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c
new file mode 100644 (file)
index 0000000..bc3c0bb
--- /dev/null
@@ -0,0 +1,82 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fwrapv" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned _Fract fx_t;
+typedef unsigned _Sat _Fract satfx_t;
+typedef unsigned int intfx_t;
+
+US_LFUN (us_add, +, fx_t, ur, >)
+US_LFUN (us_sub, -, fx_t, ur, <)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add2_##N (satfx_t a)             \
+    {                                           \
+        return us_add_ur (a, X##P##-##16ur);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##16ur;               \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub2_##N (satfx_t a)             \
+    {                                           \
+        return us_sub_ur (a, X##P##-##16ur);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##16ur;               \
+    }
+#include "vals-ur.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-ur.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000, 0x3e80, 0x3f80,
+    0x7ffe, 0x7fff,
+    0x7f7f, 0x7f81, 0x7f80,
+    0x7f01,
+    0x8000, 0x8001, 0x8080,
+    0x8081, 0x80ff, 0x80fe,
+    0x8100, 0x8180, 0x817f,
+    0xff00, 0xff01, 0xff01,
+    0xff7f, 0xff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-hr.def b/gcc/testsuite/gcc.target/avr/torture/vals-hr.def
new file mode 100644 (file)
index 0000000..f6619c2
--- /dev/null
@@ -0,0 +1,12 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04,-0x3f)
+VAL (07, 0x40)
+VAL (08,-0x40)
+VAL (10,-0x1)
+VAL (12, 0x3f)
+VAL (13,-0x3f)
+VAL (14, 0x7f)
+VAL (15,-0x7f)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-k.def b/gcc/testsuite/gcc.target/avr/torture/vals-k.def
new file mode 100644 (file)
index 0000000..a490c69
--- /dev/null
@@ -0,0 +1,32 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x40000000)
+VAL (08,-0x40000000)
+
+VAL (10,-0x7fffffff)
+VAL (11, 0x7fffffff)
+VAL (12, 0x7f800000)
+VAL (13,-0x7f800000)
+VAL (14, 0x7f800001)
+VAL (15,-0x7f800001)
+VAL (16, 0x7f7f7f7f)
+VAL (17,-0x7f7f7f7f)
+VAL (18, 0x7f808080)
+VAL (19,-0x7f808080)
+VAL (20, 0x3e800000)
+VAL (21,-0x3e800000)
+VAL (22, 0x3f800000)
+VAL (23,-0x3f800000)
+VAL (24, 0x400000)
+VAL (25,-0x400000)
+VAL (26, 0x3f000000)
+VAL (27,-0x3f000000)
+VAL (28, 0xffff00)
+VAL (29,-0xffff00)
+VAL (30, 0x00ff00ff)
+VAL (31,-0x00ff00ff)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-llk.def b/gcc/testsuite/gcc.target/avr/torture/vals-llk.def
new file mode 100644 (file)
index 0000000..726a7eb
--- /dev/null
@@ -0,0 +1,32 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x4000000000000000)
+VAL (08,-0x4000000000000000)
+
+VAL (10,-0x7fffffffffffffff)
+VAL (11, 0x7fffffffffffffff)
+VAL (12, 0x7f80000000000000)
+VAL (13,-0x7f80000000000000)
+VAL (14, 0x7f80000000000001)
+VAL (15,-0x7f80000000000001)
+VAL (16, 0x7f7f7f7f7f7f7f7f)
+VAL (17,-0x7f7f7f7f7f7f7f7f)
+VAL (18, 0x7f80808080808000)
+VAL (19,-0x7f80808080808000)
+VAL (20, 0x3e80000000000000)
+VAL (21,-0x3e80000000000000)
+VAL (22, 0x3f80000000000000)
+VAL (23,-0x3f80000000000000)
+VAL (24, 0x40000000000000)
+VAL (25,-0x40000000000000)
+VAL (26, 0x3f000000000000)
+VAL (27,-0x3f000000000000)
+VAL (28, 0xffffff00)
+VAL (29,-0xffffff00)
+VAL (30, 0x00ff00ff00ff00ff)
+VAL (31,-0x00ff00ff00ff00ff)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-r.def b/gcc/testsuite/gcc.target/avr/torture/vals-r.def
new file mode 100644 (file)
index 0000000..0c5f83f
--- /dev/null
@@ -0,0 +1,30 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x4000)
+VAL (08,-0x4000)
+
+VAL (10,-0x7fff)
+VAL (11, 0x7fff)
+VAL (12, 0x7f80)
+VAL (13,-0x7f80)
+VAL (14, 0x7f81)
+VAL (15,-0x7f81)
+VAL (16, 0x7f7f)
+VAL (17,-0x7f7f)
+VAL (18, 0x7f80)
+VAL (19,-0x7f80)
+VAL (20, 0x3e80)
+VAL (21,-0x3e80)
+VAL (22, 0x3f80)
+VAL (23,-0x3f80)
+VAL (24, 0x40)
+VAL (25,-0x40)
+VAL (26, 0x3f00)
+VAL (27,-0x3f00)
+VAL (30, 0x00ff)
+VAL (31,-0x00ff)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-uhr.def b/gcc/testsuite/gcc.target/avr/torture/vals-uhr.def
new file mode 100644 (file)
index 0000000..7144156
--- /dev/null
@@ -0,0 +1,13 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (07, 0x40)
+VAL (08, 0xc0)
+VAL (10, 0xc1)
+VAL (12, 0xff)
+VAL (14, 0x7f)
+VAL (16, 0x81)
+VAL (20, 0xbf)
+
+VAL (99, 0x80)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-uk.def b/gcc/testsuite/gcc.target/avr/torture/vals-uk.def
new file mode 100644 (file)
index 0000000..3e21283
--- /dev/null
@@ -0,0 +1,23 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x40000000)
+VAL (08, 0xc0000000)
+VAL (10, 0x7fffffff)
+VAL (12, 0x7f800000)
+VAL (14, 0x7f800001)
+VAL (16, 0x7f7f7f7f)
+VAL (18, 0x7f808000)
+VAL (20, 0x3e800000)
+VAL (22, 0x3f800000)
+VAL (24, 0x40000000)
+VAL (26, 0x3f000000)
+VAL (28, 0xffff00)
+VAL (30, 0x00ff00ff)
+VAL (31, 0xff00ff00)
+VAL (32, 0x10000000)
+VAL (33, 0xff000000)
+
+VAL (99, 0x80000000)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-ullk.def b/gcc/testsuite/gcc.target/avr/torture/vals-ullk.def
new file mode 100644 (file)
index 0000000..620182b
--- /dev/null
@@ -0,0 +1,20 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x4000000000000000)
+VAL (08, 0x4000000000000000)
+VAL (10, 0x7fffffffffffffff)
+VAL (12, 0x7f80000000000000)
+VAL (14, 0x7f80000000000001)
+VAL (16, 0x7f7f7f7f7f7f7f7f)
+VAL (18, 0x7f80808080808000)
+VAL (20, 0x3e80000000000000)
+VAL (22, 0x3f80000000000000)
+VAL (24, 0x40000000000000)
+VAL (26, 0x3f000000000000)
+VAL (28, 0xffffff00)
+VAL (30, 0x00ff00ff00ff00ff)
+
+VAL (99, 0x8000000000000000)
+
diff --git a/gcc/testsuite/gcc.target/avr/torture/vals-ur.def b/gcc/testsuite/gcc.target/avr/torture/vals-ur.def
new file mode 100644 (file)
index 0000000..d6ea8f1
--- /dev/null
@@ -0,0 +1,17 @@
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x4000)
+VAL (08, 0xc000)
+VAL (10, 0x7fff)
+VAL (12, 0x7f80)
+VAL (14, 0x7f81)
+VAL (16, 0x7f7f)
+VAL (20, 0x3e80)
+VAL (22, 0x3f80)
+VAL (26, 0x3f00)
+VAL (32, 0x100)
+
+VAL (99, 0x8000)
+
index a225e58..2045aac 100644 (file)
@@ -1,3 +1,20 @@
+2012-09-15  Georg-Johann Lay  <avr@gjlay.de>
+
+       PR target/54222
+       * config/avr/lib1funcs-fixed.S (__ssneg_2, __ssabs_2, __ssneg_4,
+       __ssabs_4, __clr_8, __ssneg_8, __ssabs_8,
+       __usadd_8, __ussub_8, __ssadd_8, __sssub_8): New functions.
+       (__divsa3): Use __negsi2 to negate r_quoL.
+       * config/avr/lib1funcs.S (FALIAS): New macro.
+       (__divmodsi4): Break out and use __divmodsi4_neg1 as...
+       (__negsi2): ...this new function.
+       * config/avr/t-avr (LIB1ASMFUNCS): Add _negsi2, _clr_8,
+       _ssneg_2, _ssneg_4, _ssneg_8, _ssabs_2, _ssabs_4,
+       _ssabs_8, _ssadd_8, _sssub_8, _usadd_8, _ussub_8.
+       (LIB2FUNCS_EXCLUDE): Fix typo for _add _sub.
+       Add: _ssadd*, _sssub*, _ssneg*, _ssabs* for signed fixed modes.
+       Add: _usadd*, _ussub*, _usneg* for unsigned fixed modes.
+
 2012-09-10  Oleg Endo  <olegendo@gcc.gnu.org>
 
        PR target/54089
index c1aff53..ddcd02e 100644 (file)
@@ -808,8 +808,8 @@ DEFUN   __divsa3
     XCALL   __udivusa3
     sbrs    r0, 7   ; negate result if needed
     ret
-    NEG4    r_quoL
-    ret
+    ;; negate r_quoL
+    XJMP    __negsi2
 ENDF __divsa3
 #endif  /* defined (L_divsa3) */
 
@@ -872,3 +872,223 @@ ENDF __udivusa3
 #undef  r_divHL
 #undef  r_divHH
 #undef  r_cnt
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturation, 2 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; First Argument and Return Register
+#define A0  24
+#define A1  A0+1
+
+#if defined (L_ssneg_2)
+DEFUN __ssneg_2
+    NEG2    A0
+    brvc 0f
+    sbiw    A0, 1
+0:  ret
+ENDF __ssneg_2
+#endif /* L_ssneg_2 */
+
+#if defined (L_ssabs_2)
+DEFUN __ssabs_2
+    sbrs    A1, 7
+    ret
+    XJMP    __ssneg_2
+ENDF __ssabs_2
+#endif /* L_ssabs_2 */
+
+#undef A0
+#undef A1
+
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturation, 4 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; First Argument and Return Register
+#define A0  22
+#define A1  A0+1
+#define A2  A0+2
+#define A3  A0+3
+
+#if defined (L_ssneg_4)
+DEFUN __ssneg_4
+    XCALL   __negsi2
+    brvc 0f
+    ldi     A3, 0x7f
+    ldi     A2, 0xff
+    ldi     A1, 0xff
+    ldi     A0, 0xff
+0:  ret
+ENDF __ssneg_4
+#endif /* L_ssneg_4 */
+
+#if defined (L_ssabs_4)
+DEFUN __ssabs_4
+    sbrs    A3, 7
+    ret
+    XJMP    __ssneg_4
+ENDF __ssabs_4
+#endif /* L_ssabs_4 */
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturation, 8 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; First Argument and Return Register
+#define A0  18
+#define A1  A0+1
+#define A2  A0+2
+#define A3  A0+3
+#define A4  A0+4
+#define A5  A0+5
+#define A6  A0+6
+#define A7  A0+7
+
+#if defined (L_clr_8)
+FALIAS __usneguta2
+FALIAS __usneguda2
+FALIAS __usnegudq2
+
+;; Clear Carry and all Bytes
+DEFUN __clr_8
+    ;; Clear Carry and set Z
+    sub     A7, A7
+    ;; FALLTHRU
+ENDF  __clr_8
+;; Propagate Carry to all Bytes, Carry unaltered
+DEFUN __sbc_8
+    sbc     A7, A7
+    sbc     A6, A6
+    wmov    A4, A6
+    wmov    A2, A6
+    wmov    A0, A6
+    ret
+ENDF __sbc_8
+#endif /* L_clr_8 */
+
+#if defined (L_ssneg_8)
+FALIAS __ssnegta2
+FALIAS __ssnegda2
+FALIAS __ssnegdq2
+
+DEFUN __ssneg_8
+    XCALL   __negdi2
+    brvc 0f
+    ;; A[] = 0x7fffffff
+    sec
+    XCALL   __sbc_8
+    ldi     A7, 0x7f
+0:  ret
+ENDF __ssneg_8
+#endif /* L_ssneg_8 */
+
+#if defined (L_ssabs_8)
+FALIAS __ssabsta2
+FALIAS __ssabsda2
+FALIAS __ssabsdq2
+
+DEFUN __ssabs_8
+    sbrs    A7, 7
+    ret
+    XJMP    __ssneg_8
+ENDF __ssabs_8
+#endif /* L_ssabs_8 */
+
+;; Second Argument
+#define B0  10
+#define B1  B0+1
+#define B2  B0+2
+#define B3  B0+3
+#define B4  B0+4
+#define B5  B0+5
+#define B6  B0+6
+#define B7  B0+7
+
+#if defined (L_usadd_8)
+FALIAS __usadduta3
+FALIAS __usadduda3
+FALIAS __usaddudq3
+
+DEFUN __usadd_8
+    XCALL   __adddi3
+    brcs 0f
+    ret
+       ;; A[] = 0xffffffff
+0:  XJMP    __sbc_8
+ENDF __usadd_8
+#endif /* L_usadd_8 */
+
+#if defined (L_ussub_8)
+FALIAS __ussubuta3
+FALIAS __ussubuda3
+FALIAS __ussubudq3
+
+DEFUN __ussub_8
+    XCALL   __subdi3
+    brcs 0f
+    ret
+       ;; A[] = 0
+0:  XJMP    __clr_8
+ENDF __ussub_8
+#endif /* L_ussub_8 */
+
+#if defined (L_ssadd_8)
+FALIAS __ssaddta3
+FALIAS __ssaddda3
+FALIAS __ssadddq3
+
+DEFUN __ssadd_8
+    ;; A = (B >= 0) ? INT64_MAX : INT64_MIN
+    XCALL   __adddi3
+    brvc 0f
+    cpi     B7, 0x80
+    XCALL   __sbc_8
+    subi    A7, 0x80
+0:  ret
+ENDF __ssadd_8
+#endif /* L_ssadd_8 */
+
+#if defined (L_sssub_8)
+FALIAS __sssubta3
+FALIAS __sssubda3
+FALIAS __sssubdq3
+
+DEFUN __sssub_8
+    XCALL   __subdi3
+    brvc 0f
+       ;; A = (B < 0) ? INT64_MAX : INT64_MIN
+    ldi     A7, 0x7f
+    cp      A7, B7
+    XCALL   __sbc_8
+    subi    A7, 0x80
+0:  ret
+ENDF __sssub_8
+#endif /* L_sssub_8 */
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+#undef A4
+#undef A5
+#undef A6
+#undef A7
+#undef B0
+#undef B1
+#undef B2
+#undef B3
+#undef B4
+#undef B5
+#undef B6
+#undef B7
index 6b9879e..ad97918 100644 (file)
@@ -91,6 +91,14 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 .endfunc
 .endm
 
+.macro FALIAS name
+.global \name
+.func \name
+\name:
+.size \name, .-\name
+.endfunc
+.endm
+
 ;; Negate a 2-byte value held in consecutive registers
 .macro NEG2  reg
     com     \reg+1
@@ -99,6 +107,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 .endm
 
 ;; Negate a 4-byte value held in consecutive registers
+;; Sets the V flag for signed overflow tests if REG >= 16
 .macro NEG4  reg
     com     \reg+3
     com     \reg+2
@@ -1325,7 +1334,7 @@ DEFUN __divmodsi4
     bst     r_arg1HH,7          ; store sign of dividend
     brtc    0f
     com     __tmp_reg__         ; r0.7 is sign of result
-    rcall   __divmodsi4_neg1    ; dividend negative: negate
+    XCALL   __negsi2            ; dividend negative: negate
 0:
     sbrc    r_arg2HH,7
     rcall   __divmodsi4_neg2    ; divisor negative: negate
@@ -1333,16 +1342,7 @@ DEFUN __divmodsi4
     sbrc    __tmp_reg__, 7      ; correct quotient sign
     rcall   __divmodsi4_neg2
     brtc    __divmodsi4_exit    ; correct remainder sign
-__divmodsi4_neg1:
-    ;; correct dividend/remainder sign
-    com     r_arg1HH
-    com     r_arg1HL
-    com     r_arg1H
-    neg     r_arg1L
-    sbci    r_arg1H, 0xff
-    sbci    r_arg1HL,0xff
-    sbci    r_arg1HH,0xff
-    ret
+    XJMP    __negsi2
 __divmodsi4_neg2:
     ;; correct divisor/quotient sign
     com     r_arg2HH
@@ -1357,6 +1357,16 @@ __divmodsi4_exit:
 ENDF __divmodsi4
 #endif /* defined (L_divmodsi4) */
 
+#if defined (L_negsi2)
+;; (set (reg:SI 22)
+;;      (neg:SI (reg:SI 22)))
+;; Sets the V flag for signed overflow tests
+DEFUN __negsi2
+    NEG4    22
+    ret
+ENDF __negsi2
+#endif /* L_negsi2 */
+
 #undef r_remHH
 #undef r_remHL
 #undef r_remH
@@ -1689,6 +1699,8 @@ ENDF __divdi3_moddi3
 ;; (set (reg:DI 18)
 ;;      (plus:DI (reg:DI 18)
 ;;               (reg:DI 10)))
+;; Sets the V flag for signed overflow tests
+;; Sets the C flag for unsigned overflow tests
 DEFUN __adddi3
     ADD A0,B0  $  adc A1,B1  $  adc A2,B2  $  adc A3,B3
     adc A4,B4  $  adc A5,B5  $  adc A6,B6  $  adc A7,B7
@@ -1700,6 +1712,8 @@ ENDF __adddi3
 ;; (set (reg:DI 18)
 ;;      (plus:DI (reg:DI 18)
 ;;               (sign_extend:SI (reg:QI 26))))
+;; Sets the V flag for signed overflow tests
+;; Sets the C flag for unsigned overflow tests provided 0 <= R26 < 128
 DEFUN __adddi3_s8
     clr     TT
     sbrc    r26, 7
@@ -1714,6 +1728,8 @@ ENDF __adddi3_s8
 ;; (set (reg:DI 18)
 ;;      (minus:DI (reg:DI 18)
 ;;                (reg:DI 10)))
+;; Sets the V flag for signed overflow tests
+;; Sets the C flag for unsigned overflow tests
 DEFUN __subdi3
     SUB A0,B0  $  sbc A1,B1  $  sbc A2,B2  $  sbc A3,B3
     sbc A4,B4  $  sbc A5,B5  $  sbc A6,B6  $  sbc A7,B7
@@ -1747,6 +1763,9 @@ ENDF __cmpdi2_s8
 #endif /* L_cmpdi2_s8 */
 
 #if defined (L_negdi2)
+;; (set (reg:DI 18)
+;;      (neg:DI (reg:DI 18)))
+;; Sets the V flag for signed overflow tests
 DEFUN __negdi2
 
     com  A4    $  com  A5    $  com  A6    $  com  A7
index 7496133..d1f55e4 100644 (file)
@@ -20,7 +20,7 @@ LIB1ASMFUNCS = \
        _divdi3 _udivdi3 \
        _muldi3 \
        _udivmod64 \
-       _negdi2 \
+       _negsi2 _negdi2 \
        _prologue \
        _epilogue \
        _exit \
@@ -72,7 +72,12 @@ LIB1ASMFUNCS += \
        _divqq3 _udivuqq3 \
        _divhq3 _udivuhq3 \
        _divha3 _udivuha3 \
-       _divsa3 _udivusa3
+       _divsa3 _udivusa3 \
+       _clr_8 \
+       _ssneg_2 _ssneg_4 _ssneg_8 \
+       _ssabs_2 _ssabs_4 _ssabs_8 \
+       _ssadd_8 _sssub_8 \
+       _usadd_8 _ussub_8
 
 LIB2FUNCS_EXCLUDE = \
        _moddi3 _umoddi3 \
@@ -103,6 +108,7 @@ endif
 
 
 # Filter out supported conversions from fixed-bit.c
+# Also filter out TQ and UTQ.
 
 conv_XY=$(conv)$(mode1)$(mode2)
 func_X=$(func)$(mode)
@@ -141,8 +147,20 @@ allfix_modes = QQ UQQ HQ UHQ HA UHA SQ USQ SA USA DA UDA DQ UDQ TQ UTQ TA UTA
 
 LIB2FUNCS_EXCLUDE += \
        $(foreach func,_add _sub,\
-       $(foreach mode,$(allfix_modes),$(func_X)3))
+       $(foreach mode,$(allfix_modes),$(func_X)))
 
 LIB2FUNCS_EXCLUDE += \
        $(foreach func,_lshr _ashl _ashr _cmp,\
        $(foreach mode,$(allfix_modes),$(func_X)))
+
+
+usat_modes = UQQ UHQ UHA USQ USA UDQ UDA UTQ UTA
+ssat_modes =  QQ  HQ  HA  SQ  SA  DQ  DA  TQ  TA
+
+LIB2FUNCS_EXCLUDE += \
+       $(foreach func,_ssadd _sssub _ssneg _ssabs,\
+       $(foreach mode,$(ssat_modes),$(func_X)))
+
+LIB2FUNCS_EXCLUDE += \
+       $(foreach func,_usadd _ussub _usneg,\
+       $(foreach mode,$(usat_modes),$(func_X)))