diff options
Diffstat (limited to 'gcc/config/sh/sh.md')
-rw-r--r-- | gcc/config/sh/sh.md | 231 |
1 files changed, 131 insertions, 100 deletions
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 1b6c284e388..2ef4a1a4ee0 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -3708,6 +3708,26 @@ label: "xor %2,%0" [(set_attr "type" "arith")]) +;; The *logical_op_t pattern helps combine eliminating sign/zero extensions +;; of results where one of the inputs is a T bit store. Notice that this +;; pattern must not match during reload. If reload picks this pattern it +;; will be impossible to split it afterwards. +(define_insn_and_split "*logical_op_t" + [(set (match_operand:SI 0 "arith_reg_dest") + (match_operator:SI 3 "logical_operator" + [(match_operand:SI 1 "arith_reg_operand") + (match_operand:SI 2 "t_reg_operand")]))] + "TARGET_SH1 && can_create_pseudo_p ()" + "#" + "&& 1" + [(set (match_dup 4) (reg:SI T_REG)) + (set (match_dup 0) (match_dup 3))] +{ + operands[4] = gen_reg_rtx (SImode); + operands[3] = gen_rtx_fmt_ee (GET_CODE (operands[3]), SImode, + operands[1], operands[4]); +}) + (define_insn "*xorsi3_media" [(set (match_operand:SI 0 "arith_reg_dest" "=r,r") (xor:SI (match_operand:SI 1 "logical_reg_operand" "%r,r") @@ -5590,39 +5610,7 @@ label: eliminated. Notice that this also helps the *cbranch_t splitter when it tries to post-combine tests and conditional branches, as it does not check for zero extensions. */ - rtx ext_reg; - if (REG_P (operands[1])) - ext_reg = operands[1]; - else if (GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1]))) - ext_reg = SUBREG_REG (operands[1]); - else - FAIL; - - /* Reg moves must be of the same mode. */ - if (GET_MODE (ext_reg) != SImode) - FAIL; - - operands[2] = NULL_RTX; - for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX; - i = prev_nonnote_insn_bb (i)) - { - if (LABEL_P (i) || BARRIER_P (i)) - break; - if (!NONJUMP_INSN_P (i)) - continue; - - if (reg_set_p (ext_reg, i)) - { - rtx set_op = XEXP (set_of (ext_reg, i), 1); - if (set_op == NULL_RTX) - break; - if (t_reg_operand (set_op, VOIDmode) - || negt_reg_operand (set_op, VOIDmode)) - operands[2] = ext_reg; - break; - } - } - + operands[2] = sh_try_omit_signzero_extend (operands[1], curr_insn); if (operands[2] == NULL_RTX) FAIL; } @@ -5850,11 +5838,23 @@ label: subreg_lowpart_offset (SImode, GET_MODE (op1))); }) -(define_insn "*extend<mode>si2_compact_reg" +(define_insn_and_split "*extend<mode>si2_compact_reg" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (sign_extend:SI (match_operand:QIHI 1 "arith_reg_operand" "r")))] "TARGET_SH1" "exts.<bw> %1,%0" + "&& can_create_pseudo_p ()" + [(set (match_dup 0) (match_dup 2))] +{ + /* Sometimes combine fails to combine a T bit or negated T bit store to a + reg with a following sign extension. In the split pass after combine, + try to figure the extended reg was set. If it originated from the T + bit we can replace the sign extension with a reg move, which will be + eliminated. */ + operands[2] = sh_try_omit_signzero_extend (operands[1], curr_insn); + if (operands[2] == NULL_RTX) + FAIL; +} [(set_attr "type" "arith")]) ;; FIXME: Fold non-SH2A and SH2A alternatives with "enabled" attribute. @@ -6629,10 +6629,19 @@ label: ;; picked to load/store regs. If the regs regs are on the stack reload will ;; try other insns and not stick to movqi_reg_reg. ;; The same applies to the movhi variants. +;; +;; Notice, that T bit is not allowed as a mov src operand here. This is to +;; avoid things like (set (reg:QI) (subreg:QI (reg:SI T_REG) 0)), which +;; introduces zero extensions after T bit stores and redundant reg copies. +;; +;; FIXME: We can't use 'arith_reg_operand' (which disallows T_REG) as a +;; predicate for the mov src operand because reload will have trouble +;; reloading MAC subregs otherwise. For that probably special patterns +;; would be required. (define_insn "*mov<mode>_reg_reg" [(set (match_operand:QIHI 0 "arith_reg_dest" "=r") (match_operand:QIHI 1 "register_operand" "r"))] - "TARGET_SH1" + "TARGET_SH1 && !t_reg_operand (operands[1], VOIDmode)" "mov %1,%0" [(set_attr "type" "move")]) @@ -8178,28 +8187,17 @@ label: rtx testing_insn = NULL_RTX; rtx tested_reg = NULL_RTX; - for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX; - i = prev_nonnote_insn_bb (i)) + set_of_reg s0 = sh_find_set_of_reg (get_t_reg_rtx (), curr_insn, + prev_nonnote_insn_bb); + if (s0.set_src != NULL_RTX + && GET_CODE (s0.set_src) == EQ + && REG_P (XEXP (s0.set_src, 0)) + && satisfies_constraint_Z (XEXP (s0.set_src, 1))) { - if (LABEL_P (i) || BARRIER_P (i)) - break; - if (!NONJUMP_INSN_P (i)) - continue; - - rtx p = PATTERN (i); - if (p != NULL_RTX - && GET_CODE (p) == SET && t_reg_operand (XEXP (p, 0), VOIDmode) - && GET_CODE (XEXP (p, 1)) == EQ - && REG_P (XEXP (XEXP (p, 1), 0)) - && satisfies_constraint_Z (XEXP (XEXP (p, 1), 1))) - { - testing_insn = i; - tested_reg = XEXP (XEXP (p, 1), 0); - break; - } + testing_insn = s0.insn; + tested_reg = XEXP (s0.set_src, 0); } - - if (testing_insn == NULL_RTX) + else FAIL; /* Continue scanning the insns backwards and try to find the insn that @@ -8213,47 +8211,37 @@ label: if (reg_used_between_p (get_t_reg_rtx (), testing_insn, curr_insn)) FAIL; - for (rtx i = prev_nonnote_insn_bb (testing_insn); i != NULL_RTX; - i = prev_nonnote_insn_bb (i)) + while (true) { - if (LABEL_P (i) || BARRIER_P (i)) + set_of_reg s1 = sh_find_set_of_reg (tested_reg, s0.insn, + prev_nonnote_insn_bb); + if (s1.set_src == NULL_RTX) break; - if (!NONJUMP_INSN_P (i)) - continue; - if (reg_set_p (tested_reg, i)) + if (t_reg_operand (s1.set_src, VOIDmode)) + operands[2] = GEN_INT (treg_value ^ 1); + else if (negt_reg_operand (s1.set_src, VOIDmode)) + operands[2] = GEN_INT (treg_value); + else if (REG_P (s1.set_src)) { - const_rtx tested_reg_set = set_of (tested_reg, i); - - /* It could also be a clobber... */ - if (tested_reg_set == NULL_RTX || GET_CODE (tested_reg_set) != SET) - break; - - rtx set_op1 = XEXP (tested_reg_set, 1); - if (t_reg_operand (set_op1, VOIDmode)) - operands[2] = GEN_INT (treg_value ^ 1); - else if (negt_reg_operand (set_op1, VOIDmode)) - operands[2] = GEN_INT (treg_value); - else if (REG_P (set_op1)) - { - /* If it's a reg-reg copy follow the copied reg. This can - happen e.g. when T bit store zero-extensions are - eliminated. */ - tested_reg = set_op1; - continue; - } + /* If it's a reg-reg copy follow the copied reg. This can + happen e.g. when T bit store zero-extensions are + eliminated. */ + tested_reg = s1.set_src; + s0.insn = s1.insn; + continue; + } - /* It's only safe to remove the testing insn if the T bit is not - modified between the testing insn and the insn that stores the - T bit. Notice that some T bit stores such as negc also modify - the T bit. */ - if (modified_between_p (get_t_reg_rtx (), i, testing_insn) - || modified_in_p (get_t_reg_rtx (), i)) - operands[2] = NULL_RTX; + /* It's only safe to remove the testing insn if the T bit is not + modified between the testing insn and the insn that stores the + T bit. Notice that some T bit stores such as negc also modify + the T bit. */ + if (modified_between_p (get_t_reg_rtx (), s1.insn, testing_insn) + || modified_in_p (get_t_reg_rtx (), s1.insn)) + operands[2] = NULL_RTX; - break; - } - } + break; + } if (operands[2] == NULL_RTX) FAIL; @@ -8486,11 +8474,14 @@ label: (pc))) (set (match_dup 0) (plus:SI (match_dup 0) (const_int -1))) - (clobber (reg:SI T_REG))])] + (clobber (reg:SI T_REG))]) + (match_operand 5 "" "")] "TARGET_SH2" { if (GET_MODE (operands[0]) != SImode) FAIL; + emit_insn (gen_doloop_end_split (operands[0], operands[4], operands[0])); + DONE; }) (define_insn_and_split "doloop_end_split" @@ -10277,6 +10268,47 @@ label: "mov.<bwl> %0,@(0,gbr)" [(set_attr "type" "store")]) +;; DImode memory accesses have to be split in two SImode accesses. +;; Split them before reload, so that it gets a better chance to figure out +;; how to deal with the R0 restriction for the individual SImode accesses. +;; Do not match this insn during or after reload because it can't be split +;; afterwards. +(define_insn_and_split "*movdi_gbr_load" + [(set (match_operand:DI 0 "register_operand") + (match_operand:DI 1 "gbr_address_mem"))] + "TARGET_SH1 && can_create_pseudo_p ()" + "#" + "&& 1" + [(set (match_dup 3) (match_dup 5)) + (set (match_dup 4) (match_dup 6))] +{ + /* Swap low/high part load order on little endian, so that the result reg + of the second load can be used better. */ + int off = TARGET_LITTLE_ENDIAN ? 1 : 0; + operands[3 + off] = gen_lowpart (SImode, operands[0]); + operands[5 + off] = gen_lowpart (SImode, operands[1]); + operands[4 - off] = gen_highpart (SImode, operands[0]); + operands[6 - off] = gen_highpart (SImode, operands[1]); +}) + +(define_insn_and_split "*movdi_gbr_store" + [(set (match_operand:DI 0 "gbr_address_mem") + (match_operand:DI 1 "register_operand"))] + "TARGET_SH1 && can_create_pseudo_p ()" + "#" + "&& 1" + [(set (match_dup 3) (match_dup 5)) + (set (match_dup 4) (match_dup 6))] +{ + /* Swap low/high part store order on big endian, so that stores of function + call results can save a reg copy. */ + int off = TARGET_LITTLE_ENDIAN ? 0 : 1; + operands[3 + off] = gen_lowpart (SImode, operands[0]); + operands[5 + off] = gen_lowpart (SImode, operands[1]); + operands[4 - off] = gen_highpart (SImode, operands[0]); + operands[6 - off] = gen_highpart (SImode, operands[1]); +}) + ;; Sometimes memory accesses do not get combined with the store_gbr insn, ;; in particular when the displacements are in the range of the regular move ;; insns. Thus, in the first split pass after the combine pass we search @@ -10287,15 +10319,15 @@ label: ;; other operand) and there's no point of doing it if the GBR is not ;; referenced in a function at all. (define_split - [(set (match_operand:QIHISI 0 "register_operand") - (match_operand:QIHISI 1 "memory_operand"))] + [(set (match_operand:QIHISIDI 0 "register_operand") + (match_operand:QIHISIDI 1 "memory_operand"))] "TARGET_SH1 && !reload_in_progress && !reload_completed && df_regs_ever_live_p (GBR_REG)" [(set (match_dup 0) (match_dup 1))] { rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]); if (gbr_mem != NULL_RTX) - operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem); + operands[1] = replace_equiv_address (operands[1], gbr_mem); else FAIL; }) @@ -10309,7 +10341,7 @@ label: { rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[1]); if (gbr_mem != NULL_RTX) - operands[1] = change_address (operands[1], GET_MODE (operands[1]), gbr_mem); + operands[1] = replace_equiv_address (operands[1], gbr_mem); else FAIL; }) @@ -10328,23 +10360,22 @@ label: if (gbr_mem != NULL_RTX) { operands[2] = gen_reg_rtx (GET_MODE (operands[1])); - operands[1] = change_address (operands[1], GET_MODE (operands[1]), - gbr_mem); + operands[1] = replace_equiv_address (operands[1], gbr_mem); } else FAIL; }) (define_split - [(set (match_operand:QIHISI 0 "memory_operand") - (match_operand:QIHISI 1 "register_operand"))] + [(set (match_operand:QIHISIDI 0 "memory_operand") + (match_operand:QIHISIDI 1 "register_operand"))] "TARGET_SH1 && !reload_in_progress && !reload_completed && df_regs_ever_live_p (GBR_REG)" [(set (match_dup 0) (match_dup 1))] { rtx gbr_mem = sh_find_equiv_gbr_addr (curr_insn, operands[0]); if (gbr_mem != NULL_RTX) - operands[0] = change_address (operands[0], GET_MODE (operands[0]), gbr_mem); + operands[0] = replace_equiv_address (operands[0], gbr_mem); else FAIL; }) |