summaryrefslogtreecommitdiffstats
path: root/gcc/config/sparc/sparc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sparc/sparc.c')
-rw-r--r--gcc/config/sparc/sparc.c278
1 files changed, 186 insertions, 92 deletions
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index ab2b57bbcd9..330c2c06981 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -280,10 +280,6 @@ static GTY(()) alias_set_type sparc_sr_alias_set;
/* The alias set for the structure return value. */
static GTY(()) alias_set_type struct_value_alias_set;
-/* Save the operands last given to a compare for use when we
- generate a scc or bcc insn. */
-rtx sparc_compare_op0, sparc_compare_op1;
-
/* Vector to say how input registers are mapped to output registers.
HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
eliminate it. You must use -fomit-frame-pointer to get that. */
@@ -2005,19 +2001,18 @@ select_cc_mode (enum rtx_code op, rtx x, rtx y ATTRIBUTE_UNUSED)
}
}
-/* Emit the compare insn and return the CC reg for a CODE comparison. */
+/* Emit the compare insn and return the CC reg for a CODE comparison
+ with operands X and Y. */
-rtx
-gen_compare_reg (enum rtx_code code)
+static rtx
+gen_compare_reg_1 (enum rtx_code code, rtx x, rtx y)
{
enum machine_mode mode;
- rtx x, y, cc_reg;
+ rtx cc_reg;
- if (GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) == MODE_CC)
- return sparc_compare_op0;
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
+ return x;
- x = sparc_compare_op0;
- y = sparc_compare_op1;
mode = SELECT_CC_MODE (code, x, y);
/* ??? We don't have movcc patterns so we cannot generate pseudo regs for the
@@ -2073,26 +2068,19 @@ gen_compare_reg (enum rtx_code code)
return cc_reg;
}
-/* Same as above but return the whole compare operator. */
+
+/* Emit the compare insn and return the CC reg for the comparison in CMP. */
rtx
-gen_compare_operator (enum rtx_code code)
+gen_compare_reg (rtx cmp)
{
- rtx cc_reg;
-
- if (GET_MODE (sparc_compare_op0) == TFmode && !TARGET_HARD_QUAD)
- code
- = sparc_emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, code);
-
- cc_reg = gen_compare_reg (code);
- return gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
+ return gen_compare_reg_1 (GET_CODE (cmp), XEXP (cmp, 0), XEXP (cmp, 1));
}
/* This function is used for v9 only.
+ DEST is the target of the Scc insn.
CODE is the code for an Scc's comparison.
- OPERANDS[0] is the target of the Scc insn.
- OPERANDS[1] is the value we compare against const0_rtx (which hasn't
- been generated yet).
+ X and Y are the values we compare.
This function is needed to turn
@@ -2106,53 +2094,50 @@ gen_compare_operator (enum rtx_code code)
IE: The instruction recognizer needs to see the mode of the comparison to
find the right instruction. We could use "gt:DI" right in the
- define_expand, but leaving it out allows us to handle DI, SI, etc.
+ define_expand, but leaving it out allows us to handle DI, SI, etc. */
- We refer to the global sparc compare operands sparc_compare_op0 and
- sparc_compare_op1. */
-
-int
-gen_v9_scc (enum rtx_code compare_code, register rtx *operands)
+static int
+gen_v9_scc (rtx dest, enum rtx_code compare_code, rtx x, rtx y)
{
if (! TARGET_ARCH64
- && (GET_MODE (sparc_compare_op0) == DImode
- || GET_MODE (operands[0]) == DImode))
+ && (GET_MODE (x) == DImode
+ || GET_MODE (dest) == DImode))
return 0;
/* Try to use the movrCC insns. */
if (TARGET_ARCH64
- && GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) == MODE_INT
- && sparc_compare_op1 == const0_rtx
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
+ && y == const0_rtx
&& v9_regcmp_p (compare_code))
{
- rtx op0 = sparc_compare_op0;
+ rtx op0 = x;
rtx temp;
/* Special case for op0 != 0. This can be done with one instruction if
- operands[0] == sparc_compare_op0. */
+ dest == x. */
if (compare_code == NE
- && GET_MODE (operands[0]) == DImode
- && rtx_equal_p (op0, operands[0]))
+ && GET_MODE (dest) == DImode
+ && rtx_equal_p (op0, dest))
{
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
gen_rtx_IF_THEN_ELSE (DImode,
gen_rtx_fmt_ee (compare_code, DImode,
op0, const0_rtx),
const1_rtx,
- operands[0])));
+ dest)));
return 1;
}
- if (reg_overlap_mentioned_p (operands[0], op0))
+ if (reg_overlap_mentioned_p (dest, op0))
{
- /* Handle the case where operands[0] == sparc_compare_op0.
+ /* Handle the case where dest == x.
We "early clobber" the result. */
- op0 = gen_reg_rtx (GET_MODE (sparc_compare_op0));
- emit_move_insn (op0, sparc_compare_op0);
+ op0 = gen_reg_rtx (GET_MODE (x));
+ emit_move_insn (op0, x);
}
- emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
+ emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
if (GET_MODE (op0) != DImode)
{
temp = gen_reg_rtx (DImode);
@@ -2160,47 +2145,137 @@ gen_v9_scc (enum rtx_code compare_code, register rtx *operands)
}
else
temp = op0;
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
+ gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
gen_rtx_fmt_ee (compare_code, DImode,
temp, const0_rtx),
const1_rtx,
- operands[0])));
+ dest)));
return 1;
}
else
{
- operands[1] = gen_compare_reg (compare_code);
+ x = gen_compare_reg_1 (compare_code, x, y);
+ y = const0_rtx;
- switch (GET_MODE (operands[1]))
- {
- case CCmode :
- case CCXmode :
- case CCFPEmode :
- case CCFPmode :
- break;
- default :
- gcc_unreachable ();
- }
- emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ gcc_assert (GET_MODE (x) != CC_NOOVmode
+ && GET_MODE (x) != CCX_NOOVmode);
+
+ emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
+ gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
gen_rtx_fmt_ee (compare_code,
- GET_MODE (operands[1]),
- operands[1], const0_rtx),
- const1_rtx, operands[0])));
+ GET_MODE (x), x, y),
+ const1_rtx, dest)));
return 1;
}
}
+
+/* Emit an scc insn. For seq, sne, sgeu, and sltu, we can do this
+ without jumps using the addx/subx instructions. */
+
+bool
+emit_scc_insn (rtx operands[])
+{
+ rtx tem;
+ rtx x;
+ rtx y;
+ enum rtx_code code;
+
+ /* The quad-word fp compare library routines all return nonzero to indicate
+ true, which is different from the equivalent libgcc routines, so we must
+ handle them specially here. */
+ if (GET_MODE (operands[2]) == TFmode && ! TARGET_HARD_QUAD)
+ {
+ operands[1] = sparc_emit_float_lib_cmp (operands[2], operands[3],
+ GET_CODE (operands[1]));
+ operands[2] = XEXP (operands[1], 0);
+ operands[3] = XEXP (operands[1], 1);
+ }
+
+ code = GET_CODE (operands[1]);
+ x = operands[2];
+ y = operands[3];
+
+ /* For seq/sne on v9 we use the same code as v8 (the addx/subx method has
+ more applications). The exception to this is "reg != 0" which can
+ be done in one instruction on v9 (so we do it). */
+ if (code == EQ)
+ {
+ if (GET_MODE (x) == SImode)
+ {
+ rtx pat = gen_seqsi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ else if (GET_MODE (x) == DImode)
+ {
+ rtx pat = gen_seqdi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ }
+
+ if (code == NE)
+ {
+ if (GET_MODE (x) == SImode)
+ {
+ rtx pat = gen_snesi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ else if (GET_MODE (x) == DImode)
+ {
+ rtx pat = gen_snedi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ }
+
+ /* For the rest, on v9 we can use conditional moves. */
+
+ if (TARGET_V9)
+ {
+ if (gen_v9_scc (operands[0], code, x, y))
+ return true;
+ }
+
+ /* We can do LTU and GEU using the addx/subx instructions too. And
+ for GTU/LEU, if both operands are registers swap them and fall
+ back to the easy case. */
+ if (code == GTU || code == LEU)
+ {
+ if ((GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ && (GET_CODE (y) == REG || GET_CODE (y) == SUBREG))
+ {
+ tem = x;
+ x = y;
+ y = tem;
+ code = swap_condition (code);
+ }
+ }
+
+ if (code == LTU || code == GEU)
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_fmt_ee (code, SImode,
+ gen_compare_reg_1 (code, x, y),
+ const0_rtx)));
+ return true;
+ }
+
+ /* Nope, do branches. */
+ return false;
+}
+
/* Emit a conditional jump insn for the v9 architecture using comparison code
CODE and jump target LABEL.
This function exists to take advantage of the v9 brxx insns. */
-void
+static void
emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
{
- gcc_assert (GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) != MODE_CC);
emit_jump_insn (gen_rtx_SET (VOIDmode,
pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
@@ -2210,6 +2285,37 @@ emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
pc_rtx)));
}
+void
+emit_conditional_branch_insn (rtx operands[])
+{
+ /* The quad-word fp compare library routines all return nonzero to indicate
+ true, which is different from the equivalent libgcc routines, so we must
+ handle them specially here. */
+ if (GET_MODE (operands[1]) == TFmode && ! TARGET_HARD_QUAD)
+ {
+ operands[0] = sparc_emit_float_lib_cmp (operands[1], operands[2],
+ GET_CODE (operands[0]));
+ operands[1] = XEXP (operands[0], 0);
+ operands[2] = XEXP (operands[0], 1);
+ }
+
+ if (TARGET_ARCH64 && operands[2] == const0_rtx
+ && GET_CODE (operands[1]) == REG
+ && GET_MODE (operands[1]) == DImode)
+ {
+ emit_v9_brxx_insn (GET_CODE (operands[0]), operands[1], operands[3]);
+ return;
+ }
+
+ operands[1] = gen_compare_reg (operands[0]);
+ operands[2] = const0_rtx;
+ operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]), VOIDmode,
+ operands[1], operands[2]);
+ emit_jump_insn (gen_cbranchcc4 (operands[0], operands[1], operands[2],
+ operands[3]));
+}
+
+
/* Generate a DFmode part of a hard TFmode register.
REG is the TFmode hard register, LOW is 1 for the
low 64bit of the register and 0 otherwise.
@@ -6116,7 +6222,7 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
values as arguments instead of the TFmode registers themselves,
that's why we cannot call emit_float_lib_cmp. */
-enum rtx_code
+rtx
sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
{
const char *qpfunc;
@@ -6207,32 +6313,24 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
switch (comparison)
{
default:
- new_comparison = NE;
- emit_cmp_insn (result, const0_rtx, new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, result, const0_rtx);
case ORDERED:
case UNORDERED:
new_comparison = (comparison == UNORDERED ? EQ : NE);
- emit_cmp_insn (result, GEN_INT(3), new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, GEN_INT(3));
case UNGT:
case UNGE:
new_comparison = (comparison == UNGT ? GT : NE);
- emit_cmp_insn (result, const1_rtx, new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, const1_rtx);
case UNLE:
- new_comparison = NE;
- emit_cmp_insn (result, const2_rtx, new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, result, const2_rtx);
case UNLT:
tem = gen_reg_rtx (mode);
if (TARGET_ARCH32)
emit_insn (gen_andsi3 (tem, result, const1_rtx));
else
emit_insn (gen_anddi3 (tem, result, const1_rtx));
- new_comparison = NE;
- emit_cmp_insn (tem, const0_rtx, new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, tem, const0_rtx);
case UNEQ:
case LTGT:
tem = gen_reg_rtx (mode);
@@ -6246,11 +6344,10 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
else
emit_insn (gen_anddi3 (tem2, tem, const2_rtx));
new_comparison = (comparison == UNEQ ? EQ : NE);
- emit_cmp_insn (tem2, const0_rtx, new_comparison, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, tem2, const0_rtx);
}
- return new_comparison;
+ gcc_unreachable ();
}
/* Generate an unsigned DImode to FP conversion. This is the same code
@@ -9021,15 +9118,12 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
res)));
- sparc_compare_op0 = resv;
- sparc_compare_op1 = val;
- cc = gen_compare_reg (NE);
-
+ cc = gen_compare_reg_1 (NE, resv, val);
emit_insn (gen_rtx_SET (VOIDmode, val, resv));
- sparc_compare_op0 = cc;
- sparc_compare_op1 = const0_rtx;
- emit_jump_insn (gen_bne (loop_label));
+ /* Use cbranchcc4 to separate the compare and branch! */
+ emit_jump_insn (gen_cbranchcc4 (gen_rtx_NE (VOIDmode, cc, const0_rtx),
+ cc, const0_rtx, loop_label));
emit_label (end_label);
OpenPOWER on IntegriCloud