diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 8a1d1d3ed4503a7f54ddb120b7053b00e2608a43..83137c7f6f63fbee8c3354ec936483b301261d22 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -69,6 +69,7 @@ extern const char *avr_out_insert_notbit (rtx_insn *, rtx*, int*); extern const char *avr_out_insv (rtx_insn *, rtx*, int*); extern const char *avr_out_extr (rtx_insn *, rtx*, int*); extern const char *avr_out_extr_not (rtx_insn *, rtx*, int*); +extern const char *avr_out_sextr (rtx_insn *, rtx*, int*); extern const char *avr_out_plus_set_ZN (rtx*, int*); extern const char *avr_out_plus_set_N (rtx*, int*); extern const char *avr_out_op8_set_ZN (rtx_code, rtx*, int*); @@ -102,6 +103,8 @@ extern void avr_expand_prologue (void); extern void avr_expand_epilogue (bool); extern bool avr_emit_cpymemhi (rtx*); extern void avr_emit_xior_with_shift (rtx_insn*, rtx*, int); +extern void avr_emit_skip_pixop (rtx_code, rtx, rtx, rtx, rtx_code, rtx, int); +extern void avr_emit_skip_clear (rtx, rtx, rtx_code, rtx, int); extern bool avr_epilogue_uses (int regno); extern void avr_output_addr_vec (rtx_insn*, rtx); diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index 8628a438ab56a7d36fde7085062d38d7f6ea95ee..656d3e7389b4f2a7c812346ed31191d245d11f8d 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -11076,6 +11076,7 @@ avr_adjust_insn_length (rtx_insn *insn, int len) case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break; case ADJUST_LEN_FLOAD: avr_out_fload (insn, op, &len); break; case ADJUST_LEN_SEXT: avr_out_sign_extend (insn, op, &len); break; + case ADJUST_LEN_SEXTR: avr_out_sextr (insn, op, &len); break; case ADJUST_LEN_SFRACT: avr_out_fract (insn, op, true, &len); break; case ADJUST_LEN_UFRACT: avr_out_fract (insn, op, false, &len); break; @@ -12560,6 +12561,19 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, ? INTVAL (XEXP (x, 1)) : -1; + if (avropt_pr118012) + { + if ((code == IOR || code == XOR || code == PLUS) + && GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND + && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == AND + && XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == const1_rtx) + { + *total = COSTS_N_INSNS (2 + n_bytes); + return true; + } + } + switch (code) { case CONST_INT: @@ -12577,6 +12591,20 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, return true; case NEG: + if (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND + && GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTRACT) + { + // Just a sign_extract of bit 0? + rtx y = XEXP (XEXP (x, 0), 0); + if (XEXP (y, 1) == const1_rtx + && XEXP (y, 2) == const0_rtx) + { + *total = COSTS_N_INSNS (1 + n_bytes + - (AVR_HAVE_MOVW && n_bytes == 4)); + return true; + } + } + switch (mode) { case E_QImode: @@ -12856,6 +12884,25 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, return true; case MULT: + if (avropt_pr118012) + { + if (GET_CODE (XEXP (x, 0)) == AND + && XEXP (XEXP (x, 0), 1) == const1_rtx) + { + // Try to defeat PR118012. The MUL variant is actually very + // expensive, but combine is given a pattern to transform this + // into something less toxic. Though this might not work + // for SImode, and we still have a completely ridiculous + // 32-bit multiplication instead of a simple bit test on + // devices that don't even have MUL. This is because on + // AVR_TINY, we'll get a libcall which we cannot undo. + // (On other devices that don't have MUL, the libcall is + // bypassed by providing mulsi3, cf. insn mulsi3_[call_]pr118012. + *total = 0; + return true; + } + } // PR118012 + switch (mode) { case E_QImode: @@ -14383,6 +14430,132 @@ avr_out_sbxx_branch (rtx_insn *insn, rtx operands[]) } +/* Output code for XOP[0] = sign_extract (XOP[1].0) and return "". + PLEN == 0: Output instructions. + PLEN != 0: Set *PLEN to the length of the sequence in words. */ + +const char * +avr_out_sextr (rtx_insn *insn, rtx *xop, int *plen) +{ + rtx dest = xop[0]; + rtx src = xop[1]; + int bit = INTVAL (xop[2]); + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + + gcc_assert (bit == 0); + + if (reg_unused_after (insn, src)) + avr_asm_len ("lsr %1", xop, plen, -1); + else + avr_asm_len ("mov %0,%1" CR_TAB + "lsr %0", xop, plen, -2); + + for (int i = 0; i < n_bytes; ++i) + { + rtx b = avr_byte (dest, i); + avr_asm_len ("sbc %0,%0", &b, plen, 1); + if (i == 1 && n_bytes == 4 && AVR_HAVE_MOVW) + return avr_asm_len ("movw %C0,%A0", xop, plen, 1); + } + + return ""; +} + + +/* + if (bits.bitno <eqne> 0) + dest = op0; + else + dest = op0 <pix> op1; + + Performed as: + + dest = op0; + if (bits.bitno <eqne> 0) + goto LL; + dest o= op1; +LL:; */ + +void +avr_emit_skip_pixop (rtx_code pix, rtx dest, rtx op0, rtx op1, + rtx_code eqne, rtx bits, int bitno) +{ + gcc_assert (eqne == EQ); + + const machine_mode mode = GET_MODE (dest); + + // Get rid of early-clobbers. + + if (reg_overlap_mentioned_p (dest, bits)) + bits = copy_to_mode_reg (GET_MODE (bits), bits); + + if (reg_overlap_mentioned_p (dest, op1)) + op1 = copy_to_mode_reg (mode, op1); + + // xorqi3 has "register_operand" for op1. + if (mode == QImode && pix == XOR) + op1 = force_reg (QImode, op1); + + emit_move_insn (dest, op0); + + // Skip if bits.bitno <eqne> bitno. + rtx xlabel = gen_label_rtx (); + rtx zerox = gen_rtx_ZERO_EXTRACT (QImode, bits, const1_rtx, GEN_INT (bitno)); + rtx cond = gen_rtx_fmt_ee (eqne, VOIDmode, zerox, const0_rtx); + emit (gen_sbrx_branchqi_split (cond, bits, const0_rtx, xlabel)); + + // Payload: plus, ior, xor for HI, PSI, SI have a scratch:QI; + // QI and plus:HI don't. + rtx src = gen_rtx_fmt_ee (pix, mode, dest, op1); + rtx set = gen_rtx_SET (dest, src); + rtx clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (QImode)); + bool no_scratch = mode == QImode || (mode == HImode && pix == PLUS); + emit (no_scratch + ? set + : gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber))); + + emit_label (xlabel); +} + + +/* + if (bits.bitno <eqne> 0) + dest = src; + else + dest = 0; + + Performed as: + + dest = src; + if (bits.bitno <eqne> 0) + goto LL; + dest = 0; +LL:; */ + +void +avr_emit_skip_clear (rtx dest, rtx src, rtx_code eqne, rtx bits, int bitno) +{ + const machine_mode mode = GET_MODE (dest); + + // Get rid of early-clobber. + if (reg_overlap_mentioned_p (dest, bits)) + bits = copy_to_mode_reg (GET_MODE (bits), bits); + + emit_move_insn (dest, src); + + // Skip if bits.bitno <eqne> bitno. + rtx xlabel = gen_label_rtx (); + rtx zerox = gen_rtx_ZERO_EXTRACT (QImode, bits, const1_rtx, GEN_INT (bitno)); + rtx cond = gen_rtx_fmt_ee (eqne, VOIDmode, zerox, const0_rtx); + emit (gen_sbrx_branchqi_split (cond, bits, const0_rtx, xlabel)); + + // Payload: dest = 0; + emit_move_insn (dest, CONST0_RTX (mode)); + + emit_label (xlabel); +} + + /* Worker function for `TARGET_ASM_CONSTRUCTOR'. */ static void diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 6550fadd101710a18aaf6fb894166984cd898290..1c956114a4b87b196b3f7e06f26deb01291524bb 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -160,7 +160,7 @@ ;; Otherwise do special processing depending on the attribute. (define_attr "adjust_len" - "out_bitop, plus, addto_sp, sext, extr, extr_not, plus_ext, + "out_bitop, plus, addto_sp, sext, extr, extr_not, plus_ext, sextr, tsthi, tstpsi, tstsi, compare, compare64, call, mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, ufract, sfract, round, @@ -336,6 +336,7 @@ (define_code_iterator any_lshift [lshiftrt ashift]) ; logic shift (define_code_iterator piaop [plus ior and]) +(define_code_iterator pixop [plus ior xor]) (define_code_iterator bitop [xor ior and]) (define_code_iterator xior [xor ior]) (define_code_iterator eqne [eq ne]) @@ -3337,8 +3338,18 @@ (match_operand:SI 2 "nonmemory_operand" ""))) (clobber (reg:HI 26)) (clobber (reg:DI 18))])] - "AVR_HAVE_MUL" + "AVR_HAVE_MUL + || (avropt_pr118012 + /* AVR_TINY passes args on the stack, so we cannot work + around PR118012 like this. */ + && ! AVR_TINY)" { + if (! AVR_HAVE_MUL) + { + emit (gen_gen_mulsi3_pr118012 (operands[0], operands[1], operands[2])); + DONE; + } + if (u16_operand (operands[2], SImode)) { operands[2] = force_reg (HImode, gen_int_mode (INTVAL (operands[2]), HImode)); @@ -3358,6 +3369,26 @@ DONE; }) +;; With PR118012, we do __mulsi3 as a transparent call, so insn combine +;; can transform (mult:SI (and:SI * (const_int 1))) into something +;; less toxic. +(define_expand "gen_mulsi3_pr118012" + [(parallel [(set (match_operand:SI 0 "register_operand") + (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "nonmemory_operand"))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30)) + (clobber (reg:DI 18))])] + "avropt_pr118012 + && ! AVR_HAVE_MUL + && ! AVR_TINY" + { + operands[2] = force_reg (SImode, operands[2]); + if (avr_emit3_fix_outputs (gen_gen_mulsi3_pr118012, operands, 1 << 0, + regmask (DImode, 18) | regmask (HImode, 26) | regmask (HImode, 30))) + DONE; + }) + (define_insn_and_split "*mulsi3" [(set (match_operand:SI 0 "pseudo_register_operand" "=r") (mult:SI (match_operand:SI 1 "pseudo_register_operand" "r") @@ -3393,6 +3424,33 @@ } }) +(define_insn_and_split "*mulsi3_pr118012" + [(set (match_operand:SI 0 "pseudo_register_operand" "=r") + (mult:SI (match_operand:SI 1 "pseudo_register_operand" "r") + (match_operand:SI 2 "pseudo_register_operand" "r"))) + (clobber (reg:HI 26)) + (clobber (reg:HI 30)) + (clobber (reg:DI 18))] + "avropt_pr118012 + && ! AVR_HAVE_MUL + && ! AVR_TINY + && ! reload_completed" + { gcc_unreachable(); } + "&& 1" + [(set (reg:SI 18) + (match_dup 1)) + (set (reg:SI 22) + (match_dup 2)) + (parallel [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:SI 18)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))]) + (set (match_dup 0) + (reg:SI 22))]) + + ;; "muluqisi3" ;; "muluhisi3" (define_expand "mulu<mode>si3" @@ -3658,6 +3716,26 @@ (clobber (reg:HI 26)) (clobber (reg:CC REG_CC))])]) +(define_insn_and_split "*mulsi3_call_pr118012_split" + [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:SI 18)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30))] + "avropt_pr118012 + && ! AVR_HAVE_MUL + && ! AVR_TINY" + "#" + "&& reload_completed" + [(parallel [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:SI 18)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30)) + (clobber (reg:CC REG_CC))])]) + (define_insn "*mulsi3_call" [(set (reg:SI 22) (mult:SI (reg:SI 22) @@ -3668,6 +3746,21 @@ "%~call __mulsi3" [(set_attr "type" "xcall")]) +(define_insn "*mulsi3_call_pr118012" + [(set (reg:SI 22) + (mult:SI (reg:SI 22) + (reg:SI 18))) + (clobber (reg:SI 18)) + (clobber (reg:HI 26)) + (clobber (reg:HI 30)) + (clobber (reg:CC REG_CC))] + "avropt_pr118012 + && ! AVR_HAVE_MUL + && ! AVR_TINY + && reload_completed" + "%~call __mulsi3" + [(set_attr "type" "xcall")]) + ;; "*mulhisi3_call" ;; "*umulhisi3_call" (define_insn_and_split "*<extend_u>mulhisi3_call_split" @@ -7508,7 +7601,7 @@ ;; Combine will create zero-extract patterns for single-bit tests. ;; Permit any mode in source pattern by using VOIDmode. -(define_insn_and_split "*sbrx_branch<mode>_split" +(define_insn_and_split "sbrx_branch<mode>_split" [(set (pc) (if_then_else (match_operator 0 "eqne_operator" @@ -8975,8 +9068,8 @@ [(set (pc) (if_then_else (ge (match_operand:QI 0 "register_operand" "") (const_int 0)) - (label_ref (match_operand 1 "" "")) - (pc)))] + (label_ref (match_operand 1 "" "")) + (pc)))] "" "#" "reload_completed" @@ -10307,9 +10400,9 @@ (define_insn_and_split "*extzv.qihi1" [(set (match_operand:HI 0 "register_operand" "=r") - (zero_extract:HI (match_operand:QI 1 "register_operand" "r") + (zero_extract:HI (match_operand:QIHI 1 "register_operand" "r") (const_int 1) - (match_operand:QI 2 "const_0_to_7_operand" "n")))] + (match_operand:QI 2 "const_0_to_<MSB>_operand" "n")))] "" "#" "" @@ -10532,6 +10625,289 @@ (match_dup 2)))]) +(define_insn_and_split "*sextr.<QISI:mode>.<QISI2:mode>_split" + [(set (match_operand:QISI 0 "register_operand" "=r") + (sign_extract:QISI (match_operand:QISI2 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const0_operand" "L")))] + "" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (sign_extract:QISI (match_dup 1) + (const_int 1) + (match_dup 2))) + (clobber (reg:CC REG_CC))])]) + +(define_insn "*sextr.<QISI:mode>.<QISI2:mode>" + [(set (match_operand:QISI 0 "register_operand" "=r") + (sign_extract:QISI (match_operand:QISI2 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const0_operand" "L"))) + (clobber (reg:CC REG_CC))] + "reload_completed" + { + return avr_out_sextr (insn, operands, NULL); + } + [(set_attr "adjust_len" "sextr")]) + + +(define_insn_and_split "*neg.zextr-to-sextr.<HISI:mode>.<QISI:mode>" + [(set (match_operand:HISI 0 "register_operand") + (neg:HISI (zero_extend:HISI + (zero_extract:QIPSI (match_operand:QISI 1 "register_operand") + (const_int 1) + (match_operand:QI 2 "const0_operand")))))] + "avropt_pr118012 + && <HISI:SIZE> > <QIPSI:SIZE> + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(set (match_dup 0) + (sign_extract:HISI (match_dup 1) + (const_int 1) + (match_dup 2)))]) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PR118012: match.pd's +;; +;; /* (zero_one == 0) ? y : z <op> y -> ((typeof(y))zero_one * z) <op> y */ +;; /* (zero_one != 0) ? z <op> y : y -> ((typeof(y))zero_one * z) <op> y */ +;; +;; introduces a crazy "optimization" that transforms code like +;; +;; if (b & 1) +;; c ^= a; +;; to +;; +;; u = extract_bit0 (b); +;; v = zero_extend (u); +;; w = NEG v; +;; x = a AND w +;; c ^= x +;; +;; or even to +;; +;; u = extract_bit0 (b); +;; v = a MULT u +;; c ^= v +;; +;; even on machines that don't have MUL instructions or that +;; have to perform the multiplication by means of a libgcc call. +;; Try to fix that below. Notice that on AVR_TINY no MUL insn is +;; available since is is performed as a libgcc call from which we +;; cannot roll back. With !AVR_HAVR_MULMUL it's a transparent call +;; from avr.md so we can get rid of that at least. + +;; Map +;; $0 = ((sign_extract ($1.0)) AND $3) <op> $4 +;; to +;; $0 = $4 +;; if ($1.0 == 0) +;; goto L +;; $0 <op>= $3 +;; L:; +(define_insn_and_split "*pixop-to-skip.<QISI:mode>" + [(set (match_operand:QISI 0 "register_operand") + (pixop:QISI (and:QISI (sign_extract:QISI (match_operand:QISI2 1 "register_operand") + (const_int 1) + (match_operand:QI 2 "const0_operand")) + (match_operand:QISI 3 "nonmemory_operand")) + (match_operand:QISI 4 "register_operand")))] + "avropt_pr118012 + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_pixop (<pixop:CODE>, operands[0], operands[4], operands[3], + EQ, operands[1], 0); + DONE; + }) + +;; Map +;; $0 = (($1 AND 1) MULT $2) o $3 +;; to +;; $0 = $3 +;; if ($1.0 == 0) +;; goto L +;; $0 o= $2 +;; L:; +(define_insn_and_split "*mul.and1-to-skip.<mode>" + [(set (match_operand:QISI 0 "register_operand") + (pixop:QISI (mult:QISI (and:QISI (match_operand:QISI 1 "register_operand") + (const_int 1)) + (match_operand:QISI 2 "nonmemory_operand")) + (match_operand:QISI 3 "register_operand")))] + "avropt_pr118012 + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_pixop (<CODE>, operands[0], operands[3], operands[2], + EQ, operands[1], 0); + DONE; + }) + +(define_insn_and_split "*mul.ext.and1-to-skip.<HISI:mode>" + [(set (match_operand:HISI 0 "register_operand") + (pixop:HISI (mult:HISI (any_extend:HISI (and:QIPSI (match_operand:QIPSI 1 "register_operand") + (const_int 1))) + (match_operand:HISI 2 "nonmemory_operand")) + (match_operand:HISI 3 "register_operand")))] + "avropt_pr118012 + && <HISI:SIZE> > <QIPSI:SIZE> + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_pixop (<pixop:CODE>, operands[0], operands[3], operands[2], + EQ, operands[1], 0); + DONE; + }) + +;; Like the one above, but where $2 was a power of 2 and MULT has been +;; transformed to ASHIFT (PR118360). +(define_insn_and_split "*shl.ext.and1-to-skip.<HISI:mode>" + [(set (match_operand:HISI 0 "register_operand") + (pixop:HISI (ashift:HISI (any_extend:HISI (and:QIPSI (match_operand:QIPSI 1 "register_operand") + (const_int 1))) + (match_operand:QI 2 "const_int_operand")) + (match_operand:HISI 3 "register_operand")))] + "avropt_pr118012 + && <HISI:SIZE> > <QIPSI:SIZE> + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + rtx op2 = gen_int_mode (1u << INTVAL (operands[2]), <HISI:MODE>mode); + avr_emit_skip_pixop (<pixop:CODE>, operands[0], operands[3], op2, + EQ, operands[1], 0); + DONE; + }) + +(define_insn_and_split "*shl.and-to-skip.<mode>" + [(set (match_operand:HISI 0 "register_operand") + (pixop:HISI (and:HISI (ashift:HISI (match_operand:HISI 1 "register_operand") + (match_operand:QI 4 "const_0_to_<MSB>_operand")) + (match_operand:HISI 2 "single_one_operand")) + (match_operand:HISI 3 "register_operand")))] + "avropt_pr118012 + && exact_log2 (UINTVAL (operands[2]) & GET_MODE_MASK (<MODE>mode)) + == INTVAL (operands[4]) + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_pixop (<CODE>, operands[0], operands[3], operands[2], + EQ, operands[1], 0); + DONE; + }) + + +;; Map +;; $0 = ($1 AND 1) MULT $2 +;; to +;; $0 = $2 +;; if ($1.0 != 0) +;; goto L +;; $0 = 0 +;; L:; +(define_insn_and_split "*map.mul.and1-to-skip.<QISI:mode>" + [(set (match_operand:QISI 0 "register_operand") + (mult:QISI (and:QISI (match_operand:QISI2 1 "register_operand") + (const_int 1)) + (match_operand:QISI 2 "nonmemory_operand")))] + "avropt_pr118012 + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_clear (operands[0], operands[2], NE, operands[1], 0); + DONE; + }) + +(define_insn_and_split "*map.mul.and1-to-skip.<mode>" + [(set (match_operand:QISI 0 "register_operand") + (mult:QISI (and:QISI (match_operand:QISI 1 "register_operand") + (const_int 1)) + (match_operand:QISI 2 "nonmemory_operand")))] + "avropt_pr118012 + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_clear (operands[0], operands[2], NE, operands[1], 0); + DONE; + }) + +(define_insn_and_split "*map.mul.ext.and1-to-skip.<HISI:mode>" + [(set (match_operand:HISI 0 "register_operand") + (mult:HISI (any_extend:HISI (and:QIPSI (match_operand:QIPSI 1 "register_operand") + (const_int 1))) + (match_operand:HISI 2 "nonmemory_operand")))] + "avropt_pr118012 + && <HISI:SIZE> > <QIPSI:SIZE> + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_clear (operands[0], operands[2], NE, operands[1], 0); + DONE; + }) + +;; Similar, but the MULT has been turned to ASHIFT. +(define_insn_and_split "*map.shl.ext.and1-to-skip.<HISI:mode>" + [(set (match_operand:HISI 0 "register_operand") + (ashift:HISI (any_extend:HISI (and:QIPSI (match_operand:QIPSI 1 "register_operand") + (const_int 1))) + (match_operand:QI 2 "const_0_to_<HISI:MSB>_operand")))] + "avropt_pr118012 + && <HISI:SIZE> > <QIPSI:SIZE> + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + rtx op2 = gen_int_mode (1u << INTVAL (operands[2]), <HISI:MODE>mode); + avr_emit_skip_clear (operands[0], op2, NE, operands[1], 0); + DONE; + }) + + +;; Map +;; $0 = sign_extract($1.0) AND $3 +;; to +;; $0 = $3 +;; if ($1.0 != 0) +;; goto L +;; $0 = 0 +;; L:; +(define_insn_and_split "*map.and1-to-skip.<QISI:mode>" + [(set (match_operand:QISI 0 "register_operand") + (and:QISI (sign_extract:QISI (match_operand:QISI2 1 "register_operand") + (const_int 1) + (match_operand:QI 2 "const0_operand")) + (match_operand:QISI 3 "nonmemory_operand")))] + "avropt_pr118012 + && ! reload_completed" + { gcc_unreachable (); } + "&& 1" + [(scratch)] + { + avr_emit_skip_clear (operands[0], operands[3], NE, operands[1], 0); + DONE; + }) + + ;; Work around PR115307: Early passes expand isinf/f/l to a bloat. ;; These passes do not consider costs, and there is no way to ;; hook in or otherwise disable the generated bloat. diff --git a/gcc/config/avr/avr.opt b/gcc/config/avr/avr.opt index 8eb9b3ddf23f91d608414d94d54b56e2cf6c3931..ce6a8db95085817e64d7bac0849fb0f870fa204d 100644 --- a/gcc/config/avr/avr.opt +++ b/gcc/config/avr/avr.opt @@ -52,6 +52,11 @@ Target Undocumented Mask(ALL_DEBUG) mlog= Target RejectNegative Joined Undocumented Var(avropt_log_details) +;; Tries to work around PR118012. +mpr118012 +Target Var(avropt_pr118012) UInteger Init(1) Undocumented +This option is on per default in order to work around PR118012. + mshort-calls Target RejectNegative Mask(SHORT_CALLS) This option is used internally for multilib generation and selection. Assume RJMP / RCALL can target all program memory. diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m103.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m103.c new file mode 100644 index 0000000000000000000000000000000000000000..bb0d375105e5edc2be161bb70b45fcfa0fa8aa79 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m103.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=atmega103" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m128.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m128.c new file mode 100644 index 0000000000000000000000000000000000000000..76b218f618c73914c3652de7d6df9ed58a84d6d3 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-m128.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=atmega128" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-t40.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-t40.c new file mode 100644 index 0000000000000000000000000000000000000000..16ce57445a4eae0d84858b3722049e0dc5e01b7e --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-o2-t40.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=attiny40" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m103.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m103.c new file mode 100644 index 0000000000000000000000000000000000000000..cb07987f9c312f9501a308a3278ec334df0624ba --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m103.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=atmega103" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m128.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m128.c new file mode 100644 index 0000000000000000000000000000000000000000..9924b22ca18a85b8b70499d83ec56434caac706f --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-m128.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=atmega128" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-t40.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-t40.c new file mode 100644 index 0000000000000000000000000000000000000000..e064152a76211a9911ee55b2c39aa748ee2c9cc1 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1-os-t40.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=attiny40" } */ + +#include "pr118012-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1.h b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1.h new file mode 100644 index 0000000000000000000000000000000000000000..d68beb93e64001875237b80fb716e8cbbbd7bce6 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118012-1.h @@ -0,0 +1,63 @@ +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; + +// Generates Galois Field GF(2^15) = GF(2)[x] mod (p * GF(2)[x]) +#define POLY 0xe125 // 1 + x^2 + x^5 + x^8 + x^13 + x^14 + x^15 +#define DEGREE 15 + +typedef uint16_t poly; + +#define MASK(b) (((poly) 1) << (b)) + +#ifndef BIT +#define BIT 0 +#endif + +// Calculate a * b mod p. +poly pprod (poly a, poly b) +{ + const poly mask = MASK (DEGREE); + poly c = 0; + + while (b) + { + // Is bit i of b set? + if (b & (1u << BIT)) + { + //__asm ("" : "+r" (c)); + c ^= a; // yes, then c := c + a * x^i + } + a <<= 1; // a := a*x + + if (a & mask) // a := a mod p + a ^= POLY; + + b >>= 1; + } + + return c; +} + +uint32_t pprod32 (uint32_t a, uint32_t b) +{ + const uint32_t mask = MASK (DEGREE); + uint32_t c = 0; + + while (b) + { + // Is bit i of b set? + if (b & (1u << BIT)) + { + //__asm ("" : "+r" (c)); + c ^= a; // yes, then c := c + a * x^i + } + a <<= 1; // a := a*x + + if (a & mask) // a := a mod p + a ^= POLY; + + b >>= 1; + } + + return c; +} diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m103.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m103.c new file mode 100644 index 0000000000000000000000000000000000000000..3786586041c4003d795646db6517587ca611deb6 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m103.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=atmega103" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m128.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m128.c new file mode 100644 index 0000000000000000000000000000000000000000..97d5a0e4b5e98b8d0995a0277eed61e5dde84869 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-m128.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=atmega128" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-t40.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-t40.c new file mode 100644 index 0000000000000000000000000000000000000000..d588fbcf1a64742052e7a4f99466c9163c957ecd --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-o2-t40.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mmcu=attiny40" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m103.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m103.c new file mode 100644 index 0000000000000000000000000000000000000000..745bbdf5948941064a3a6c429a328533dd493dc3 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m103.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=atmega103" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m128.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m128.c new file mode 100644 index 0000000000000000000000000000000000000000..eb2b64906edf7b8cd97b7e5a4ecff0e28e8b0b5f --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-m128.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=atmega128" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-t40.c b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-t40.c new file mode 100644 index 0000000000000000000000000000000000000000..8701e73e0622ef55faab3fcae25af693ea05a448 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1-os-t40.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mmcu=attiny40" } */ + +#include "pr118360-1.h" + +/* { dg-final { scan-assembler-not "mul" } } */ +/* { dg-final { scan-assembler-not "neg" } } */ +/* { dg-final { scan-assembler-not "rol " } } */ diff --git a/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1.h b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1.h new file mode 100644 index 0000000000000000000000000000000000000000..cc97a88448e8f05dbffbf5eef4f1960b35e02c2f --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/mmcu/pr118360-1.h @@ -0,0 +1,27 @@ +int fun9_16 (int a, int b) +{ + if (a & 1) + b = b | 0x9; + return b; +} + +long fun9_32 (int a, long b) +{ + if (a & 1) + b = b | 0x9; + return b; +} + +int fun8_16 (int a, int b) +{ + if (a & 1) + b = b | 0x8; + return b; +} + +long fun8_32 (int a, long b) +{ + if (a & 1) + b = b | 0x8; + return b; +}