aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorGeorg-Johann Lay <avr@gjlay.de>2013-07-19 11:10:08 +0000
committerGeorg-Johann Lay <gjl@gcc.gnu.org>2013-07-19 11:10:08 +0000
commit0237179843a59b84cc1ae9a14bdf66a308ebd289 (patch)
treeaec31c0be57cb1d48958052153b5c54d7e94476e /gcc
parent1545921d63fe20ebe99a3280728ec33c99f8af78 (diff)
downloadgcc-0237179843a59b84cc1ae9a14bdf66a308ebd289.zip
gcc-0237179843a59b84cc1ae9a14bdf66a308ebd289.tar.gz
gcc-0237179843a59b84cc1ae9a14bdf66a308ebd289.tar.bz2
re PR target/57516 ([avr] Incorrect fixed-point rounding result in the overflow case)
gcc/ 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. libgcc/ 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. gcc/testsuite/ PR target/57516 * gcc.target/avr/torture/builtins-4-roundfx.c (test2hr, test2k): Adjust to corrected rounding. From-SVN: r201051
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog14
-rw-r--r--gcc/config/avr/avr-fixed.md51
-rw-r--r--gcc/config/avr/avr-protos.h3
-rw-r--r--gcc/config/avr/avr.c104
-rw-r--r--gcc/config/avr/avr.md2
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/gcc.target/avr/torture/builtins-4-roundfx.c21
7 files changed, 133 insertions, 68 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 981d489..1810dbe 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 7d9b525..b2f0b9a 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 5246d06..21ad26a 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 f8a43e5..50100bf 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 e9f5d03..f268123 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 d5f4c32..9c8bfe3 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,11 @@
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.
* gcc.dg/cilk-plus/cilk-plus.exp: only run if
check_effective_target_cilkplus.
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 6ad0775..46e915a 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);