aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meissner <meissner@linux.vnet.ibm.com>2016-05-31 22:39:48 +0000
committerMichael Meissner <meissner@linux.vnet.ibm.com>2016-05-31 22:39:48 +0000
commit33361a08a158fa5427f77b0ac876899cf1eebaee (patch)
tree5a248e0d53a539a1793e50f8e70207a0b91f393d
parenta392408d09f870ac83ee7b4101146d2f8a8f9869 (diff)
Backport p9 min/max changes to gcc 6
git-svn-id: https://gcc.gnu.org/svn/gcc/branches/ibm/power9-gcc6@236961 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/config/rs6000/predicates.md9
-rw-r--r--gcc/config/rs6000/rs6000.c107
-rw-r--r--gcc/config/rs6000/rs6000.h9
-rw-r--r--gcc/config/rs6000/rs6000.md227
-rw-r--r--gcc/testsuite/gcc.target/powerpc/p9-minmax-1.c171
-rw-r--r--gcc/testsuite/gcc.target/powerpc/p9-minmax-2.c191
6 files changed, 586 insertions, 128 deletions
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index c1b1c013ca7..d03d164e75f 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1116,10 +1116,6 @@
(define_special_predicate "equality_operator"
(match_code "eq,ne"))
-;; Return true if operand is MIN or MAX operator.
-(define_predicate "min_max_operator"
- (match_code "smin,smax,umin,umax"))
-
;; Return 1 if OP is a comparison operation that is valid for a branch
;; instruction. We check the opcode against the mode of the CC value.
;; validate_condition_mode is an assertion.
@@ -1162,6 +1158,11 @@
(and (match_operand 0 "branch_comparison_operator")
(match_code "ne,le,ge,leu,geu,ordered")))
+;; Return 1 if OP is a comparison operator suitable for vector/scalar
+;; comparisons that generate a -1/0 mask.
+(define_predicate "fpmask_comparison_operator"
+ (match_code "eq,gt,ge"))
+
;; Return 1 if OP is a comparison operation that is valid for a branch
;; insn, which is true if the corresponding bit in the CC register is set.
(define_predicate "branch_positive_comparison_operator"
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 5519b1aeea1..3852029cc80 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -22394,6 +22394,101 @@ rs6000_emit_vector_cond_expr (rtx dest, rtx op_true, rtx op_false,
return 1;
}
+/* ISA 3.0 (power9) minmax subcase to emit a XSMAXCDP or XSMINCDP instruction
+ for SF/DF scalars. Move TRUE_COND to DEST if OP of the operands of the last
+ comparison is nonzero/true, FALSE_COND if it is zero/false. Return 0 if the
+ hardware has no such operation. */
+
+static int
+rs6000_emit_p9_fp_minmax (rtx dest, rtx op, rtx true_cond, rtx false_cond)
+{
+ enum rtx_code code = GET_CODE (op);
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+ machine_mode compare_mode = GET_MODE (op0);
+ machine_mode result_mode = GET_MODE (dest);
+ bool max_p = false;
+
+ if (result_mode != compare_mode)
+ return 0;
+
+ if (code == GE || code == GT)
+ max_p = true;
+ else if (code == LE || code == LT)
+ max_p = false;
+ else
+ return 0;
+
+ if (rtx_equal_p (op0, true_cond) && rtx_equal_p (op1, false_cond))
+ ;
+
+ else if (rtx_equal_p (op1, true_cond) && rtx_equal_p (op0, false_cond))
+ max_p = !max_p;
+
+ else
+ return 0;
+
+ rs6000_emit_minmax (dest, max_p ? SMAX : SMIN, op0, op1);
+ return 1;
+}
+
+/* ISA 3.0 (power9) conditional move subcase to emit XSCMP{EQ,GE,GT,NE}DP and
+ XXSEL instructions for SF/DF scalars. Move TRUE_COND to DEST if OP of the
+ operands of the last comparison is nonzero/true, FALSE_COND if it is
+ zero/false. Return 0 if the hardware has no such operation. */
+
+static int
+rs6000_emit_p9_fp_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond)
+{
+ enum rtx_code code = GET_CODE (op);
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+ machine_mode result_mode = GET_MODE (dest);
+ rtx compare_rtx;
+ rtx cmove_rtx;
+ rtx clobber_rtx;
+
+ if (!can_create_pseudo_p ())
+ return 0;
+
+ switch (code)
+ {
+ case EQ:
+ case GE:
+ case GT:
+ break;
+
+ case NE:
+ case LT:
+ case LE:
+ code = swap_condition (code);
+ std::swap (op0, op1);
+ break;
+
+ default:
+ return 0;
+ }
+
+ /* Generate: [(parallel [(set (dest)
+ (if_then_else (op (cmp1) (cmp2))
+ (true)
+ (false)))
+ (clobber (scratch))])]. */
+
+ compare_rtx = gen_rtx_fmt_ee (code, CCFPmode, op0, op1);
+ cmove_rtx = gen_rtx_SET (dest,
+ gen_rtx_IF_THEN_ELSE (result_mode,
+ compare_rtx,
+ true_cond,
+ false_cond));
+
+ clobber_rtx = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (V2DImode));
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, cmove_rtx, clobber_rtx)));
+
+ return 1;
+}
+
/* Emit a conditional move: move TRUE_COND to DEST if OP of the
operands of the last comparison is nonzero/true, FALSE_COND if it
is zero/false. Return 0 if the hardware has no such operation. */
@@ -22420,6 +22515,18 @@ rs6000_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond)
if (GET_MODE (false_cond) != result_mode)
return 0;
+ /* See if we can use the ISA 3.0 (power9) min/max/compare functions. */
+ if (TARGET_P9_MINMAX
+ && (compare_mode == SFmode || compare_mode == DFmode)
+ && (result_mode == SFmode || result_mode == DFmode))
+ {
+ if (rs6000_emit_p9_fp_minmax (dest, op, true_cond, false_cond))
+ return 1;
+
+ if (rs6000_emit_p9_fp_cmove (dest, op, true_cond, false_cond))
+ return 1;
+ }
+
/* Don't allow using floating point comparisons for integer results for
now. */
if (FLOAT_MODE_P (compare_mode) && !FLOAT_MODE_P (result_mode))
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index a5f43879c9b..b0f4cbb6b1e 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -594,6 +594,15 @@ extern int rs6000_vector_align[];
in the register. */
#define TARGET_NO_SDMODE_STACK (TARGET_LFIWZX && TARGET_STFIWX && TARGET_DFP)
+/* ISA 3.0 has new min/max functions that don't need fast math that are being
+ phased in. Min/max using FSEL or XSMAXDP/XSMINDP do not return the correct
+ answers if the arguments are not in the normal range. */
+#define TARGET_MINMAX_SF (TARGET_SF_FPR && TARGET_PPC_GFXOPT \
+ && (TARGET_P9_MINMAX || !flag_trapping_math))
+
+#define TARGET_MINMAX_DF (TARGET_DF_FPR && TARGET_PPC_GFXOPT \
+ && (TARGET_P9_MINMAX || !flag_trapping_math))
+
/* In switching from using target_flags to using rs6000_isa_flags, the options
machinery creates OPTION_MASK_<xxx> instead of MASK_<xxx>. For now map
OPTION_MASK_<xxx> back into MASK_<xxx>. */
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index b24f5d92ab7..cc5ef2e5d8d 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -489,6 +489,10 @@
; Iterator for just SF/DF
(define_mode_iterator SFDF [SF DF])
+; Like SFDF, but a different name to match conditional move where the
+; comparison operands may be a different mode than the input operands.
+(define_mode_iterator SFDF2 [SF DF])
+
; Iterator for 128-bit floating point that uses the IBM double-double format
(define_mode_iterator IBM128 [(IF "FLOAT128_IBM_P (IFmode)")
(TF "FLOAT128_IBM_P (TFmode)")])
@@ -700,6 +704,15 @@
(define_mode_iterator RELOAD [V16QI V8HI V4SI V2DI V4SF V2DF V1TI
SF SD SI DF DD DI TI PTI KF IF TF])
+;; Iterate over smin, smax
+(define_code_iterator fp_minmax [smin smax])
+
+(define_code_attr minmax [(smin "min")
+ (smax "max")])
+
+(define_code_attr SMINMAX [(smin "SMIN")
+ (smax "SMAX")])
+
;; Start with fixed-point load and store insns. Here we put only the more
;; complex forms. Basic data transfer is done later.
@@ -4601,74 +4614,45 @@
;; On VSX, we only check for TARGET_VSX instead of checking for a vsx/p8 vector
;; to allow either DF/SF to use only traditional registers.
-(define_expand "smax<mode>3"
+(define_expand "s<minmax><mode>3"
[(set (match_operand:SFDF 0 "gpc_reg_operand" "")
- (if_then_else:SFDF (ge (match_operand:SFDF 1 "gpc_reg_operand" "")
- (match_operand:SFDF 2 "gpc_reg_operand" ""))
- (match_dup 1)
- (match_dup 2)))]
- "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT && !flag_trapping_math"
+ (fp_minmax:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")
+ (match_operand:SFDF 2 "gpc_reg_operand" "")))]
+ "TARGET_MINMAX_<MODE>"
{
- rs6000_emit_minmax (operands[0], SMAX, operands[1], operands[2]);
+ rs6000_emit_minmax (operands[0], <SMINMAX>, operands[1], operands[2]);
DONE;
})
-(define_insn "*smax<mode>3_vsx"
- [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>")
- (smax:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "%<Ff>,<Fv>")
- (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv>")))]
- "TARGET_<MODE>_FPR && TARGET_VSX"
- "xsmaxdp %x0,%x1,%x2"
- [(set_attr "type" "fp")])
-
-(define_expand "smin<mode>3"
- [(set (match_operand:SFDF 0 "gpc_reg_operand" "")
- (if_then_else:SFDF (ge (match_operand:SFDF 1 "gpc_reg_operand" "")
- (match_operand:SFDF 2 "gpc_reg_operand" ""))
- (match_dup 2)
- (match_dup 1)))]
- "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT && !flag_trapping_math"
+(define_insn "*s<minmax><mode>3_vsx"
+ [(set (match_operand:SFDF 0 "vsx_register_operand" "=<Fv>")
+ (fp_minmax:SFDF (match_operand:SFDF 1 "vsx_register_operand" "<Fv>")
+ (match_operand:SFDF 2 "vsx_register_operand" "<Fv>")))]
+ "TARGET_VSX && TARGET_<MODE>_FPR"
{
- rs6000_emit_minmax (operands[0], SMIN, operands[1], operands[2]);
- DONE;
-})
-
-(define_insn "*smin<mode>3_vsx"
- [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>")
- (smin:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "%<Ff>,<Fv>")
- (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv>")))]
- "TARGET_<MODE>_FPR && TARGET_VSX"
- "xsmindp %x0,%x1,%x2"
+ return (TARGET_P9_MINMAX
+ ? "xs<minmax>cdp %x0,%x1,%x2"
+ : "xs<minmax>dp %x0,%x1,%x2");
+}
[(set_attr "type" "fp")])
-(define_split
+;; The conditional move instructions allow us to perform max and min operations
+;; even when we don't have the appropriate max/min instruction using the FSEL
+;; instruction.
+
+(define_insn_and_split "*s<minmax><mode>3_fpr"
[(set (match_operand:SFDF 0 "gpc_reg_operand" "")
- (match_operator:SFDF 3 "min_max_operator"
- [(match_operand:SFDF 1 "gpc_reg_operand" "")
- (match_operand:SFDF 2 "gpc_reg_operand" "")]))]
- "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT && !flag_trapping_math
- && !TARGET_VSX"
+ (fp_minmax:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")
+ (match_operand:SFDF 2 "gpc_reg_operand" "")))]
+ "!TARGET_VSX && TARGET_MINMAX_<MODE>"
+ "#"
+ "&& 1"
[(const_int 0)]
{
- rs6000_emit_minmax (operands[0], GET_CODE (operands[3]), operands[1],
- operands[2]);
+ rs6000_emit_minmax (operands[0], <SMINMAX>, operands[1], operands[2]);
DONE;
})
-(define_split
- [(set (match_operand:SF 0 "gpc_reg_operand" "")
- (match_operator:SF 3 "min_max_operator"
- [(match_operand:SF 1 "gpc_reg_operand" "")
- (match_operand:SF 2 "gpc_reg_operand" "")]))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS
- && TARGET_SINGLE_FLOAT && !flag_trapping_math"
- [(const_int 0)]
- "
-{ rs6000_emit_minmax (operands[0], GET_CODE (operands[3]),
- operands[1], operands[2]);
- DONE;
-}")
-
(define_expand "mov<mode>cc"
[(set (match_operand:GPR 0 "gpc_reg_operand" "")
(if_then_else:GPR (match_operand 1 "comparison_operator" "")
@@ -4751,12 +4735,13 @@
[(set_attr "type" "isel")
(set_attr "length" "4")])
-(define_expand "movsfcc"
- [(set (match_operand:SF 0 "gpc_reg_operand" "")
- (if_then_else:SF (match_operand 1 "comparison_operator" "")
- (match_operand:SF 2 "gpc_reg_operand" "")
- (match_operand:SF 3 "gpc_reg_operand" "")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+;; Floating point conditional move
+(define_expand "mov<mode>cc"
+ [(set (match_operand:SFDF 0 "gpc_reg_operand" "")
+ (if_then_else:SFDF (match_operand 1 "comparison_operator" "")
+ (match_operand:SFDF 2 "gpc_reg_operand" "")
+ (match_operand:SFDF 3 "gpc_reg_operand" "")))]
+ "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT"
"
{
if (rs6000_emit_cmove (operands[0], operands[1], operands[2], operands[3]))
@@ -4765,76 +4750,70 @@
FAIL;
}")
-(define_insn "*fselsfsf4"
- [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
- (if_then_else:SF (ge (match_operand:SF 1 "gpc_reg_operand" "f")
- (match_operand:SF 4 "zero_fp_constant" "F"))
- (match_operand:SF 2 "gpc_reg_operand" "f")
- (match_operand:SF 3 "gpc_reg_operand" "f")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT"
+(define_insn "*fsel<SFDF:mode><SFDF2:mode>4"
+ [(set (match_operand:SFDF 0 "fpr_reg_operand" "=&<SFDF:rreg2>")
+ (if_then_else:SFDF
+ (ge (match_operand:SFDF2 1 "fpr_reg_operand" "<SFDF2:rreg2>")
+ (match_operand:SFDF2 4 "zero_fp_constant" "F"))
+ (match_operand:SFDF 2 "fpr_reg_operand" "<SFDF:rreg2>")
+ (match_operand:SFDF 3 "fpr_reg_operand" "<SFDF:rreg2>")))]
+ "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT"
"fsel %0,%1,%2,%3"
[(set_attr "type" "fp")])
-(define_insn "*fseldfsf4"
- [(set (match_operand:SF 0 "gpc_reg_operand" "=f")
- (if_then_else:SF (ge (match_operand:DF 1 "gpc_reg_operand" "d")
- (match_operand:DF 4 "zero_fp_constant" "F"))
- (match_operand:SF 2 "gpc_reg_operand" "f")
- (match_operand:SF 3 "gpc_reg_operand" "f")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_SINGLE_FLOAT"
- "fsel %0,%1,%2,%3"
- [(set_attr "type" "fp")])
-
-;; The conditional move instructions allow us to perform max and min
-;; operations even when
-
-(define_split
- [(set (match_operand:DF 0 "gpc_reg_operand" "")
- (match_operator:DF 3 "min_max_operator"
- [(match_operand:DF 1 "gpc_reg_operand" "")
- (match_operand:DF 2 "gpc_reg_operand" "")]))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
- && !flag_trapping_math"
- [(const_int 0)]
- "
-{ rs6000_emit_minmax (operands[0], GET_CODE (operands[3]),
- operands[1], operands[2]);
- DONE;
-}")
-
-(define_expand "movdfcc"
- [(set (match_operand:DF 0 "gpc_reg_operand" "")
- (if_then_else:DF (match_operand 1 "comparison_operator" "")
- (match_operand:DF 2 "gpc_reg_operand" "")
- (match_operand:DF 3 "gpc_reg_operand" "")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
- "
+(define_insn_and_split "*mov<SFDF:mode><SFDF2:mode>cc_p9"
+ [(set (match_operand:SFDF 0 "vsx_register_operand" "=&<SFDF:Fv>,<SFDF:Fv>")
+ (if_then_else:SFDF
+ (match_operator:CCFP 1 "fpmask_comparison_operator"
+ [(match_operand:SFDF2 2 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>")
+ (match_operand:SFDF2 3 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>")])
+ (match_operand:SFDF 4 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>")
+ (match_operand:SFDF 5 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>")))
+ (clobber (match_scratch:V2DI 6 "=0,&wa"))]
+ "TARGET_P9_MINMAX"
+ "#"
+ ""
+ [(set (match_dup 6)
+ (if_then_else:V2DI (match_dup 1)
+ (match_dup 7)
+ (match_dup 8)))
+ (set (match_dup 0)
+ (if_then_else:SFDF (ne (match_dup 6)
+ (match_dup 8))
+ (match_dup 4)
+ (match_dup 5)))]
{
- if (rs6000_emit_cmove (operands[0], operands[1], operands[2], operands[3]))
- DONE;
- else
- FAIL;
-}")
+ if (GET_CODE (operands[6]) == SCRATCH)
+ operands[6] = gen_reg_rtx (V2DImode);
-(define_insn "*fseldfdf4"
- [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
- (if_then_else:DF (ge (match_operand:DF 1 "gpc_reg_operand" "d")
- (match_operand:DF 4 "zero_fp_constant" "F"))
- (match_operand:DF 2 "gpc_reg_operand" "d")
- (match_operand:DF 3 "gpc_reg_operand" "d")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT"
- "fsel %0,%1,%2,%3"
- [(set_attr "type" "fp")])
+ operands[7] = CONSTM1_RTX (V2DImode);
+ operands[8] = CONST0_RTX (V2DImode);
+}
+ [(set_attr "length" "8")
+ (set_attr "type" "vecperm")])
+
+(define_insn "*fpmask<mode>"
+ [(set (match_operand:V2DI 0 "vsx_register_operand" "=wa")
+ (if_then_else:V2DI
+ (match_operator:CCFP 1 "fpmask_comparison_operator"
+ [(match_operand:SFDF 2 "vsx_register_operand" "<Fv>")
+ (match_operand:SFDF 3 "vsx_register_operand" "<Fv>")])
+ (match_operand:V2DI 4 "all_ones_constant" "")
+ (match_operand:V2DI 5 "zero_constant" "")))]
+ "TARGET_P9_MINMAX"
+ "xscmp%V1dp %x0,%x2,%x3"
+ [(set_attr "type" "fpcompare")])
+
+(define_insn "*xxsel<mode>"
+ [(set (match_operand:SFDF 0 "vsx_register_operand" "=<Fv>")
+ (if_then_else:SFDF (ne (match_operand:V2DI 1 "vsx_register_operand" "wa")
+ (match_operand:V2DI 2 "zero_constant" ""))
+ (match_operand:SFDF 3 "vsx_register_operand" "<Fv>")
+ (match_operand:SFDF 4 "vsx_register_operand" "<Fv>")))]
+ "TARGET_P9_MINMAX"
+ "xxsel %x0,%x1,%x3,%x4"
+ [(set_attr "type" "vecperm")])
-(define_insn "*fselsfdf4"
- [(set (match_operand:DF 0 "gpc_reg_operand" "=d")
- (if_then_else:DF (ge (match_operand:SF 1 "gpc_reg_operand" "f")
- (match_operand:SF 4 "zero_fp_constant" "F"))
- (match_operand:DF 2 "gpc_reg_operand" "d")
- (match_operand:DF 3 "gpc_reg_operand" "d")))]
- "TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_SINGLE_FLOAT"
- "fsel %0,%1,%2,%3"
- [(set_attr "type" "fp")])
;; Conversions to and from floating-point.
diff --git a/gcc/testsuite/gcc.target/powerpc/p9-minmax-1.c b/gcc/testsuite/gcc.target/powerpc/p9-minmax-1.c
new file mode 100644
index 00000000000..c182da9470e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/p9-minmax-1.c
@@ -0,0 +1,171 @@
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-skip-if "do not override -mcpu" { powerpc*-*-* } { "-mcpu=*" } { "-mcpu=power9" } } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { dg-options "-mcpu=power9 -O2 -mpower9-minmax -ffast-math" } */
+/* { dg-final { scan-assembler-not "fsel" } } */
+/* { dg-final { scan-assembler "xscmpeqdp" } } */
+/* { dg-final { scan-assembler "xscmpgtdp" } } */
+/* { dg-final { scan-assembler "xscmpgedp" } } */
+/* { dg-final { scan-assembler-not "xscmpodp" } } */
+/* { dg-final { scan-assembler-not "xscmpudp" } } */
+/* { dg-final { scan-assembler "xsmaxcdp" } } */
+/* { dg-final { scan-assembler-not "xsmaxdp" } } */
+/* { dg-final { scan-assembler "xsmincdp" } } */
+/* { dg-final { scan-assembler-not "xsmindp" } } */
+/* { dg-final { scan-assembler "xxsel" } } */
+
+double
+dbl_max1 (double a, double b)
+{
+ return (a >= b) ? a : b;
+}
+
+double
+dbl_max2 (double a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_min1 (double a, double b)
+{
+ return (a < b) ? a : b;
+}
+
+double
+dbl_min2 (double a, double b)
+{
+ return (a <= b) ? a : b;
+}
+
+double
+dbl_cmp_eq (double a, double b, double c, double d)
+{
+ return (a == b) ? c : d;
+}
+
+double
+dbl_cmp_ne (double a, double b, double c, double d)
+{
+ return (a != b) ? c : d;
+}
+
+double
+dbl_cmp_gt (double a, double b, double c, double d)
+{
+ return (a > b) ? c : d;
+}
+
+double
+dbl_cmp_ge (double a, double b, double c, double d)
+{
+ return (a >= b) ? c : d;
+}
+
+double
+dbl_cmp_lt (double a, double b, double c, double d)
+{
+ return (a < b) ? c : d;
+}
+
+double
+dbl_cmp_le (double a, double b, double c, double d)
+{
+ return (a <= b) ? c : d;
+}
+
+float
+flt_max1 (float a, float b)
+{
+ return (a >= b) ? a : b;
+}
+
+float
+flt_max2 (float a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+float
+flt_min1 (float a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+float
+flt_min2 (float a, float b)
+{
+ return (a <= b) ? a : b;
+}
+
+float
+flt_cmp_eq (float a, float b, float c, float d)
+{
+ return (a == b) ? c : d;
+}
+
+float
+flt_cmp_ne (float a, float b, float c, float d)
+{
+ return (a != b) ? c : d;
+}
+
+float
+flt_cmp_gt (float a, float b, float c, float d)
+{
+ return (a > b) ? c : d;
+}
+
+float
+flt_cmp_ge (float a, float b, float c, float d)
+{
+ return (a >= b) ? c : d;
+}
+
+float
+flt_cmp_lt (float a, float b, float c, float d)
+{
+ return (a < b) ? c : d;
+}
+
+float
+flt_cmp_le (float a, float b, float c, float d)
+{
+ return (a <= b) ? c : d;
+}
+
+double
+dbl_flt_max1 (float a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_max2 (double a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_max3 (float a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_min1 (float a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+double
+dbl_flt_min2 (double a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+double
+dbl_flt_min3 (float a, double b)
+{
+ return (a < b) ? a : b;
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/p9-minmax-2.c b/gcc/testsuite/gcc.target/powerpc/p9-minmax-2.c
new file mode 100644
index 00000000000..f6742142966
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/p9-minmax-2.c
@@ -0,0 +1,191 @@
+/* { dg-do compile { target { powerpc*-*-* } } } */
+/* { dg-skip-if "do not override -mcpu" { powerpc*-*-* } { "-mcpu=*" } { "-mcpu=power9" } } */
+/* { dg-require-effective-target powerpc_p9vector_ok } */
+/* { dg-options "-mcpu=power9 -O2 -mpower9-minmax" } */
+/* { dg-final { scan-assembler-not "fsel" } } */
+/* { dg-final { scan-assembler "xscmpeqdp" } } */
+/* { dg-final { scan-assembler "xscmpgtdp" } } */
+/* { dg-final { scan-assembler-not "xscmpodp" } } */
+/* { dg-final { scan-assembler-not "xscmpudp" } } */
+/* { dg-final { scan-assembler "xsmaxcdp" } } */
+/* { dg-final { scan-assembler-not "xsmaxdp" } } */
+/* { dg-final { scan-assembler "xsmincdp" } } */
+/* { dg-final { scan-assembler-not "xsmindp" } } */
+/* { dg-final { scan-assembler "xxsel" } } */
+
+/* Due to NaN support, <= and >= are not handled presently unless -ffast-math
+ is used. At some point this will be fixed and the xscmpgedp instruction can
+ be generated normally. The <= and >= tests are bracketed with
+ #ifdef DO_GE_LE. */
+
+#ifdef DO_GE_LE
+double
+dbl_max1 (double a, double b)
+{
+ return (a >= b) ? a : b;
+}
+#endif
+
+double
+dbl_max2 (double a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_min1 (double a, double b)
+{
+ return (a < b) ? a : b;
+}
+
+#ifdef DO_GE_LE
+double
+dbl_min2 (double a, double b)
+{
+ return (a <= b) ? a : b;
+}
+#endif
+
+double
+dbl_cmp_eq (double a, double b, double c, double d)
+{
+ return (a == b) ? c : d;
+}
+
+double
+dbl_cmp_ne (double a, double b, double c, double d)
+{
+ return (a != b) ? c : d;
+}
+
+double
+dbl_cmp_gt (double a, double b, double c, double d)
+{
+ return (a > b) ? c : d;
+}
+
+#ifdef DO_GE_LE
+double
+dbl_cmp_ge (double a, double b, double c, double d)
+{
+ return (a >= b) ? c : d;
+}
+#endif
+
+double
+dbl_cmp_lt (double a, double b, double c, double d)
+{
+ return (a < b) ? c : d;
+}
+
+#ifdef DO_GE_LE
+double
+dbl_cmp_le (double a, double b, double c, double d)
+{
+ return (a <= b) ? c : d;
+}
+#endif
+
+#ifdef DO_GE_LE
+float
+flt_max1 (float a, float b)
+{
+ return (a >= b) ? a : b;
+}
+#endif
+
+float
+flt_max2 (float a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+float
+flt_min1 (float a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+#ifdef DO_GE_LE
+float
+flt_min2 (float a, float b)
+{
+ return (a <= b) ? a : b;
+}
+#endif
+
+float
+flt_cmp_eq (float a, float b, float c, float d)
+{
+ return (a == b) ? c : d;
+}
+
+float
+flt_cmp_ne (float a, float b, float c, float d)
+{
+ return (a != b) ? c : d;
+}
+
+float
+flt_cmp_gt (float a, float b, float c, float d)
+{
+ return (a > b) ? c : d;
+}
+
+#ifdef DO_GE_LE
+float
+flt_cmp_ge (float a, float b, float c, float d)
+{
+ return (a >= b) ? c : d;
+}
+#endif
+
+float
+flt_cmp_lt (float a, float b, float c, float d)
+{
+ return (a < b) ? c : d;
+}
+
+#ifdef DO_GE_LE
+float
+flt_cmp_le (float a, float b, float c, float d)
+{
+ return (a <= b) ? c : d;
+}
+#endif
+
+double
+dbl_flt_max1 (float a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_max2 (double a, float b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_max3 (float a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+double
+dbl_flt_min1 (float a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+double
+dbl_flt_min2 (double a, float b)
+{
+ return (a < b) ? a : b;
+}
+
+double
+dbl_flt_min3 (float a, double b)
+{
+ return (a < b) ? a : b;
+}