diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 981d4895cd545a8dd2bedcf20473581c2043187e..1810dbeccb8a04dc48be1713ee0816089aa75fba 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2013-07-19 Georg-Johann Lay <avr@gjlay.de> + + PR target/57516 + * config/avr/avr-fixed.md (round<mode>3_const): Turn expander to insn. + * config/avr/avr.md (adjust_len): Add `round'. + * config/avr/avr-protos.h (avr_out_round): New prototype. + (avr_out_plus): Add `out_label' argument. + * config/avr/avr.c (avr_out_plus_1): Add `out_label' argument. + (avr_out_plus): Pass down `out_label' to avr_out_plus_1. + Handle the case where `insn' is just a pattern. + (avr_out_bitop): Handle the case where `insn' is just a pattern. + (avr_out_round): New function. + (avr_adjust_insn_length): Handle ADJUST_LEN_ROUND. + 2013-07-18 David Holsgrove <david.holsgrove@xilinx.com> * config/microblaze/microblaze.c (microblaze_expand_prologue): diff --git a/gcc/config/avr/avr-fixed.md b/gcc/config/avr/avr-fixed.md index 7d9b525ef6cfc06edec53b63990a1b7ba3714321..b2f0b9aa144ec383175f231ae549b5ee24da5de8 100644 --- a/gcc/config/avr/avr-fixed.md +++ b/gcc/config/avr/avr-fixed.md @@ -447,49 +447,18 @@ ;; "roundqq3_const" "rounduqq3_const" ;; "roundhq3_const" "rounduhq3_const" "roundha3_const" "rounduha3_const" ;; "roundsq3_const" "roundusq3_const" "roundsa3_const" "roundusa3_const" -(define_expand "round<mode>3_const" - [(parallel [(match_operand:ALL124QA 0 "register_operand" "") - (match_operand:ALL124QA 1 "register_operand" "") - (match_operand:HI 2 "const_int_operand" "")])] +(define_insn "round<mode>3_const" + [(set (match_operand:ALL124QA 0 "register_operand" "=d") + (unspec:ALL124QA [(match_operand:ALL124QA 1 "register_operand" "0") + (match_operand:HI 2 "const_int_operand" "n") + (const_int 0)] + UNSPEC_ROUND))] "" { - // The rounding point RP is $2. The smallest fractional - // bit that is not cleared by the rounding is 2^(-RP). - - enum machine_mode imode = int_mode_for_mode (<MODE>mode); - int fbit = (int) GET_MODE_FBIT (<MODE>mode); - - // Add-Saturate 1/2 * 2^(-RP) - - double_int i_add = double_int_zero.set_bit (fbit-1 - INTVAL (operands[2])); - rtx x_add = const_fixed_from_double_int (i_add, <MODE>mode); - - if (SIGNED_FIXED_POINT_MODE_P (<MODE>mode)) - emit_move_insn (operands[0], - gen_rtx_SS_PLUS (<MODE>mode, operands[1], x_add)); - else - emit_move_insn (operands[0], - gen_rtx_US_PLUS (<MODE>mode, operands[1], x_add)); - - // Keep all bits from RP and higher: ... 2^(-RP) - // Clear all bits from RP+1 and lower: 2^(-RP-1) ... - // Rounding point ^^^^^^^ - // Added above ^^^^^^^^^ - - rtx xreg = simplify_gen_subreg (imode, operands[0], <MODE>mode, 0); - rtx xmask = immed_double_int_const (-i_add - i_add, imode); - - if (SImode == imode) - emit_insn (gen_andsi3 (xreg, xreg, xmask)); - else if (HImode == imode) - emit_insn (gen_andhi3 (xreg, xreg, xmask)); - else if (QImode == imode) - emit_insn (gen_andqi3 (xreg, xreg, xmask)); - else - gcc_unreachable(); - - DONE; - }) + return avr_out_round (insn, operands); + } + [(set_attr "cc" "clobber") + (set_attr "adjust_len" "round")]) ;; "*roundqq3.libgcc" "*rounduqq3.libgcc" diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 5246d063799f7b3b69af04f7a9f66a89d65aa006..21ad26a6f3420ec2e351194615f59f4936560f6e 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -86,7 +86,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, rtx*, int* =NULL, int* =NULL); +extern const char* avr_out_plus (rtx, rtx*, int* =NULL, int* =NULL, bool =true); +extern const char* avr_out_round (rtx, rtx*, int* =NULL); extern const char* avr_out_addto_sp (rtx*, int*); extern const char* avr_out_xload (rtx, rtx*, int*); extern const char* avr_out_movmem (rtx, rtx*, int*); diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index f8a43e5715e04c9d9321c53098b48ea4806719a5..50100bf55134d0882dbb762ce2a94282c2ad5662 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -6232,11 +6232,14 @@ lshrsi3_out (rtx insn, rtx operands[], int *len) the subtrahend in the original insn, provided it is a compile time constant. In all other cases, SIGN is 0. - Return "". */ + If OUT_LABEL is true, print the final 0: label which is needed for + saturated addition / subtraction. The only case where OUT_LABEL = false + is useful is for saturated addition / subtraction performed during + fixed-point rounding, cf. `avr_out_round'. */ static void avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc, - enum rtx_code code_sat = UNKNOWN, int sign = 0) + enum rtx_code code_sat, int sign, bool out_label) { /* MODE of the operation. */ enum machine_mode mode = GET_MODE (xop[0]); @@ -6675,7 +6678,8 @@ avr_out_plus_1 (rtx *xop, int *plen, enum rtx_code code, int *pcc, "mov %r0+5,%0", xop, plen, 4); } - avr_asm_len ("0:", op, plen, 0); + if (out_label) + avr_asm_len ("0:", op, plen, 0); } @@ -6713,8 +6717,8 @@ avr_out_plus_symbol (rtx *xop, enum rtx_code code, int *plen, int *pcc) /* 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. + INSN is a single_set insn or an insn pattern 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]. @@ -6729,19 +6733,22 @@ avr_out_plus_symbol (rtx *xop, enum rtx_code code, int *plen, int *pcc) PLEN and PCC default to NULL. + OUT_LABEL defaults to TRUE. For a description, see AVR_OUT_PLUS_1. + Return "" */ const char* -avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) +avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc, bool out_label) { int cc_plus, cc_minus, cc_dummy; int len_plus, len_minus; rtx op[4]; - rtx xdest = SET_DEST (single_set (insn)); + rtx xpattern = INSN_P (insn) ? single_set (insn) : insn; + rtx xdest = SET_DEST (xpattern); 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_sat = GET_CODE (SET_SRC (xpattern)); enum rtx_code code = (PLUS == code_sat || SS_PLUS == code_sat || US_PLUS == code_sat ? PLUS : MINUS); @@ -6756,7 +6763,7 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) if (n_bytes <= 4 && REG_P (xop[2])) { - avr_out_plus_1 (xop, plen, code, pcc, code_sat); + avr_out_plus_1 (xop, plen, code, pcc, code_sat, 0, out_label); return ""; } @@ -6783,7 +6790,8 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) /* 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; + xpattern = INSN_P (insn) ? PATTERN (insn) : insn; + op[3] = PARALLEL == GET_CODE (xpattern) ? xop[3] : NULL_RTX; /* Saturation will need the sign of the original operand. */ @@ -6798,8 +6806,8 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) /* Work out the shortest sequence. */ - 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); + avr_out_plus_1 (op, &len_minus, MINUS, &cc_plus, code_sat, sign, out_label); + avr_out_plus_1 (op, &len_plus, PLUS, &cc_minus, code_sat, sign, out_label); if (plen) { @@ -6807,9 +6815,9 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) *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); + avr_out_plus_1 (op, NULL, MINUS, pcc, code_sat, sign, out_label); else - avr_out_plus_1 (op, NULL, PLUS, pcc, code_sat, sign); + avr_out_plus_1 (op, NULL, PLUS, pcc, code_sat, sign, out_label); return ""; } @@ -6823,13 +6831,15 @@ avr_out_plus (rtx insn, rtx *xop, int *plen, int *pcc) and return "". 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 either an 8-bit clobber - register or SCRATCH if no clobber register is needed for the operation. */ + register or SCRATCH if no clobber register is needed for the operation. + INSN is an INSN_P or a pattern of an insn. */ const char* avr_out_bitop (rtx insn, rtx *xop, int *plen) { /* CODE and MODE of the operation. */ - enum rtx_code code = GET_CODE (SET_SRC (single_set (insn))); + rtx xpattern = INSN_P (insn) ? single_set (insn) : insn; + enum rtx_code code = GET_CODE (SET_SRC (xpattern)); enum machine_mode mode = GET_MODE (xop[0]); /* Number of bytes to operate on. */ @@ -7332,6 +7342,67 @@ avr_out_fract (rtx insn, rtx operands[], bool intsigned, int *plen) } +/* Output fixed-point rounding. XOP[0] = XOP[1] is the operand to round. + XOP[2] is the rounding point, a CONST_INT. The function prints the + instruction sequence if PLEN = NULL and computes the length in words + of the sequence if PLEN != NULL. Most of this function deals with + preparing operands for calls to `avr_out_plus' and `avr_out_bitop'. */ + +const char* +avr_out_round (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen) +{ + enum machine_mode mode = GET_MODE (xop[0]); + enum machine_mode imode = int_mode_for_mode (mode); + // The smallest fractional bit not cleared by the rounding is 2^(-RP). + int fbit = (int) GET_MODE_FBIT (mode); + double_int i_add = double_int_zero.set_bit (fbit-1 - INTVAL (xop[2])); + // Lengths of PLUS and AND parts. + int len_add = 0, *plen_add = plen ? &len_add : NULL; + int len_and = 0, *plen_and = plen ? &len_and : NULL; + + // Add-Saturate 1/2 * 2^(-RP). Don't print the label "0:" when printing + // the saturated addition so that we can emit the "rjmp 1f" before the + // "0:" below. + + rtx xadd = const_fixed_from_double_int (i_add, mode); + rtx xpattern, xsrc, op[4]; + + xsrc = SIGNED_FIXED_POINT_MODE_P (mode) + ? gen_rtx_SS_PLUS (mode, xop[1], xadd) + : gen_rtx_US_PLUS (mode, xop[1], xadd); + xpattern = gen_rtx_SET (VOIDmode, xop[0], xsrc); + + op[0] = xop[0]; + op[1] = xop[1]; + op[2] = xadd; + avr_out_plus (xpattern, op, plen_add, NULL, false /* Don't print "0:" */); + + avr_asm_len ("rjmp 1f" CR_TAB + "0:", NULL, plen_add, 1); + + // Keep all bits from RP and higher: ... 2^(-RP) + // Clear all bits from RP+1 and lower: 2^(-RP-1) ... + // Rounding point ^^^^^^^ + // Added above ^^^^^^^^^ + rtx xreg = simplify_gen_subreg (imode, xop[0], mode, 0); + rtx xmask = immed_double_int_const (-i_add - i_add, imode); + + xpattern = gen_rtx_SET (VOIDmode, xreg, gen_rtx_AND (imode, xreg, xmask)); + + op[0] = xreg; + op[1] = xreg; + op[2] = xmask; + op[3] = gen_rtx_SCRATCH (QImode); + avr_out_bitop (xpattern, op, plen_and); + avr_asm_len ("1:", NULL, plen, 0); + + if (plen) + *plen = len_add + len_and; + + return ""; +} + + /* Create RTL split patterns for byte sized rotate expressions. This produces a series of move instructions and considers overlap situations. Overlapping non-HImode operands need a scratch register. */ @@ -7540,6 +7611,7 @@ avr_adjust_insn_length (rtx insn, int len) case ADJUST_LEN_SFRACT: avr_out_fract (insn, op, true, &len); break; case ADJUST_LEN_UFRACT: avr_out_fract (insn, op, false, &len); break; + case ADJUST_LEN_ROUND: avr_out_round (insn, op, &len); break; case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break; case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break; diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index e9f5d038f1f4bcd73b03ef25044501329cf35ca1..f2681233acee549114e42dc678777e244e268157 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -140,7 +140,7 @@ "out_bitop, plus, addto_sp, tsthi, tstpsi, tstsi, compare, compare64, call, mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, - ufract, sfract, + ufract, sfract, round, xload, lpm, movmem, ashlqi, ashrqi, lshrqi, ashlhi, ashrhi, lshrhi, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d5f4c32de0272484ff3cde53c83f8b1361067f91..9c8bfe331029ada652a122dfc91b5f1d1bdab43e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-07-19 Georg-Johann Lay <avr@gjlay.de> + + PR target/57516 + * gcc.target/avr/torture/builtins-4-roundfx.c (test2hr, test2k): + Adjust to corrected rounding. + 2013-07-19 Georg-Johann Lay <avr@gjlay.de> * lib/target-supports.exp (check_effective_target_cilkplus): New proc. diff --git a/gcc/testsuite/gcc.target/avr/torture/builtins-4-roundfx.c b/gcc/testsuite/gcc.target/avr/torture/builtins-4-roundfx.c index 6ad0775553a09a31c9b5bb94a4d6cf10d147af46..46e915a6c8755f14a68dbe172dba0660a333a358 100644 --- a/gcc/testsuite/gcc.target/avr/torture/builtins-4-roundfx.c +++ b/gcc/testsuite/gcc.target/avr/torture/builtins-4-roundfx.c @@ -72,11 +72,11 @@ DEFTEST1 (long long accum, llk) static void test2hr (void) { - TEST2 (hr, 1, 0x7f, 0x40); - TEST2 (hr, 2, 0x7f, 0b1100000); - TEST2 (hr, 3, 0x7f, 0b1110000); - TEST2 (hr, 4, 0x7f, 0b1111000); - + TEST2 (hr, 1, 0x7f, 0x7f); + TEST2 (hr, 2, 0x70, 0x7f); + TEST2 (hr, 3, 0x78, 0x7f); + TEST2 (hr, 4, 0x7f, 0x7f); + TEST2 (uhr, 1, 0x7f, 0x80); TEST2 (uhr, 2, 0x7f, 0x80); TEST2 (uhr, 3, 0x7f, 0x80); @@ -85,10 +85,13 @@ static void test2hr (void) void test2k (void) { - TEST2 (k, 1, 0x7fffffff, 0x7fff8000 | 0b100000000000000); - TEST2 (k, 2, 0x7fffffff, 0x7fff8000 | 0b110000000000000); - TEST2 (k, 3, 0x7fffffff, 0x7fff8000 | 0b111000000000000); - TEST2 (k, 4, 0x7fffffff, 0x7fff8000 | 0b111100000000000); + TEST2 (k, 1, 0x7fffff00, 0x7fffffff); + TEST2 (k, 2, 0x7ffffff0, 0x7fffffff); + TEST2 (k, 2, 0x7ffff000, 0x7fffffff); + TEST2 (k, 3, 0x7ffff000, 0x7ffff000); + TEST2 (k, 3, 0x7ffff800, 0x7fffffff); + TEST2 (k, 3, 0x7ffff7ff, 0x7ffff000); + TEST2 (k, 4, 0x7ffff7ff, 0x7ffff800); TEST2 (uk, 1, 0x7fffffff, 1ul << 31); TEST2 (uk, 2, 0x7fffffff, 1ul << 31); diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 506fa43bdc02146798657aac308e6c18b650a43f..9020a64394637858d48c39bbd45321d4ad575b61 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,11 @@ +2013-07-19 Georg-Johann Lay <avr@gjlay.de> + + PR target/57516 + * config/avr/lib1funcs-fixed.S (__roundqq3, __rounduqq3) + (__round_s2_const, __round_u2_const) + (__round_s4_const, __round_u4_const, __round_x8): + Saturate result if addition result cannot be represented. + 2013-07-15 Matthias Klose <doko@ubuntu.com> * libgcc2.c: Don't include <limits.h>. diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S index d80389ce2ee70a72054c68a17882c7d328057f67..f920891e19e8c4656d3811ed6fd331c931ff48df 100644 --- a/libgcc/config/avr/lib1funcs-fixed.S +++ b/libgcc/config/avr/lib1funcs-fixed.S @@ -1464,12 +1464,13 @@ DEFUN __roundqq3 ;; Add-Saturate 2^{-RP-1} add A0, C0 brvc 0f - ldi A0, 0x7f + ldi C0, 0x7f + rjmp 9f 0: ;; Mask out bits beyond RP lsl C0 neg C0 and C0, A0 - mov C1, __tmp_reg__ +9: mov C1, __tmp_reg__ ret ENDF __roundqq3 #endif /* L_roundqq3 */ @@ -1488,12 +1489,13 @@ DEFUN __rounduqq3 ;; Add-Saturate 2^{-RP-1} add A0, C0 brcc 0f - ldi A0, 0xff + ldi C0, 0xff + rjmp 9f 0: ;; Mask out bits beyond RP lsl C0 neg C0 and C0, A0 - mov C1, __tmp_reg__ +9: mov C1, __tmp_reg__ ret ENDF __rounduqq3 #endif /* L_rounduqq3 */ @@ -1565,16 +1567,17 @@ ENDF __rounduha3 DEFUN __round_s2_const brvc 2f - ldi A1, 0x7f + ldi C1, 0x7f rjmp 1f ;; FALLTHRU (Barrier) ENDF __round_s2_const DEFUN __round_u2_const brcc 2f - ldi A1, 0xff + ldi C1, 0xff 1: - ldi A0, 0xff + ldi C0, 0xff + rjmp 9f 2: ;; Saturation is performed now. ;; Currently, we have C[] = 2^{-RP-1} @@ -1586,7 +1589,7 @@ DEFUN __round_u2_const ;; Clear the bits beyond the rounding point. and C0, A0 and C1, A1 - ret +9: ret ENDF __round_u2_const #endif /* L_round_2_const */ @@ -1681,18 +1684,19 @@ ENDF __roundusa3 DEFUN __round_s4_const brvc 2f - ldi A3, 0x7f + ldi C3, 0x7f rjmp 1f ;; FALLTHRU (Barrier) ENDF __round_s4_const DEFUN __round_u4_const brcc 2f - ldi A3, 0xff + ldi C3, 0xff 1: - ldi A2, 0xff - ldi A1, 0xff - ldi A0, 0xff + ldi C2, 0xff + ldi C1, 0xff + ldi C0, 0xff + rjmp 9f 2: ;; Saturation is performed now. ;; Currently, we have C[] = 2^{-RP-1} @@ -1707,7 +1711,7 @@ DEFUN __round_u4_const and C1, A1 and C2, A2 and C3, A3 - ret +9: ret ENDF __round_u4_const #endif /* L_round_4_const */ @@ -1847,12 +1851,13 @@ DEFUN __round_x8 1: ;; Unsigned brcc 3f ;; Unsigned overflow: A[] = 0xff... -2: ldi A7, 0xff - ldi A6, 0xff - wmov A0, A6 - wmov A2, A6 - wmov A4, A6 - bld A7, 7 +2: ldi C7, 0xff + ldi C6, 0xff + wmov C0, C6 + wmov C2, C6 + wmov C4, C6 + bld C7, 7 + rjmp 9f 3: ;; C[] = -C[] - C[] push A0 @@ -1869,7 +1874,7 @@ DEFUN __round_x8 and C5, A5 and C6, A6 and C7, A7 - ;; Epilogue +9: ;; Epilogue pop r29 pop r28 pop r17