===================================================================
@@ -1772,10 +1772,23 @@ expand_arith_overflow (enum tree_code co
int prec1 = TYPE_PRECISION (TREE_TYPE (arg1));
int precres = TYPE_PRECISION (type);
location_t loc = gimple_location (stmt);
- if (!uns0_p && get_range_pos_neg (arg0) == 1)
- uns0_p = true;
- if (!uns1_p && get_range_pos_neg (arg1) == 1)
- uns1_p = true;
+ /* Try to promote to unsigned since unsigned overflow is easier to open
+ code than signed overflow, but not for multiplication if that would
+ mean not using the hardware because this would very likely result in
+ doing 2 multiplications instead of only 1, e.g. on PowerPC. */
+ if (code == MULT_EXPR
+ && !unsr_p
+ && precres <= BITS_PER_WORD
+ && optab_handler (mulv4_optab, TYPE_MODE (type)) != CODE_FOR_nothing
+ && optab_handler (umulv4_optab, TYPE_MODE (type)) == CODE_FOR_nothing)
+ ;
+ else
+ {
+ if (!uns0_p && get_range_pos_neg (arg0) == 1)
+ uns0_p = true;
+ if (!uns1_p && get_range_pos_neg (arg1) == 1)
+ uns1_p = true;
+ }
int pr = get_min_precision (arg0, uns0_p ? UNSIGNED : SIGNED);
prec0 = MIN (prec0, pr);
pr = get_min_precision (arg1, uns1_p ? UNSIGNED : SIGNED);
===================================================================
@@ -32,13 +32,15 @@ FLOAT_MODE (TF, 16, ieee_quad_format);
/* Add any extra modes needed to represent the condition code.
For the RS/6000, we need separate modes when unsigned (logical) comparisons
- are being done and we need a separate mode for floating-point. We also
- use a mode for the case when we are comparing the results of two
- comparisons, as then only the EQ bit is valid in the register. */
+ are being done and we need a separate mode for floating-point. We also use
+ a mode for the case when we are comparing the results of two comparisons,
+ as then only the EQ bit is valid in the register. We also use a mode for
+ detecting signed overflow, as only the GT bit is valid in the register. */
CC_MODE (CCUNS);
CC_MODE (CCFP);
CC_MODE (CCEQ);
+CC_MODE (CCV);
/* Vector modes. */
VECTOR_MODES (INT, 8); /* V8QI V4HI V2SI */
===================================================================
@@ -127,8 +127,8 @@ extern int ccr_bit (rtx, int);
extern void rs6000_output_function_entry (FILE *, const char *);
extern void print_operand (FILE *, rtx, int);
extern void print_operand_address (FILE *, rtx);
-extern enum rtx_code rs6000_reverse_condition (machine_mode,
- enum rtx_code);
+extern machine_mode rs6000_select_cc_mode (enum rtx_code, rtx, rtx);
+extern enum rtx_code rs6000_reverse_condition (machine_mode, enum rtx_code);
extern rtx rs6000_emit_eqne (machine_mode, rtx, rtx, rtx);
extern void rs6000_emit_sISEL (machine_mode, rtx[]);
extern void rs6000_emit_sCOND (machine_mode, rtx[]);
===================================================================
@@ -2374,6 +2374,7 @@ rs6000_debug_reg_global (void)
CCmode,
CCUNSmode,
CCEQmode,
+ CCVmode
};
/* Virtual regs we are interested in. */
@@ -19334,6 +19335,7 @@ validate_condition_mode (enum rtx_code c
/* These are invalid; the information is not there. */
gcc_assert (mode != CCEQmode || code == EQ || code == NE);
+ gcc_assert (mode != CCVmode || code == EQ || code == NE);
}
@@ -21863,6 +21865,14 @@ print_operand (FILE *file, rtx x, int co
/* %c is output_addr_const if a CONSTANT_ADDRESS_P, otherwise
output_operand. */
+ case 'C':
+ /* X is a CR register. Print the index number of the CR. */
+ if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+ output_operand_lossage ("invalid %%E value");
+ else
+ fputs (reg_names[REGNO (x)], file);
+ return;
+
case 'D':
/* Like 'J' but get to the GT bit only. */
gcc_assert (REG_P (x));
@@ -22638,6 +22648,34 @@ rs6000_assemble_visibility (tree decl, i
}
#endif
+machine_mode
+rs6000_select_cc_mode (enum rtx_code op, rtx x, rtx y)
+{
+ /* For floating-point, CCFPmode should be used. CCUNSmode should be used
+ for unsigned comparisons. CCEQmode should be used when we are doing an
+ inequality comparison on the result of a comparison. CCVmode should be
+ used for the special overflow comparisons. CCmode should be used in all
+ the other cases. */
+
+ if (SCALAR_FLOAT_MODE_P (GET_MODE (x)))
+ return CCFPmode;
+
+ if ((op == GTU || op == LTU || op == GEU || op == LEU))
+ return CCUNSmode;
+
+ if ((op == EQ || op == NE) && COMPARISON_P (x))
+ return CCEQmode;
+
+ if (GET_CODE (y) == UNSPEC
+ && (XINT (y, 1) == UNSPEC_ADDV
+ || XINT (y, 1) == UNSPEC_SUBV
+ || XINT (y, 1) == UNSPEC_NEGV
+ || XINT (y, 1) == UNSPEC_MULV))
+ return CCVmode;
+
+ return CCmode;
+}
+
enum rtx_code
rs6000_reverse_condition (machine_mode mode, enum rtx_code code)
{
@@ -23558,7 +23596,6 @@ output_cbranch (rtx op, const char *labe
enum rtx_code code = GET_CODE (op);
rtx cc_reg = XEXP (op, 0);
machine_mode mode = GET_MODE (cc_reg);
- int cc_regno = REGNO (cc_reg) - CR0_REGNO;
int need_longbranch = label != NULL && get_attr_length (insn) == 8;
int really_reversed = reversed ^ need_longbranch;
char *s = string;
@@ -23601,6 +23638,24 @@ output_cbranch (rtx op, const char *labe
}
}
+ if (mode == CCVmode)
+ {
+ switch (code)
+ {
+ case EQ:
+ /* Opposite of GT. */
+ code = LE;
+ break;
+
+ case NE:
+ code = GT;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
switch (code)
{
/* Not all of these are actually distinct opcodes, but
@@ -23659,9 +23714,9 @@ output_cbranch (rtx op, const char *labe
/* We need to escape any '%' characters in the reg_names string.
Assume they'd only be the first character.... */
- if (reg_names[cc_regno + CR0_REGNO][0] == '%')
+ if (reg_names[REGNO (cc_reg)][0] == '%')
*s++ = '%';
- s += sprintf (s, "%s", reg_names[cc_regno + CR0_REGNO]);
+ s += sprintf (s, "%s", reg_names[REGNO (cc_reg)]);
if (label != NULL)
{
===================================================================
@@ -2225,17 +2225,8 @@ extern unsigned rs6000_pmode;
/* #define ADJUST_INSN_LENGTH(X,LENGTH) */
/* Given a comparison code (EQ, NE, etc.) and the first operand of a
- COMPARE, return the mode to be used for the comparison. For
- floating-point, CCFPmode should be used. CCUNSmode should be used
- for unsigned comparisons. CCEQmode should be used when we are
- doing an inequality comparison on the result of a
- comparison. CCmode should be used in all other cases. */
-
-#define SELECT_CC_MODE(OP,X,Y) \
- (SCALAR_FLOAT_MODE_P (GET_MODE (X)) ? CCFPmode \
- : (OP) == GTU || (OP) == LTU || (OP) == GEU || (OP) == LEU ? CCUNSmode \
- : (((OP) == EQ || (OP) == NE) && COMPARISON_P (X) \
- ? CCEQmode : CCmode))
+ COMPARE, return the mode to be used for the comparison. */
+#define SELECT_CC_MODE(OP,X,Y) rs6000_select_cc_mode (OP, X, Y)
/* Can the condition code MODE be safely reversed? This is safe in
all cases on this port, because at present it doesn't use the
===================================================================
@@ -149,6 +149,10 @@ (define_c_enum "unspec"
UNSPEC_IEEE128_CONVERT
UNSPEC_SIGNBIT
UNSPEC_DOLOOP
+ UNSPEC_ADDV
+ UNSPEC_SUBV
+ UNSPEC_NEGV
+ UNSPEC_MULV
])
;;
@@ -1636,6 +1640,39 @@ (define_expand "add<mode>3"
}
})
+(define_expand "addv<mode>4"
+ [(match_operand:SDI 0 "register_operand")
+ (match_operand:SDI 1 "register_operand")
+ (match_operand:SDI 2 "register_operand")
+ (match_operand 3 "")]
+ "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+ rtx cc_reg = gen_reg_rtx (CCVmode);
+
+ if (<MODE>mode == DImode && !TARGET_POWERPC64)
+ {
+ rtx lo0 = gen_lowpart (SImode, operands[0]);
+ rtx lo1 = gen_lowpart (SImode, operands[1]);
+ rtx lo2 = gen_lowpart (SImode, operands[2]);
+ rtx hi0 = gen_highpart (SImode, operands[0]);
+ rtx hi1 = gen_highpart (SImode, operands[1]);
+ rtx hi2 = gen_highpart (SImode, operands[2]);
+
+ emit_insn (gen_addsi3_carry (lo0, lo1, lo2));
+ emit_insn (gen_addsi3_overflow_carry_in (hi0, hi1, hi2, cc_reg));
+ }
+ else
+ emit_insn (gen_add<mode>3_overflow (operands[0], operands[1], operands[2],
+ cc_reg));
+
+ rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+ rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+ loc_ref, pc_rtx)));
+ DONE;
+})
+
(define_insn "*add<mode>3"
[(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r,r")
(plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b,b")
@@ -1837,6 +1874,21 @@ (define_insn "*add<mode>3_imm_carry_neg"
[(set_attr "type" "add")])
+;; The OV flag is set on overflow in Pmode only.
+(define_insn "add<mode>3_overflow"
+ [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+ (compare:CCV (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+ (match_operand:P 2 "gpc_reg_operand" "r"))
+ (unspec:P [(match_dup 1) (match_dup 2)]
+ UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (match_dup 1) (match_dup 2)))]
+ ""
+ "addo %0,%1,%2\;mcrxr %C3"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+
(define_expand "add<mode>3_carry_in"
[(parallel [
(set (match_operand:GPR 0 "gpc_reg_operand")
@@ -1888,6 +1940,83 @@ (define_insn "add<mode>3_carry_in_m1"
[(set_attr "type" "add")])
+(define_expand "add<mode>3_overflow_carry_in"
+ [(parallel [
+ (set (match_operand:CCV 3 "cc_reg_operand")
+ (compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand")
+ (match_operand:P 2 "adde_operand"))
+ (reg:P CA_REGNO))
+ (unspec:P [(plus:P (match_dup 1) (match_dup 2))
+ (reg:P CA_REGNO)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand")
+ (plus:P (plus:P (match_dup 1) (match_dup 2))
+ (reg:P CA_REGNO)))
+ (clobber (reg:P CA_REGNO))])]
+ ""
+{
+ if (operands[2] == const0_rtx)
+ {
+ emit_insn (gen_add<mode>3_overflow_carry_in_0 (operands[0],
+ operands[1],
+ operands[3]));
+ DONE;
+ }
+ if (operands[2] == constm1_rtx)
+ {
+ emit_insn (gen_add<mode>3_overflow_carry_in_m1 (operands[0],
+ operands[1],
+ operands[3]));
+ DONE;
+ }
+})
+
+(define_insn "*add<mode>3_overflow_carry_in_internal"
+ [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+ (compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+ (match_operand:P 2 "gpc_reg_operand" "r"))
+ (reg:P CA_REGNO))
+ (unspec:P [(plus:P (match_dup 1) (match_dup 2))
+ (reg:P CA_REGNO)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (plus:P (match_dup 1) (match_dup 2))
+ (reg:P CA_REGNO)))
+ (clobber (reg:GPR CA_REGNO))]
+ ""
+ "addeo %0,%1,%2\;mcrxr %C3"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+(define_insn "add<mode>3_overflow_carry_in_0"
+ [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+ (compare:CCV (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+ (reg:P CA_REGNO))
+ (unspec:P [(plus:P (match_dup 1) (reg:P CA_REGNO))]
+ UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (match_dup 1) (reg:P CA_REGNO)))
+ (clobber (reg:P CA_REGNO))]
+ ""
+ "addzeo %0,%1\;mcrxr %C2"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+(define_insn "add<mode>3_overflow_carry_in_m1"
+ [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+ (compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+ (reg:P CA_REGNO))
+ (const_int -1))
+ (unspec:P [(plus:P (match_dup 1) (reg:P CA_REGNO))
+ (const_int -1)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (plus:P (match_dup 1) (reg:P CA_REGNO))
+ (const_int -1)))
+ (clobber (reg:P CA_REGNO))]
+ ""
+ "addmeo %0,%1\;mcrxr %C2"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+
(define_expand "one_cmpl<mode>2"
[(set (match_operand:SDI 0 "gpc_reg_operand" "")
(not:SDI (match_operand:SDI 1 "gpc_reg_operand" "")))]
@@ -1980,6 +2109,39 @@ (define_expand "sub<mode>3"
}
})
+(define_expand "subv<mode>4"
+ [(match_operand:SDI 0 "register_operand")
+ (match_operand:SDI 1 "register_operand")
+ (match_operand:SDI 2 "register_operand")
+ (match_operand 3 "")]
+ "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+ rtx cc_reg = gen_reg_rtx (CCVmode);
+
+ if (<MODE>mode == DImode && !TARGET_POWERPC64)
+ {
+ rtx lo0 = gen_lowpart (SImode, operands[0]);
+ rtx lo1 = gen_lowpart (SImode, operands[1]);
+ rtx lo2 = gen_lowpart (SImode, operands[2]);
+ rtx hi0 = gen_highpart (SImode, operands[0]);
+ rtx hi1 = gen_highpart (SImode, operands[1]);
+ rtx hi2 = gen_highpart (SImode, operands[2]);
+
+ emit_insn (gen_subfsi3_carry (lo0, lo2, lo1));
+ emit_insn (gen_subfsi3_overflow_carry_in (hi0, hi2, hi1, cc_reg));
+ }
+ else
+ emit_insn (gen_subf<mode>3_overflow (operands[0], operands[2], operands[1],
+ cc_reg));
+
+ rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+ rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+ loc_ref, pc_rtx)));
+ DONE;
+})
+
(define_insn "*subf<mode>3"
[(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
(minus:GPR (match_operand:GPR 2 "gpc_reg_operand" "r")
@@ -2075,6 +2237,21 @@ (define_insn "*subf<mode>3_imm_carry_m1"
[(set_attr "type" "add")])
+;; The OV flag is set on overflow in Pmode only.
+(define_insn "subf<mode>3_overflow"
+ [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+ (compare:CCV (minus:P (match_operand:P 2 "gpc_reg_operand" "r")
+ (match_operand:P 1 "gpc_reg_operand" "r"))
+ (unspec:P [(match_dup 2) (match_dup 1)]
+ UNSPEC_SUBV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (minus:P (match_dup 2) (match_dup 1)))]
+ ""
+ "subfo %0,%1,%2\;mcrxr %C3"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+
(define_expand "subf<mode>3_carry_in"
[(parallel [
(set (match_operand:GPR 0 "gpc_reg_operand")
@@ -2135,6 +2312,87 @@ (define_insn "subf<mode>3_carry_in_xx"
[(set_attr "type" "add")])
+(define_expand "subf<mode>3_overflow_carry_in"
+ [(parallel [
+ (set (match_operand:CCV 3 "cc_reg_operand")
+ (compare:CCV
+ (plus:P (plus:P (not:P (match_operand:P 1 "gpc_reg_operand"))
+ (reg:P CA_REGNO))
+ (match_operand:P 2 "adde_operand"))
+ (unspec:P [(plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+ (match_dup 2)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand")
+ (plus:P (plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+ (match_dup 2)))
+ (clobber (reg:P CA_REGNO))])]
+ ""
+{
+ if (operands[2] == const0_rtx)
+ {
+ emit_insn (gen_subf<mode>3_overflow_carry_in_0 (operands[0],
+ operands[1],
+ operands[3]));
+ DONE;
+ }
+ if (operands[2] == constm1_rtx)
+ {
+ emit_insn (gen_subf<mode>3_overflow_carry_in_m1 (operands[0],
+ operands[1],
+ operands[3]));
+ DONE;
+ }
+})
+
+(define_insn "*sub<mode>3_overflow_carry_in_internal"
+ [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+ (compare:CCV
+ (plus:P (plus:P (not:P (match_operand:P 1 "gpc_reg_operand" "r"))
+ (reg:P CA_REGNO))
+ (match_operand:P 2 "gpc_reg_operand" "r"))
+ (unspec:P [(plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+ (match_dup 2)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+ (match_dup 2)))
+ (clobber (reg:P CA_REGNO))]
+ ""
+ "subfeo %0,%1,%2\;mcrxr %C3"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+(define_insn "subf<mode>3_overflow_carry_in_0"
+ [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+ (compare:CCV
+ (plus:P (not:P (match_operand:P 1 "gpc_reg_operand" "r"))
+ (reg:P CA_REGNO))
+ (unspec:P [(not:P (match_dup 1)) (reg:P CA_REGNO)]
+ UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (not:P (match_dup 1)) (reg:P CA_REGNO)))
+ (clobber (reg:P CA_REGNO))]
+ ""
+ "subfzeo %0,%1\;mcrxr %C2"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+(define_insn "subf<mode>3_overflow_carry_in_m1"
+ [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+ (compare:CCV
+ (plus:P (minus:P (reg:P CA_REGNO)
+ (match_operand:P 1 "gpc_reg_operand" "r"))
+ (const_int -2))
+ (unspec:P [(minus:P (reg:P CA_REGNO) (match_dup 1))
+ (const_int -2)] UNSPEC_ADDV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (plus:P (minus:P (reg:P CA_REGNO) (match_dup 1))
+ (const_int -2)))
+ (clobber (reg:P CA_REGNO))]
+ ""
+ "subfmeo %0,%1\;mcrxr %C2"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+
(define_insn "neg<mode>2"
[(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
(neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))]
@@ -2142,6 +2400,35 @@ (define_insn "neg<mode>2"
"neg %0,%1"
[(set_attr "type" "add")])
+(define_expand "negv<mode>3"
+ [(match_operand:SDI 0 "register_operand")
+ (match_operand:SDI 1 "register_operand")
+ (match_operand 2 "")]
+ "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+ rtx cc_reg = gen_reg_rtx (CCVmode);
+
+ if (<MODE>mode == DImode && !TARGET_POWERPC64)
+ {
+ rtx lo0 = gen_lowpart (SImode, operands[0]);
+ rtx lo1 = gen_lowpart (SImode, operands[1]);
+ rtx hi0 = gen_highpart (SImode, operands[0]);
+ rtx hi1 = gen_highpart (SImode, operands[1]);
+
+ emit_insn (gen_subfsi3_carry (lo0, lo1, const0_rtx));
+ emit_insn (gen_subfsi3_overflow_carry_in_0 (hi0, hi1, cc_reg));
+ }
+ else
+ emit_insn (gen_neg<mode>2_overflow (operands[0], operands[1], cc_reg));
+
+ rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+ rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[2]);
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+ loc_ref, pc_rtx)));
+ DONE;
+})
+
(define_insn_and_split "*neg<mode>2_dot"
[(set (match_operand:CC 2 "cc_reg_operand" "=x,?y")
(compare:CC (neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r"))
@@ -2184,6 +2471,18 @@ (define_insn_and_split "*neg<mode>2_dot2
(set_attr "length" "4,8")])
+(define_insn "neg<mode>2_overflow"
+ [(set (match_operand 2 "cc_reg_operand" "=y")
+ (compare:CCV (neg:P (match_operand:P 1 "gpc_reg_operand" "r"))
+ (unspec:P [(match_dup 1)] UNSPEC_NEGV)))
+ (set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (neg:P (match_dup 1)))]
+ ""
+ "nego %0,%1\;mcrxr %C2"
+ [(set_attr "type" "add")
+ (set_attr "length" "8")])
+
+
(define_insn "clz<mode>2"
[(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
(clz:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))]
@@ -2759,6 +3058,24 @@ (define_insn "mul<mode>3"
(const_string "16")]
(const_string "<bits>")))])
+(define_expand "mulv<mode>4"
+ [(match_operand:SDI 0 "register_operand")
+ (match_operand:SDI 1 "register_operand")
+ (match_operand:SDI 2 "register_operand")
+ (match_operand 3 "")]
+ "<MODE>mode == SImode || TARGET_POWERPC64"
+{
+ rtx cc_reg = gen_reg_rtx (CCVmode);
+ emit_insn (gen_mul<mode>3_overflow (operands[0], operands[1], operands[2],
+ cc_reg));
+ rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+ rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+ loc_ref, pc_rtx)));
+ DONE;
+})
+
(define_insn_and_split "*mul<mode>3_dot"
[(set (match_operand:CC 3 "cc_reg_operand" "=x,?y")
(compare:CC (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")
@@ -2807,6 +3124,20 @@ (define_insn_and_split "*mul<mode>3_dot2
(set_attr "dot" "yes")
(set_attr "length" "4,8")])
+;; The OV flag is set on overflow in SImode for mullw and DImode for mulld.
+(define_insn "mul<mode>3_overflow"
+ [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+ (compare:CCV (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")
+ (match_operand:GPR 2 "gpc_reg_operand" "r"))
+ (unspec:GPR [(match_dup 1) (match_dup 2)]
+ UNSPEC_MULV)))
+ (set (match_operand:GPR 0 "gpc_reg_operand" "=r")
+ (mult:GPR (match_dup 1) (match_dup 2)))]
+ ""
+ "mull<wd>o %0,%1,%2\;mcrxr %C3"
+ [(set_attr "type" "mul")
+ (set_attr "length" "8")])
+
(define_expand "<su>mul<mode>3_highpart"
[(set (match_operand:GPR 0 "gpc_reg_operand")
===================================================================
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int32_t a, int32_t *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-times "addo" 1 } } */
+/* { dg-final { scan-assembler-times "subfo" 1 } } */
+/* { dg-final { scan-assembler-times "nego" 1 } } */
+/* { dg-final { scan-assembler-times "mullwo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 4 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
===================================================================
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target lp64 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int32_t a, int32_t *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int32_t a, int32_t b, int32_t *res)
+{
+ return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-not "addo" } } */
+/* { dg-final { scan-assembler-not "subfo" } } */
+/* { dg-final { scan-assembler-not "nego" } } */
+/* { dg-final { scan-assembler-times "mullwo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 1 } } */
===================================================================
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int64_t a, int64_t b, int64_t *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int64_t a, int64_t b, int64_t *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int64_t a, int64_t *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+/* { dg-final { scan-assembler-times "addeo" 1 } } */
+/* { dg-final { scan-assembler-times "subfeo" 1 } } */
+/* { dg-final { scan-assembler-times "subfzeo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 3 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
===================================================================
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target lp64 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int64_t a, int64_t b, int64_t *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int64_t a, int64_t b, int64_t *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int64_t a, int64_t *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int64_t a, int64_t b, int64_t *res)
+{
+ return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-times "addo" 1 } } */
+/* { dg-final { scan-assembler-times "subfo" 1 } } */
+/* { dg-final { scan-assembler-times "nego" 1 } } */
+/* { dg-final { scan-assembler-times "mulldo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 4 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */