diff options
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r-- | gcc/config/sh/sh.c | 281 |
1 files changed, 220 insertions, 61 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index d23a45a..e25cd42 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -105,12 +105,6 @@ static int skip_cycles = 0; and returned from sh_reorder2. */ static short cached_can_issue_more; -/* Saved operands from the last compare to use when we generate an scc - or bcc insn. */ - -rtx sh_compare_op0; -rtx sh_compare_op1; - /* Provides the class number of the smallest class containing reg number. */ @@ -1677,10 +1671,26 @@ expand_cbranchdi4 (rtx *operands, enum rtx_code comparison) return true; } +/* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4. */ + +static void +sh_emit_set_t_insn (rtx insn, enum machine_mode mode) +{ + if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT) + { + insn = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, insn, + gen_rtx_USE (VOIDmode, get_fpscr_rtx ()))); + (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn); + } + else + emit_insn (insn); +} + /* Prepare the operands for an scc instruction; make sure that the - compare has been done. */ -rtx -prepare_scc_operands (enum rtx_code code) + compare has been done and the result is in T_REG. */ +void +sh_emit_scc_to_t (enum rtx_code code, rtx op0, rtx op1) { rtx t_reg = gen_rtx_REG (SImode, T_REG); enum rtx_code oldcode = code; @@ -1709,77 +1719,222 @@ prepare_scc_operands (enum rtx_code code) } if (code != oldcode) { - rtx tmp = sh_compare_op0; - sh_compare_op0 = sh_compare_op1; - sh_compare_op1 = tmp; + rtx tmp = op0; + op0 = op1; + op1 = tmp; } - mode = GET_MODE (sh_compare_op0); + mode = GET_MODE (op0); if (mode == VOIDmode) - mode = GET_MODE (sh_compare_op1); + mode = GET_MODE (op1); - sh_compare_op0 = force_reg (mode, sh_compare_op0); + op0 = force_reg (mode, op0); if ((code != EQ && code != NE - && (sh_compare_op1 != const0_rtx + && (op1 != const0_rtx || code == GTU || code == GEU || code == LTU || code == LEU)) - || (mode == DImode && sh_compare_op1 != const0_rtx) + || (mode == DImode && op1 != const0_rtx) || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)) - sh_compare_op1 = force_reg (mode, sh_compare_op1); + op1 = force_reg (mode, op1); - if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT) - (mode == SFmode ? emit_sf_insn : emit_df_insn) - (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, - gen_rtx_SET (VOIDmode, t_reg, - gen_rtx_fmt_ee (code, SImode, - sh_compare_op0, sh_compare_op1)), - gen_rtx_USE (VOIDmode, get_fpscr_rtx ())))); - else - emit_insn (gen_rtx_SET (VOIDmode, t_reg, - gen_rtx_fmt_ee (code, SImode, - sh_compare_op0, sh_compare_op1))); + sh_emit_set_t_insn (gen_rtx_SET (VOIDmode, t_reg, + gen_rtx_fmt_ee (code, SImode, op0, op1)), + mode); +} - return t_reg; +rtx +sh_emit_cheap_store_flag (enum machine_mode mode, enum rtx_code code, + rtx op0, rtx op1) +{ + rtx target = gen_reg_rtx (SImode); + rtx tmp; + + gcc_assert (TARGET_SHMEDIA); + switch (code) + { + case EQ: + case GT: + case LT: + case UNORDERED: + case GTU: + case LTU: + tmp = gen_rtx_fmt_ee (code, SImode, op0, op1); + emit_insn (gen_cstore4_media (target, tmp, op0, op1)); + code = NE; + break; + + case NE: + case GE: + case LE: + case ORDERED: + case GEU: + case LEU: + tmp = gen_rtx_fmt_ee (reverse_condition (code), mode, op0, op1); + emit_insn (gen_cstore4_media (target, tmp, op0, op1)); + code = EQ; + break; + + case UNEQ: + case UNGE: + case UNGT: + case UNLE: + case UNLT: + case LTGT: + return NULL_RTX; + + default: + gcc_unreachable (); + } + + if (mode == DImode) + { + rtx t2 = gen_reg_rtx (DImode); + emit_insn (gen_extendsidi2 (t2, target)); + target = t2; + } + + return gen_rtx_fmt_ee (code, VOIDmode, target, const0_rtx); } /* Called from the md file, set up the operands of a compare instruction. */ void -from_compare (rtx *operands, int code) +sh_emit_compare_and_branch (rtx *operands, enum machine_mode mode) { - enum machine_mode mode = GET_MODE (sh_compare_op0); - rtx insn; - if (mode == VOIDmode) - mode = GET_MODE (sh_compare_op1); - if (code != EQ - || mode == DImode - || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)) + enum rtx_code code = GET_CODE (operands[0]); + enum rtx_code branch_code; + rtx op0 = operands[1]; + rtx op1 = operands[2]; + rtx insn, tem; + bool need_ccmpeq = false; + + if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT) { - /* Force args into regs, since we can't use constants here. */ - sh_compare_op0 = force_reg (mode, sh_compare_op0); - if (sh_compare_op1 != const0_rtx - || code == GTU || code == GEU - || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)) - sh_compare_op1 = force_reg (mode, sh_compare_op1); + op0 = force_reg (mode, op0); + op1 = force_reg (mode, op1); } - if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT && code == GE) + else { - from_compare (operands, GT); - insn = gen_ieee_ccmpeqsf_t (sh_compare_op0, sh_compare_op1); + if (code != EQ || mode == DImode) + { + /* Force args into regs, since we can't use constants here. */ + op0 = force_reg (mode, op0); + if (op1 != const0_rtx || code == GTU || code == GEU) + op1 = force_reg (mode, op1); + } } + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + if (code == LT + || (code == LE && TARGET_IEEE && TARGET_SH2E) + || (code == GE && !(TARGET_IEEE && TARGET_SH2E))) + { + tem = op0, op0 = op1, op1 = tem; + code = swap_condition (code); + } + + /* GE becomes fcmp/gt+fcmp/eq, for SH2E and TARGET_IEEE only. */ + if (code == GE) + { + gcc_assert (TARGET_IEEE && TARGET_SH2E); + need_ccmpeq = true; + code = GT; + } + + /* Now we can have EQ, NE, GT, LE. NE and LE are then transformed + to EQ/GT respectively. */ + gcc_assert (code == EQ || code == GT || code == NE || code == LE); + } + + switch (code) + { + case EQ: + case GT: + case GE: + case GTU: + case GEU: + branch_code = code; + break; + case NE: + case LT: + case LE: + case LTU: + case LEU: + branch_code = reverse_condition (code); + break; + default: + gcc_unreachable (); + } + + insn = gen_rtx_SET (VOIDmode, + gen_rtx_REG (SImode, T_REG), + gen_rtx_fmt_ee (branch_code, SImode, op0, op1)); + + sh_emit_set_t_insn (insn, mode); + if (need_ccmpeq) + sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode); + + if (branch_code == code) + emit_jump_insn (gen_branch_true (operands[3])); else - insn = gen_rtx_SET (VOIDmode, - gen_rtx_REG (SImode, T_REG), - gen_rtx_fmt_ee ((enum rtx_code) code, SImode, - sh_compare_op0, sh_compare_op1)); - if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT) + emit_jump_insn (gen_branch_false (operands[3])); +} + +void +sh_emit_compare_and_set (rtx *operands, enum machine_mode mode) +{ + enum rtx_code code = GET_CODE (operands[1]); + rtx op0 = operands[2]; + rtx op1 = operands[3]; + rtx lab = NULL_RTX; + bool invert = false; + rtx tem; + + op0 = force_reg (mode, op0); + if ((code != EQ && code != NE + && (op1 != const0_rtx + || code == GTU || code == GEU || code == LTU || code == LEU)) + || (mode == DImode && op1 != const0_rtx) + || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)) + op1 = force_reg (mode, op1); + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) { - insn = gen_rtx_PARALLEL (VOIDmode, - gen_rtvec (2, insn, - gen_rtx_USE (VOIDmode, get_fpscr_rtx ()))); - (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn); + if (code == LT || code == LE) + { + code = swap_condition (code); + tem = op0, op0 = op1, op1 = tem; + } + if (code == GE) + { + if (TARGET_IEEE) + { + lab = gen_label_rtx (); + sh_emit_scc_to_t (EQ, op0, op1); + emit_jump_insn (gen_branch_true (lab)); + code = GT; + } + else + { + code = LT; + invert = true; + } + } } + + if (code == NE) + { + code = EQ; + invert = true; + } + + sh_emit_scc_to_t (code, op0, op1); + if (lab) + emit_label (lab); + if (invert) + emit_insn (gen_movnegt (operands[0])); else - emit_insn (insn); + emit_move_insn (operands[0], gen_rtx_REG (SImode, T_REG)); } /* Functions to output assembly code. */ @@ -10782,17 +10937,21 @@ sh_get_pr_initial_val (void) } int -sh_expand_t_scc (enum rtx_code code, rtx target) +sh_expand_t_scc (rtx operands[]) { + enum rtx_code code = GET_CODE (operands[1]); + rtx target = operands[0]; + rtx op0 = operands[2]; + rtx op1 = operands[3]; rtx result = target; HOST_WIDE_INT val; - if (GET_CODE (sh_compare_op0) != REG || REGNO (sh_compare_op0) != T_REG - || GET_CODE (sh_compare_op1) != CONST_INT) + if (GET_CODE (op0) != REG || REGNO (op0) != T_REG + || GET_CODE (op1) != CONST_INT) return 0; if (GET_CODE (result) != REG) result = gen_reg_rtx (SImode); - val = INTVAL (sh_compare_op1); + val = INTVAL (op1); if ((code == EQ && val == 1) || (code == NE && val == 0)) emit_insn (gen_movt (result)); else if (TARGET_SH2A && ((code == EQ && val == 0) |