From b4ebb666446595021ea12a3ba435bf07268d3385 Mon Sep 17 00:00:00 2001 From: gjl Date: Sat, 15 Sep 2012 15:52:28 +0000 Subject: [PATCH] gcc/ PR target/54222 * config/avr/avr-fixed.md (ALL2S, ALL4S, ALL24S, ALL124S, ALL124U): New mode iterators. (3): New insns for SS_PLUS, SS_MINUS. (3): New insns for US_PLUS, US_MINUS. (usneg2): New insns. (2): New expanders for SS_NEG, SS_ABS. (*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. (3): New expanders for SS_PLUS, SS_MINUS. (3): New expanders for US_PLUS, US_MINUS. (3_insn): New insns. (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. (*add3, add3_clobber, add3, addpsi3, sub3): 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 --- gcc/ChangeLog | 41 ++ gcc/config/avr/avr-dimode.md | 111 +++- gcc/config/avr/avr-fixed.md | 112 +++++ gcc/config/avr/avr-protos.h | 6 +- gcc/config/avr/avr.c | 557 ++++++++++++++++----- gcc/config/avr/avr.md | 86 ++-- gcc/testsuite/ChangeLog | 21 + gcc/testsuite/gcc.target/avr/torture/fix-types.h | 134 +++++ .../gcc.target/avr/torture/sat-hr-plus-minus.c | 98 ++++ .../gcc.target/avr/torture/sat-k-plus-minus.c | 108 ++++ .../gcc.target/avr/torture/sat-llk-plus-minus.c | 108 ++++ .../gcc.target/avr/torture/sat-r-plus-minus.c | 107 ++++ .../gcc.target/avr/torture/sat-uhr-plus-minus.c | 73 +++ .../gcc.target/avr/torture/sat-uk-plus-minus.c | 82 +++ .../gcc.target/avr/torture/sat-ullk-plus-minus.c | 82 +++ .../gcc.target/avr/torture/sat-ur-plus-minus.c | 82 +++ gcc/testsuite/gcc.target/avr/torture/vals-hr.def | 12 + gcc/testsuite/gcc.target/avr/torture/vals-k.def | 32 ++ gcc/testsuite/gcc.target/avr/torture/vals-llk.def | 32 ++ gcc/testsuite/gcc.target/avr/torture/vals-r.def | 30 ++ gcc/testsuite/gcc.target/avr/torture/vals-uhr.def | 13 + gcc/testsuite/gcc.target/avr/torture/vals-uk.def | 23 + gcc/testsuite/gcc.target/avr/torture/vals-ullk.def | 20 + gcc/testsuite/gcc.target/avr/torture/vals-ur.def | 17 + libgcc/ChangeLog | 17 + libgcc/config/avr/lib1funcs-fixed.S | 224 ++++++++- libgcc/config/avr/lib1funcs.S | 41 +- libgcc/config/avr/t-avr | 24 +- 28 files changed, 2087 insertions(+), 206 deletions(-) create mode 100644 gcc/testsuite/gcc.target/avr/torture/fix-types.h create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-hr.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-k.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-llk.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-r.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-uhr.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-uk.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-ullk.def create mode 100644 gcc/testsuite/gcc.target/avr/torture/vals-ur.def diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ae8bf3443d2..a5e3018802f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,44 @@ +2012-09-15 Georg-Johann Lay + + PR target/54222 + * config/avr/avr-fixed.md (ALL2S, ALL4S, ALL24S, ALL124S, + ALL124U): New mode iterators. + (3): New insns for SS_PLUS, SS_MINUS. + (3): New insns for US_PLUS, US_MINUS. + (usneg2): New insns. + (2): New expanders for SS_NEG, SS_ABS. + (*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. + (3): New expanders for SS_PLUS, SS_MINUS. + (3): New expanders for US_PLUS, US_MINUS. + (3_insn): New insns. + (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. + (*add3, add3_clobber, add3, addpsi3, sub3): + 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 * 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 ed5752319eb..31527dbf041 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 "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, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +(define_insn "3_insn" + [(set (reg:ALL8S ACC_A) + (ss_addsub:ALL8S (reg:ALL8S ACC_A) + (reg:ALL8S ACC_B)))] + "avr_have_dimode" + "%~call __3" + [(set_attr "adjust_len" "call") + (set_attr "cc" "clobber")]) + +(define_insn "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 "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, ACC_A); + + emit_move_insn (acc_a, operands[1]); + + if (const_operand (operands[2], GET_MODE (operands[2]))) + { + emit_insn (gen_3_const_insn (operands[2])); + } + else + { + emit_move_insn (gen_rtx_REG (mode, ACC_B), operands[2]); + emit_insn (gen_3_insn ()); + } + + emit_move_insn (operands[0], acc_a); + DONE; + }) + +(define_insn "3_insn" + [(set (reg:ALL8U ACC_A) + (us_addsub:ALL8U (reg:ALL8U ACC_A) + (reg:ALL8U ACC_B)))] + "avr_have_dimode" + "%~call __3" + [(set_attr "adjust_len" "call") (set_attr "cc" "clobber")]) +(define_insn "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 bfbdaecf215..5a994992150 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 "3" + [(set (match_operand:ALL124S 0 "register_operand" "=??d,d") + (ss_addsub:ALL124S (match_operand:ALL124S 1 "register_operand" "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 "3" + [(set (match_operand:ALL124U 0 "register_operand" "=??r,d") + (us_addsub:ALL124U (match_operand:ALL124U 1 "register_operand" "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 "usneg2" + [(parallel [(match_operand:ALL124U 0 "register_operand" "") + (match_operand:ALL124U 1 "nonmemory_operand" "")])] + "" + { + emit_move_insn (operands[0], CONST0_RTX (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 "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, 26 - GET_MODE_SIZE (mode)); + }) + +;; "*ssneghq2" "*ssnegha2" +;; "*ssabshq2" "*ssabsha2" +(define_insn "*2" + [(set (reg:ALL2S 24) + (ss_abs_neg:ALL2S (reg:ALL2S 24)))] + "" + "%~call ___2" + [(set_attr "type" "xcall") + (set_attr "cc" "clobber")]) + +;; "*ssnegsq2" "*ssnegsa2" +;; "*ssabssq2" "*ssabssa2" +(define_insn "*2" + [(set (reg:ALL4S 22) + (ss_abs_neg:ALL4S (reg:ALL4S 22)))] + "" + "%~call ___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 5d6fabb6b6d..690c89bcf01 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 e69e35aaecf..c46f488603f 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 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 . + + + 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 6146fe6a013..8b13010e700 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 9a2ef739c88..89b65e035b2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,24 @@ +2012-09-15 Georg-Johann Lay + + 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 * 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 00000000000..f6a2aeb6fa2 --- /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 00000000000..1e6215e4f41 --- /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 00000000000..8a26ffeeb3e --- /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 00000000000..e81cbb187e0 --- /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 00000000000..e59bcf655ab --- /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 00000000000..6dd191f7ac3 --- /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 00000000000..c9a7cd6ba4d --- /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 00000000000..22ebb8af50f --- /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 00000000000..bc3c0bbcd94 --- /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 00000000000..f6619c2ff57 --- /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 00000000000..a490c69b366 --- /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 00000000000..726a7ebed35 --- /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 00000000000..0c5f83f7d02 --- /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 00000000000..71441567ca5 --- /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 00000000000..3e212836f4d --- /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 00000000000..620182be640 --- /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 00000000000..d6ea8f1c50d --- /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 a225e5875e0..2045aac2803 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,20 @@ +2012-09-15 Georg-Johann Lay + + 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 PR target/54089 diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S index c1aff53d5fd..ddcd02e197c 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 6b9879ee7d7..ad979189306 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 749613376b3..d1f55e408d3 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))) -- 2.11.4.GIT