aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog41
-rw-r--r--gcc/config/avr/avr-dimode.md111
-rw-r--r--gcc/config/avr/avr-fixed.md112
-rw-r--r--gcc/config/avr/avr-protos.h6
-rw-r--r--gcc/config/avr/avr.c557
-rw-r--r--gcc/config/avr/avr.md86
-rw-r--r--gcc/testsuite/ChangeLog21
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/fix-types.h134
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c98
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c108
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c108
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c107
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c73
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c82
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c82
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c82
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-hr.def12
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-k.def32
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-llk.def32
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-r.def30
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-uhr.def13
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-uk.def23
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-ullk.def20
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/vals-ur.def17
-rw-r--r--libgcc/ChangeLog17
-rw-r--r--libgcc/config/avr/lib1funcs-fixed.S224
-rw-r--r--libgcc/config/avr/lib1funcs.S41
-rw-r--r--libgcc/config/avr/t-avr24
28 files changed, 2087 insertions, 206 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index ae8bf34..a5e3018 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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,
diff --git a/gcc/config/avr/avr-dimode.md b/gcc/config/avr/avr-dimode.md
index ed57523..31527db 100644
--- a/gcc/config/avr/avr-dimode.md
+++ b/gcc/config/avr/avr-dimode.md
@@ -48,10 +48,10 @@
(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
@@ -124,9 +124,9 @@
"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")])
@@ -185,11 +185,106 @@
(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
diff --git a/gcc/config/avr/avr-fixed.md b/gcc/config/avr/avr-fixed.md
index bfbdaec..5a99499 100644
--- a/gcc/config/avr/avr-fixed.md
+++ b/gcc/config/avr/avr-fixed.md
@@ -29,6 +29,12 @@
(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
@@ -72,6 +78,112 @@
(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"
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 5d6fabb..690c89b 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -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*);
diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c
index e69e35a..c46f488 100644
--- a/gcc/config/avr/avr.c
+++ b/gcc/config/avr/avr.c
@@ -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;
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 6146fe6..8b13010 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -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"
@@ -138,8 +138,7 @@
;; 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,
@@ -250,6 +249,10 @@
(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")
@@ -268,6 +271,10 @@
[(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")
@@ -275,7 +282,10 @@
(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.
@@ -1181,17 +1191,11 @@
(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
@@ -1238,13 +1242,11 @@
(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"
@@ -1257,14 +1259,11 @@
(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")
@@ -1318,22 +1317,11 @@
(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")
@@ -1401,10 +1389,10 @@
(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")
@@ -1438,14 +1426,10 @@
(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")
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 9a2ef73..89b65e0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..f6a2aeb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/fix-types.h
@@ -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
index 0000000..1e6215e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c
@@ -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
index 0000000..8a26ffe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c
@@ -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
index 0000000..e81cbb1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c
@@ -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
index 0000000..e59bcf6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c
@@ -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
index 0000000..6dd191f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c
@@ -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
index 0000000..c9a7cd6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c
@@ -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
index 0000000..22ebb8a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c
@@ -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
index 0000000..bc3c0bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c
@@ -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
index 0000000..f6619c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-hr.def
@@ -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
index 0000000..a490c69
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-k.def
@@ -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
index 0000000..726a7eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-llk.def
@@ -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
index 0000000..0c5f83f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-r.def
@@ -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
index 0000000..7144156
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-uhr.def
@@ -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
index 0000000..3e21283
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-uk.def
@@ -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
index 0000000..620182b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-ullk.def
@@ -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
index 0000000..d6ea8f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/vals-ur.def
@@ -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)
+
diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog
index a225e58..2045aac 100644
--- a/libgcc/ChangeLog
+++ b/libgcc/ChangeLog
@@ -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
diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S
index c1aff53..ddcd02e 100644
--- a/libgcc/config/avr/lib1funcs-fixed.S
+++ b/libgcc/config/avr/lib1funcs-fixed.S
@@ -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
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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
diff --git a/libgcc/config/avr/lib1funcs.S b/libgcc/config/avr/lib1funcs.S
index 6b9879e..ad97918 100644
--- a/libgcc/config/avr/lib1funcs.S
+++ b/libgcc/config/avr/lib1funcs.S
@@ -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
diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr
index 7496133..d1f55e4 100644
--- a/libgcc/config/avr/t-avr
+++ b/libgcc/config/avr/t-avr
@@ -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)))