;; "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"
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]);
"mov %r0+5,%0", xop, plen, 4);
}
- avr_asm_len ("0:", op, plen, 0);
+ if (out_label)
+ avr_asm_len ("0:", op, plen, 0);
}
/* 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].
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);
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 "";
}
/* 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. */
/* 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)
{
*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 "";
}
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. */
}
+/* 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. */
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;
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);
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);