diff options
Diffstat (limited to 'gcc/config/avr')
-rw-r--r-- | gcc/config/avr/avr-mcus.def | 6 | ||||
-rw-r--r-- | gcc/config/avr/avr-passes.cc | 71 | ||||
-rw-r--r-- | gcc/config/avr/avr-passes.def | 12 | ||||
-rw-r--r-- | gcc/config/avr/avr-protos.h | 2 | ||||
-rw-r--r-- | gcc/config/avr/avr.cc | 152 | ||||
-rw-r--r-- | gcc/config/avr/avr.md | 215 | ||||
-rw-r--r-- | gcc/config/avr/avr.opt | 4 | ||||
-rw-r--r-- | gcc/config/avr/avr.opt.urls | 3 |
8 files changed, 454 insertions, 11 deletions
diff --git a/gcc/config/avr/avr-mcus.def b/gcc/config/avr/avr-mcus.def index 9f79a9a..ad64050 100644 --- a/gcc/config/avr/avr-mcus.def +++ b/gcc/config/avr/avr-mcus.def @@ -326,6 +326,9 @@ AVR_MCU ("avr64du32", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR AVR_MCU ("avr64ea28", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64EA28__", 0x6800, 0x0, 0x10000, 0) AVR_MCU ("avr64ea32", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64EA32__", 0x6800, 0x0, 0x10000, 0) AVR_MCU ("avr64ea48", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64EA48__", 0x6800, 0x0, 0x10000, 0) +AVR_MCU ("avr64sd28", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64SD28__", 0x6000, 0x0, 0x10000, 0) +AVR_MCU ("avr64sd32", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64SD32__", 0x6000, 0x0, 0x10000, 0) +AVR_MCU ("avr64sd48", ARCH_AVRXMEGA2, AVR_CVT | AVR_ISA_FLMAP, "__AVR_AVR64SD48__", 0x6000, 0x0, 0x10000, 0) /* Xmega, Flash + RAM < 64K, flash visible in RAM address space */ AVR_MCU ("avrxmega3", ARCH_AVRXMEGA3, AVR_ISA_NONE, NULL, 0x3f00, 0x0, 0x8000, 0) AVR_MCU ("attiny202", ARCH_AVRXMEGA3, AVR_CVT | AVR_ISA_RCALL, "__AVR_ATtiny202__", 0x3f80, 0x0, 0x800, 0x8000) @@ -407,6 +410,9 @@ AVR_MCU ("avr16ea48", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR AVR_MCU ("avr32ea28", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32EA28__", 0x7000, 0x0, 0x8000, 0x8000) AVR_MCU ("avr32ea32", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32EA32__", 0x7000, 0x0, 0x8000, 0x8000) AVR_MCU ("avr32ea48", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32EA48__", 0x7000, 0x0, 0x8000, 0x8000) +AVR_MCU ("avr32sd20", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32SD20__", 0x7000, 0x0, 0x8000, 0x8000) +AVR_MCU ("avr32sd28", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32SD28__", 0x7000, 0x0, 0x8000, 0x8000) +AVR_MCU ("avr32sd32", ARCH_AVRXMEGA3, AVR_CVT, "__AVR_AVR32SD32__", 0x7000, 0x0, 0x8000, 0x8000) /* Xmega, 64K < Flash <= 128K, RAM <= 64K */ AVR_MCU ("avrxmega4", ARCH_AVRXMEGA4, AVR_ISA_NONE, NULL, 0x2000, 0x0, 0x11000, 0) AVR_MCU ("atxmega64a3", ARCH_AVRXMEGA4, AVR_ISA_NONE, "__AVR_ATxmega64A3__", 0x2000, 0x0, 0x11000, 0) diff --git a/gcc/config/avr/avr-passes.cc b/gcc/config/avr/avr-passes.cc index 184619a..2c21e7b 100644 --- a/gcc/config/avr/avr-passes.cc +++ b/gcc/config/avr/avr-passes.cc @@ -29,6 +29,7 @@ #include "target.h" #include "rtl.h" #include "tree.h" +#include "diagnostic-core.h" #include "cfghooks.h" #include "cfganal.h" #include "df.h" @@ -4848,6 +4849,70 @@ avr_pass_fuse_add::execute1 (function *func) ////////////////////////////////////////////////////////////////////////////// +// Split insns with nonzero_bits() after combine. + +static const pass_data avr_pass_data_split_nzb = +{ + RTL_PASS, // type + "", // name (will be patched) + OPTGROUP_NONE, // optinfo_flags + TV_DF_SCAN, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + 0 // todo_flags_finish +}; + +class avr_pass_split_nzb : public rtl_opt_pass +{ +public: + avr_pass_split_nzb (gcc::context *ctxt, const char *name) + : rtl_opt_pass (avr_pass_data_split_nzb, ctxt) + { + this->name = name; + } + + unsigned int execute (function *) final override + { + if (avropt_use_nonzero_bits) + split_nzb_insns (); + return 0; + } + + void split_nzb_insns (); + +}; // avr_pass_split_nzb + + +void +avr_pass_split_nzb::split_nzb_insns () +{ + rtx_insn *next; + + for (rtx_insn *insn = get_insns (); insn; insn = next) + { + next = NEXT_INSN (insn); + + if (INSN_P (insn) + && single_set (insn) + && get_attr_nzb (insn) == NZB_YES) + { + rtx_insn *last = try_split (PATTERN (insn), insn, 1 /*last*/); + + // The nonzero_bits() insns *must* split. If not: ICE. + if (last == insn) + { + debug_rtx (insn); + internal_error ("failed to split insn"); + } + } + } +} + + + +////////////////////////////////////////////////////////////////////////////// // Split shift insns after peephole2 / befor avr-fuse-move. static const pass_data avr_pass_data_split_after_peephole2 = @@ -5645,6 +5710,12 @@ make_avr_pass_casesi (gcc::context *ctxt) return new avr_pass_casesi (ctxt, "avr-casesi"); } +rtl_opt_pass * +make_avr_pass_split_nzb (gcc::context *ctxt) +{ + return new avr_pass_split_nzb (ctxt, "avr-split-nzb"); +} + // Try to replace 2 cbranch insns with 1 comparison and 2 branches. rtl_opt_pass * diff --git a/gcc/config/avr/avr-passes.def b/gcc/config/avr/avr-passes.def index 091005e..eb60a93 100644 --- a/gcc/config/avr/avr-passes.def +++ b/gcc/config/avr/avr-passes.def @@ -74,6 +74,18 @@ INSERT_PASS_BEFORE (pass_free_cfg, 1, avr_pass_recompute_notes); INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi); +/* Some combine insns have nonzero_bits() in their condition, though insns + should not use such stuff in their condition. Therefore, we split such + insn into something without nonzero_bits() in their condition right after + insn combine. + + Since neither split_all_insns() nor split_all_insns_noflow() work at that + point (presumably since there are splits involving branches), we split + respective insns (and only such insns) by hand. Respective insns are + tagged with insn attribute nzb = "yes" so that they are easy to spot. */ + +INSERT_PASS_AFTER (pass_combine, 1, avr_pass_split_nzb); + /* If-else decision trees generated for switch / case may produce sequences like diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 83137c7..ca30136 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -136,6 +136,7 @@ extern bool reg_unused_after (rtx_insn *insn, rtx reg); extern int avr_jump_mode (rtx x, rtx_insn *insn, int = 0); extern bool test_hard_reg_class (enum reg_class rclass, rtx x); extern bool jump_over_one_insn_p (rtx_insn *insn, rtx dest); +extern bool avr_nonzero_bits_lsr_operands_p (rtx_code, rtx *); extern void avr_final_prescan_insn (rtx_insn *insn, rtx *operand, int num_operands); @@ -205,6 +206,7 @@ extern rtl_opt_pass *make_avr_pass_pre_proep (gcc::context *); extern rtl_opt_pass *make_avr_pass_recompute_notes (gcc::context *); extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *); extern rtl_opt_pass *make_avr_pass_ifelse (gcc::context *); +extern rtl_opt_pass *make_avr_pass_split_nzb (gcc::context *); extern rtl_opt_pass *make_avr_pass_split_after_peephole2 (gcc::context *); #ifdef RTX_CODE extern bool avr_casei_sequence_check_operands (rtx *xop); diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index 71c03b4..b192a12 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -567,6 +567,7 @@ avr_option_override (void) { opt_pass *extra_peephole2 = g->get_passes ()->get_pass_peephole2 ()->clone (); + extra_peephole2->name = "avr-peep2-after-fuse-move"; register_pass_info peep2_2_info = { extra_peephole2, "avr-fuse-move", 1, PASS_POS_INSERT_AFTER }; @@ -11736,6 +11737,23 @@ avr_handle_isr_attribute (tree node, tree *attrs, const char *name) } +/* Helper for `avr_insert_attributes'. + Return the section name from attribute "section" in attribute list ATTRS. + When no "section" attribute is present, then return nullptr. */ + +static const char * +avr_attrs_section_name (tree attrs) +{ + if (tree a_sec = lookup_attribute ("section", attrs)) + if (TREE_VALUE (a_sec)) + if (tree t_section_name = TREE_VALUE (TREE_VALUE (a_sec))) + if (TREE_CODE (t_section_name) == STRING_CST) + return TREE_STRING_POINTER (t_section_name); + + return nullptr; +} + + /* Implement `TARGET_INSERT_ATTRIBUTES'. */ static void @@ -11768,25 +11786,31 @@ avr_insert_attributes (tree node, tree *attributes) NULL, *attributes); } + const char *section_name = avr_attrs_section_name (*attributes); + + // When the function is in an .initN or .finiN section, then add "used" + // since such functions are never called. + if (section_name + && strlen (section_name) == strlen (".init*") + && IN_RANGE (section_name[5], '0', '9') + && (startswith (section_name, ".init") + || startswith (section_name, ".fini")) + && !lookup_attribute ("used", *attributes)) + { + *attributes = tree_cons (get_identifier ("used"), NULL, *attributes); + } + #if defined WITH_AVRLIBC if (avropt_call_main == 0 && TREE_CODE (node) == FUNCTION_DECL && MAIN_NAME_P (DECL_NAME (node))) { - const char *s_section_name = nullptr; + bool in_init9_p = section_name && !strcmp (section_name, ".init9"); - if (tree a_sec = lookup_attribute ("section", *attributes)) - if (TREE_VALUE (a_sec)) - if (tree t_section_name = TREE_VALUE (TREE_VALUE (a_sec))) - if (TREE_CODE (t_section_name) == STRING_CST) - s_section_name = TREE_STRING_POINTER (t_section_name); - - bool in_init9_p = s_section_name && !strcmp (s_section_name, ".init9"); - - if (s_section_name && !in_init9_p) + if (section_name && !in_init9_p) { warning (OPT_Wattributes, "%<section(\"%s\")%> attribute on main" - " function inhibits %<-mno-call-main%>", s_section_name); + " function inhibits %<-mno-call-main%>", section_name); } else { @@ -12683,6 +12707,50 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, } } + // Insns with nonzero_bits() == 1 in the condition. + if (avropt_use_nonzero_bits + && mode == QImode + && (code == AND || code == IOR || code == XOR) + && REG_P (XEXP (x, 1))) + { + // "*nzb=1.<code>.lsr_split" + // "*nzb=1.<code>.lsr.not_split" + bool is_nzb = (GET_CODE (XEXP (x, 0)) == LSHIFTRT + && (REG_P (XEXP (XEXP (x, 0), 0)) + || GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR) + && const_0_to_7_operand (XEXP (XEXP (x, 0), 1), QImode)); + // "*nzb=1.<code>.zerox_split" + // "*nzb=1.<code>.zerox.not_split" + is_nzb |= (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT + && (REG_P (XEXP (XEXP (x, 0), 0)) + || GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR) + && const1_operand (XEXP (XEXP (x, 0), 1), QImode) + && const_0_to_7_operand (XEXP (XEXP (x, 0), 2), QImode)); + // "*nzb=1.<code>.ge0_split" + is_nzb |= (GET_CODE (XEXP (x, 0)) == GE + && REG_P (XEXP (XEXP (x, 0), 0)) + && const0_operand (XEXP (XEXP (x, 0), 1), QImode)); + if (is_nzb) + { + *total = COSTS_N_INSNS (code == XOR ? 3 : 2); + return true; + } + } + + // Insn "*nzb=1.ior.ashift_split" with nonzero_bits() == 1 in the condition. + if (avropt_use_nonzero_bits + && mode == QImode + && code == IOR + && REG_P (XEXP (x, 1)) + && GET_CODE (XEXP (x, 0)) == ASHIFT + && REG_P (XEXP (XEXP (x, 0), 0)) + && CONST_INT_P (XEXP (XEXP (x, 0), 1))) + { + *total = COSTS_N_INSNS (2); + return true; + } + + switch (code) { case CONST_INT: @@ -13661,6 +13729,28 @@ avr_rtx_costs_1 (rtx x, machine_mode mode, int outer_code, *total += avr_operand_rtx_cost (XEXP (x, 0), mode, code, 0, speed); return true; + case GE: + if (mode == QImode + && REG_P (XEXP (x, 0)) + && XEXP (x, 1) == const0_rtx) + { + *total = COSTS_N_INSNS (3); + return true; + } + break; + + case ZERO_EXTRACT: + if (mode == QImode + && REG_P (XEXP (x, 0)) + && XEXP (x, 1) == const1_rtx + && CONST_INT_P (XEXP (x, 2))) + { + int bpos = INTVAL (XEXP (x, 2)); + *total = COSTS_N_INSNS (bpos == 0 ? 1 : bpos == 1 ? 2 : 3); + return true; + } + break; + case COMPARE: switch (GET_MODE (XEXP (x, 0))) { @@ -15148,6 +15238,46 @@ avr_emit3_fix_outputs (rtx (*gen)(rtx,rtx,rtx), rtx *op, } +/* A helper for the insn condition of "*nzb=1.<code>.lsr[.not]_split" + where <code> is AND, IOR or XOR. Return true when + + OP[0] <code>= OP[1] >> OP[2] + + can be performed by means of the code of "*nzb=1.<code>.zerox", i.e. + + OP[0] <code>= OP[1].OP[2] + + For example, when OP[0] is in { 0, 1 }, then R24 &= R10.4 + can be performed by means of SBRS R10,4 $ CLR R24. + Notice that the constraint of OP[3] is "0". */ + +bool +avr_nonzero_bits_lsr_operands_p (rtx_code code, rtx *op) +{ + if (reload_completed) + return false; + + const auto offs = INTVAL (op[2]); + const auto op1_non0 = nonzero_bits (op[1], QImode); + const auto op3_non0 = nonzero_bits (op[3], QImode); + + switch (code) + { + default: + gcc_unreachable (); + + case IOR: + case XOR: + return op1_non0 >> offs == 1; + + case AND: + return op3_non0 == 1; + } + + return false; +} + + /* Worker function for cpymemhi expander. XOP[0] Destination as MEM:BLK XOP[1] Source " " diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 06e31aa..1c4e44d 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -84,6 +84,7 @@ [UNSPEC_STRLEN UNSPEC_CPYMEM UNSPEC_INDEX_JMP + UNSPEC_NZB UNSPEC_FMUL UNSPEC_FMULS UNSPEC_FMULSU @@ -175,6 +176,10 @@ no" (const_string "no")) +(define_attr "nzb" + "yes, no" + (const_string "no")) + ;; Flavours of instruction set architecture (ISA), used in enabled attribute ;; mov : ISA has no MOVW movw : ISA has MOVW @@ -10916,6 +10921,216 @@ DONE; }) +;; Patterns for -muse-nonzero-bits use nonzero_bits() in their condition, +;; which makes possible some more optimizations. +;; Since combine may add clobber of REG_CC, we must make sure that there are +;; no other routes to synthesize such patterns. We use an UNSPEC for that. +;; As insns are not supposed to use stuff like nonzero_bits() in their +;; condition, we split the insns right after reload. For CFG reasons we have +;; to do the splits by hand in avr_pass_split_nzb. All insns that must be +;; split by that pass must have insn attribute "nzb" set to "yes". Moreover, +;; the insns to split must be single_sets and must not touch control flow. + +(define_code_attr nzb_constr_rdr [(and "r") (ior "d") (xor "r")]) +(define_code_attr nzb_use1_nnr [(and "n") (ior "n") (xor "r")]) + +(define_insn_and_split "*nzb=1.<code>.zerox_split" + [(set (match_operand:QI 0 "register_operand") + (bitop:QI (zero_extract:QI (match_operand:QI 1 "register_operand") + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand")) + (match_operand:QI 3 "register_operand")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && (<CODE> == IOR || <CODE> == XOR + || nonzero_bits (operands[3], QImode) == 1)" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + [(parallel [(set (match_dup 0) + (bitop:QI (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 2)) + (unspec:QI [(match_dup 3) + ] UNSPEC_NZB))) + (use (const_int 1)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn "*nzb=1.<code>.zerox" + [(set (match_operand:QI 0 "register_operand" "=<nzb_constr_rdr>") + (bitop:QI (zero_extract:QI (match_operand:QI 1 "register_operand" "r") + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n")) + (unspec:QI [(match_operand:QI 3 "register_operand" "0") + ] UNSPEC_NZB))) + (use (match_operand:QI 4 "nonmemory_operand" "<nzb_use1_nnr>")) + (clobber (reg:CC REG_CC))] + "optimize && avropt_use_nonzero_bits" + { + if (<CODE> == AND) + return "sbrs %1,%2\;clr %0"; + else if (<CODE> == IOR) + return "sbrc %1,%2\;ori %0,1"; + else if (<CODE> == XOR) + return "sbrc %1,%2\;eor %0,%4"; + else + gcc_unreachable (); + } + [(set_attr "length" "2")]) + +(define_insn_and_split "*nzb=1.<code>.lsr_split" + [(set (match_operand:QI 0 "register_operand") + (bitop:QI (lshiftrt:QI (match_operand:QI 1 "register_operand") + (match_operand:QI 2 "const_0_to_7_operand")) + (match_operand:QI 3 "register_operand")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && avr_nonzero_bits_lsr_operands_p (<CODE>, operands)" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + [(parallel [(set (match_dup 0) + (bitop:QI (zero_extract:QI (match_dup 1) + (const_int 1) + (match_dup 2)) + (unspec:QI [(match_dup 3) + ] UNSPEC_NZB))) + (use (const_int 1)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn_and_split "*nzb=1.<code>.zerox.not_split" + [(set (match_operand:QI 0 "register_operand") + (bitop:QI (zero_extract:QI (xor:QI (match_operand:QI 1 "register_operand") + (match_operand:QI 4 "const_int_operand")) + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand")) + (match_operand:QI 3 "register_operand")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && INTVAL (operands[2]) == exact_log2 (0xff & INTVAL (operands[4])) + && (<CODE> == IOR + || nonzero_bits (operands[3], QImode) == 1)" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + ; "*nzb=1.<code>.zerox.not" + [(parallel [(set (match_dup 0) + (bitop:QI (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (match_dup 2)) + (unspec:QI [(match_dup 3) + ] UNSPEC_NZB))) + (use (const_int 1)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn_and_split "*nzb=1.<code>.lsr.not_split" + [(set (match_operand:QI 0 "register_operand") + (bitop:QI (lshiftrt:QI (xor:QI (match_operand:QI 1 "register_operand") + (match_operand:QI 4 "const_int_operand")) + (match_operand:QI 2 "const_0_to_7_operand")) + (match_operand:QI 3 "register_operand")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && INTVAL (operands[2]) == exact_log2 (0xff & INTVAL (operands[4])) + && avr_nonzero_bits_lsr_operands_p (<CODE>, operands)" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + ; "*nzb=1.<code>.zerox.not" + [(parallel [(set (match_dup 0) + (bitop:QI (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (match_dup 2)) + (unspec:QI [(match_dup 3) + ] UNSPEC_NZB))) + (use (const_int 1)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn_and_split "*nzb=1.<code>.ge0_split" + [(set (match_operand:QI 0 "register_operand") + (bitop:QI (ge:QI (match_operand:QI 1 "register_operand") + (const_int 0)) + (match_operand:QI 2 "register_operand")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && (<CODE> == IOR || <CODE> == XOR + || nonzero_bits (operands[2], QImode) == 1)" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + ; "*nzb=1.<code>.zerox.not" + [(parallel [(set (match_dup 0) + (bitop:QI (zero_extract:QI (not:QI (match_dup 1)) + (const_int 1) + (const_int 7)) + (unspec:QI [(match_dup 2) + ] UNSPEC_NZB))) + (use (const_int 1)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn "*nzb=1.<code>.zerox.not" + [(set (match_operand:QI 0 "register_operand" "=<nzb_constr_rdr>") + (bitop:QI (zero_extract:QI (not:QI (match_operand:QI 1 "register_operand" "r")) + (const_int 1) + (match_operand:QI 2 "const_0_to_7_operand" "n")) + (unspec:QI [(match_operand:QI 3 "register_operand" "0") + ] UNSPEC_NZB))) + (use (match_operand:QI 4 "nonmemory_operand" "<nzb_use1_nnr>")) + (clobber (reg:CC REG_CC))] + "optimize && avropt_use_nonzero_bits" + { + if (<CODE> == AND) + return "sbrc %1,%2\;clr %0"; + else if (<CODE> == IOR) + return "sbrs %1,%2\;ori %0,1"; + else if (<CODE> == XOR) + return "sbrs %1,%2\;eor %0,%4"; + else + gcc_unreachable (); + } + [(set_attr "length" "2")]) + +(define_insn_and_split "*nzb=1.ior.ashift_split" + [(set (match_operand:QI 0 "register_operand" "=d") + (ior:QI (ashift:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "const_0_to_7_operand" "n")) + (match_operand:QI 3 "register_operand" "0")))] + "optimize && avropt_use_nonzero_bits + && !reload_completed + && nonzero_bits (operands[1], QImode) == 1" + { gcc_unreachable (); } + "optimize && avropt_use_nonzero_bits + && !reload_completed" + [(parallel [(set (match_dup 0) + (unspec:QI [(ior:QI (ashift:QI (match_dup 1) + (match_dup 2)) + (match_dup 3)) + ] UNSPEC_NZB)) + (clobber (reg:CC REG_CC))])] + "" + [(set_attr "nzb" "yes")]) + +(define_insn "*nzb=1.ior.ashift" + [(set (match_operand:QI 0 "register_operand" "=d") + (unspec:QI [(ior:QI (ashift:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "const_0_to_7_operand" "n")) + (match_operand:QI 3 "register_operand" "0")) + ] UNSPEC_NZB)) + (clobber (reg:CC REG_CC))] + "optimize && avropt_use_nonzero_bits" + "sbrc %1,0\;ori %0,1<<%2" + [(set_attr "length" "2")]) + ;; Work around PR115307: Early passes expand isinf/f/l to a bloat. ;; These passes do not consider costs, and there is no way to diff --git a/gcc/config/avr/avr.opt b/gcc/config/avr/avr.opt index d22a118..fcd2bf6 100644 --- a/gcc/config/avr/avr.opt +++ b/gcc/config/avr/avr.opt @@ -65,6 +65,10 @@ mpr118012 Target Var(avropt_pr118012) UInteger Init(1) Undocumented This option is on per default in order to work around PR118012. +muse-nonzero-bits +Target Var(avropt_use_nonzero_bits) UInteger Init(0) Optimization +Optimization. Allow to use nonzero_bits() in some insn conditions. + 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/config/avr/avr.opt.urls b/gcc/config/avr/avr.opt.urls index 64cf060..662fdee 100644 --- a/gcc/config/avr/avr.opt.urls +++ b/gcc/config/avr/avr.opt.urls @@ -26,6 +26,9 @@ UrlSuffix(gcc/AVR-Options.html#index-mskip-bug) mrmw UrlSuffix(gcc/AVR-Options.html#index-mrmw) +muse-nonzero-bits +UrlSuffix(gcc/AVR-Options.html#index-muse-nonzero-bits) + mshort-calls UrlSuffix(gcc/AVR-Options.html#index-mshort-calls) |