@@ -100,7 +100,7 @@ extern bool loongarch_cfun_has_cprestore_slot_p (void);
extern void loongarch_expand_scc (rtx *);
extern bool loongarch_expand_vec_cmp (rtx *);
extern void loongarch_expand_conditional_branch (rtx *);
-extern void loongarch_expand_conditional_move (rtx *);
+extern bool loongarch_expand_conditional_move (rtx *);
extern void loongarch_expand_conditional_trap (rtx);
#endif
extern void loongarch_set_return_address (rtx, rtx);
@@ -5063,7 +5063,42 @@ loongarch_expand_conditional_branch (rtx *operands)
/* Perform the comparison in OPERANDS[1]. Move OPERANDS[2] into OPERANDS[0]
if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0]. */
-void
+/* iiii: means selecting a fixed point based on fixed point comparison result.
+ cmp_code is eq/ne:
+ xor op0 i i
+ maskeqz
+ masknez
+ or
+ cmp_code is not eq/ne:
+ slt[u] op0 i i
+ maskeqz
+ masknez
+ or
+
+ iiff: means Selecting a floating point base on fixed point comparison result.
+ cmp_code is eq/ne:
+ xor op0 i i
+ slt[u] op1
+ movdgr2fr f, op1
+ movfr2fcc fcc, f0
+ fsel f, f, f, fcc
+ cmp_code is not eq/ne:
+ slt[u] op0
+ movdgr2fr f, op0
+ movfr2fcc fcc, f
+ fsel f,f,f,fcc
+
+ ffii: means Selecting a fixed point base on floating point comparison result.
+ fcmp.cond.{s/d} fcc, f, f
+ movgr2fr f, i
+ movgr2fr f, i
+ fsel f,f,f,fcc
+ movfr2gr i,f
+
+ ffff: means Selecting a floating point base on floating point comparison
+ result.
+ fcmp.cond.{s.d}. */
+bool
loongarch_expand_conditional_move (rtx *operands)
{
enum rtx_code code = GET_CODE (operands[1]);
@@ -5071,6 +5106,8 @@ loongarch_expand_conditional_move (rtx *operands)
rtx op1 = XEXP (operands[1], 1);
rtx op0_extend = op0;
rtx op1_extend = op1;
+ machine_mode cmp_mode = GET_MODE (op0);
+ machine_mode sel_mode = GET_MODE (operands[2]);
/* Record whether operands[2] and operands[3] modes are promoted to word_mode. */
bool promote_p = false;
@@ -5097,6 +5134,12 @@ loongarch_expand_conditional_move (rtx *operands)
if (code == EQ || code == NE)
{
op0 = loongarch_zero_if_equal (op0, op1);
+
+ /* Be careful iiff. */
+ if (FLOAT_MODE_P (sel_mode))
+ loongarch_emit_int_order_test (LTU, NULL, op0,
+ force_reg (GET_MODE (op0),
+ const0_rtx), op0);
op1 = const0_rtx;
}
else
@@ -5115,7 +5158,8 @@ loongarch_expand_conditional_move (rtx *operands)
rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
/* There is no direct support for general conditional GP move involving
two registers using SEL. */
- if (INTEGRAL_MODE_P (GET_MODE (operands[2]))
+ if (INTEGRAL_MODE_P (cmp_mode)
+ && (INTEGRAL_MODE_P (sel_mode))
&& register_operand (operands[2], VOIDmode)
&& register_operand (operands[3], VOIDmode))
{
@@ -5165,11 +5209,72 @@ loongarch_expand_conditional_move (rtx *operands)
}
else
emit_insn (gen_rtx_SET (operands[0], gen_rtx_IOR (mode, temp, temp2)));
+
+ return true;
}
- else
- emit_insn (gen_rtx_SET (operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
- operands[2], operands[3])));
+ /* For ffii, iiff due to movgr2fr, movfr2gr overhead is relatively large, so
+ we use some compromise. */
+ else if (INTEGRAL_MODE_P (cmp_mode)
+ && (FLOAT_MODE_P (sel_mode))
+ && register_operand (operands[2], VOIDmode)
+ && register_operand (operands[3], VOIDmode))
+ {
+ rtx temp = gen_reg_rtx (sel_mode);
+ rtx fcc_reg = loongarch_allocate_fcc (FCCmode);
+ rtx diop0 = convert_to_mode (E_DImode, op0, true);
+
+ /* stl t0 i i-> movgr2fr f0 t0 -> movfr2cf fcc0 f0 -> fsel f f. */
+ emit_insn (gen_movdgr2fr (sel_mode, temp, diop0));
+ emit_insn (gen_movfr2fcc (sel_mode, fcc_reg, temp));
+
+ cond = gen_rtx_fmt_ee (code, GET_MODE (fcc_reg), fcc_reg, const0_rtx);
+
+ emit_insn (gen_rtx_SET (operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ cond, operands[2],
+ operands[3])));
+ return true;
+ }
+ else if (FLOAT_MODE_P (cmp_mode) && (INTEGRAL_MODE_P (sel_mode)))
+ {
+ /* movgr2fr f0 i -> movgr2fr f1 i -> fcmp fcc0 f f
+ -> fsel f3 f0 f1 -> movfr2gr t0 f3. */
+ machine_mode dst_mode = GET_MODE (operands[0]);
+ rtx temp = gen_reg_rtx (E_DFmode);
+ rtx temp2 = gen_reg_rtx (E_DFmode);
+ rtx temp3 = gen_reg_rtx (E_DFmode);
+
+ if (CONST_INT_P (operands[2]))
+ operands[2] = copy_to_mode_reg (dst_mode, operands[2]);
+
+ if (CONST_INT_P (operands[3]))
+ operands[3] = copy_to_mode_reg (dst_mode, operands[3]);
+
+ if (GET_MODE (operands[2]) != E_DImode)
+ operands[2] = convert_to_mode (E_DImode, operands[2], false);
+
+ if (GET_MODE (operands[3]) != E_DImode)
+ operands[3] = convert_to_mode (E_DImode, operands[3], false);
+
+ emit_insn (gen_movdgr2frdf (temp2, operands[2]));
+ emit_insn (gen_movdgr2frdf (temp3, operands[3]));
+ emit_insn (gen_rtx_SET (temp,
+ gen_rtx_IF_THEN_ELSE (E_DFmode, cond,
+ temp2, temp3)));
+ emit_insn (gen_movdfr2gr (GET_MODE (operands[0]), operands[0], temp));
+
+ return true;
+ }
+ else if (FLOAT_MODE_P (cmp_mode) && FLOAT_MODE_P (sel_mode))
+ {
+ emit_insn (gen_rtx_SET (operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ cond, operands[2],
+ operands[3])));
+ return true;
+ }
+
+ return false;
}
/* Implement TARGET_EXPAND_BUILTIN_VA_START. */
@@ -30,7 +30,12 @@ (define_c_enum "unspec" [
UNSPEC_LOAD_HIGH
UNSPEC_STORE_WORD
UNSPEC_MOVGR2FRH
+ UNSPEC_MOVGR2FR
UNSPEC_MOVFRH2GR
+ UNSPEC_MOVFR2GR
+ UNSPEC_MOVFCC2GR
+ UNSPEC_MOVGR2FCC
+ UNSPEC_MOVFR2FCC
;; Floating point unspecs.
UNSPEC_FRINT
@@ -295,6 +300,7 @@ (define_attr "type"
;; D2I float to integer (DF to SI/DI)
;; D2S double to float single
;; S2D float single to double
+;; C2D fcc to DI
(define_attr "cnv_mode" "unknown,I2S,I2D,S2I,D2I,D2S,S2D"
(const_string "unknown"))
@@ -559,6 +565,7 @@ (define_code_attr fcond [(unordered "cun")
;; The sel mnemonic to use depending on the condition test.
(define_code_attr sel [(eq "masknez") (ne "maskeqz")])
+(define_code_attr fsel_invert [(eq "%2,%3") (ne "%3,%2")])
(define_code_attr selinv [(eq "maskeqz") (ne "masknez")])
;; Iterator and attributes for floating-point to fixed-point conversion
@@ -2178,12 +2185,12 @@ (define_insn "*sel<code><GPR:mode>_using_<GPR2:mode>"
(define_insn "*sel<mode>"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(if_then_else:ANYF
- (ne:FCC (match_operand:FCC 1 "register_operand" "z")
+ (equality_op:FCC (match_operand:FCC 1 "register_operand" "z")
(const_int 0))
(match_operand:ANYF 2 "reg_or_0_operand" "f")
(match_operand:ANYF 3 "reg_or_0_operand" "f")))]
""
- "fsel\t%0,%3,%2,%1"
+ "fsel\t%0,<fsel_invert>,%1"
[(set_attr "type" "condmove")
(set_attr "mode" "<ANYF:MODE>")])
@@ -2196,11 +2203,10 @@ (define_expand "mov<mode>cc"
(match_operand:GPR 3 "reg_or_0_operand")])))]
"TARGET_COND_MOVE_INT"
{
- if (!INTEGRAL_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+ if (loongarch_expand_conditional_move (operands))
+ DONE;
+ else
FAIL;
-
- loongarch_expand_conditional_move (operands);
- DONE;
})
(define_expand "mov<mode>cc"
@@ -2210,11 +2216,11 @@ (define_expand "mov<mode>cc"
(match_operand:ANYF 3 "reg_or_0_operand")])))]
"TARGET_COND_MOVE_FLOAT"
{
- if (!FLOAT_MODE_P (GET_MODE (XEXP (operands[1], 0))))
- FAIL;
- loongarch_expand_conditional_move (operands);
- DONE;
+ if (loongarch_expand_conditional_move (operands))
+ DONE;
+ else
+ FAIL;
})
(define_insn "lu32i_d"
@@ -2447,6 +2453,15 @@ (define_insn "movgr2frh<mode>"
[(set_attr "move_type" "mgtf")
(set_attr "mode" "<HALFMODE>")])
+(define_insn "@movdgr2fr<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:DI 1 "register_operand" "r")]
+ UNSPEC_MOVGR2FR))]
+ "TARGET_DOUBLE_FLOAT"
+ "movgr2fr.d\t%0,%1"
+ [(set_attr "move_type" "mgtf")
+ (set_attr "mode" "<MODE>")])
+
;; Move high word of operand 1 to operand 0 using movfrh2gr.s.
(define_insn "movfrh2gr<mode>"
[(set (match_operand:<HALFMODE> 0 "register_operand" "=r")
@@ -2457,6 +2472,31 @@ (define_insn "movfrh2gr<mode>"
[(set_attr "move_type" "mftg")
(set_attr "mode" "<HALFMODE>")])
+(define_insn "@movdfr2gr<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (unspec:GPR [(match_operand:DF 1 "register_operand" "f")]
+ UNSPEC_MOVFR2GR))]
+ "TARGET_DOUBLE_FLOAT"
+ "movfr2gr.d\t%0,%1"
+ [(set_attr "move_type" "mftg")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "@movfr2fcc<mode>"
+ [(set (match_operand:FCC 0 "register_operand" "=z")
+ (unspec:FCC [(match_operand:ANYF 1 "register_operand" "f")]
+ UNSPEC_MOVFR2FCC))]
+ "TARGET_HARD_FLOAT"
+ "movfr2cf\t%0,%1"
+ [(set_attr "mode" "<MODE>")])
+
+(define_insn "@movgr2fcc<mode>"
+ [(set (match_operand:FCC 0 "register_operand" "=z")
+ (unspec:FCC [(match_operand:GPR 1 "register_operand" "r")]
+ UNSPEC_MOVGR2FCC))]
+ "TARGET_HARD_FLOAT"
+ "movgr2cf\t%0,%1"
+ [(set_attr "mode" "<MODE>")])
+
;; Expand in-line code to clear the instruction cache between operand[0] and
;; operand[1].
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*fcmp.*fsel.*" } } */
+
+extern void foo_ff (float *, float *, float *, float *);
+
+float
+test (void)
+{
+ float a, b;
+ float c, d, out;
+ foo_ff (&a, &b, &c, &d);
+ out = a > b ? c : d;
+ return out;
+}
+
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*movgr2fr.*movgr2fr.*fsel.*movfr2gr.*" } } */
+
+extern void foo_fi (float *, float *, int *, int *);
+
+int
+test (void)
+{
+ float a, b;
+ int c, d, out;
+ foo_fi (&a, &b, &c, &d);
+ out = a > b ? c : d;
+ return out;
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mdouble-float" } */
+/* { dg-final { scan-assembler "test:.*movfr2cf.*fsel.*" } } */
+
+extern void foo_if (int *, int *, float *, float *);
+
+float
+test (void)
+{
+ int a, b;
+ float c, d, out;
+ foo_if (&a, &b, &c, &d);
+ out = a == b ? c : d;
+ return out;
+}