S/390: Add add/sub/mul overflow check patterns

This patch implements the addv, subv, and mulv patterns for signed
integers.

gcc/ChangeLog:

2019-07-24  Andreas Krebbel  <krebbel@linux.ibm.com>

	* config/s390/predicates.md (addv_const_operand): New predicate.
	* config/s390/s390-modes.def (CCO): New condition code mode.
	* config/s390/s390.c (s390_match_ccmode_set): Handle E_CCOmode.
	(s390_branch_condition_mask): Likewise.
	* config/s390/s390.md ("addv<mode>4", "subv<mode>4")
	("mulv<mode>4"): New expanders.
	("*addv<mode>3_ccoverflow", "*addv<mode>3_ccoverflow_const")
	("*subv<mode>3_ccoverflow", "*mulv<mode>3_ccoverflow"): New
	pattern definitions.

gcc/testsuite/ChangeLog:

2019-07-24  Andreas Krebbel  <krebbel@linux.ibm.com>

	* gcc.target/s390/addsub-signed-overflow-1.c: New test.
	* gcc.target/s390/addsub-signed-overflow-2.c: New test.
	* gcc.target/s390/mul-signed-overflow-1.c: New test.
	* gcc.target/s390/mul-signed-overflow-2.c: New test.

From-SVN: r273759
This commit is contained in:
Andreas Krebbel 2019-07-24 11:27:56 +00:00 committed by Andreas Krebbel
parent 21caa1a264
commit 4caa6bab76
10 changed files with 466 additions and 0 deletions

View File

@ -1,3 +1,15 @@
2019-07-24 Andreas Krebbel <krebbel@linux.ibm.com>
* config/s390/predicates.md (addv_const_operand): New predicate.
* config/s390/s390-modes.def (CCO): New condition code mode.
* config/s390/s390.c (s390_match_ccmode_set): Handle E_CCOmode.
(s390_branch_condition_mask): Likewise.
* config/s390/s390.md ("addv<mode>4", "subv<mode>4")
("mulv<mode>4"): New expanders.
("*addv<mode>3_ccoverflow", "*addv<mode>3_ccoverflow_const")
("*subv<mode>3_ccoverflow", "*mulv<mode>3_ccoverflow"): New
pattern definitions.
2019-07-24 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
PR middle-end/91166

View File

@ -585,3 +585,9 @@
return s390_valid_shift_count (op, 0);
}
)
; An integer constant which can be used in a signed add with overflow
; pattern without being reloaded.
(define_predicate "addv_const_operand"
(and (match_code "const_int")
(match_test "INTVAL (op) >= -32768 && INTVAL (op) <= 32767")))

View File

@ -31,6 +31,8 @@ FLOAT_MODE (TF, 16, ieee_quad_format);
Condition Codes
CC0 CC1 CC2 CC3
Check for zero
CCZ: EQ NE NE NE
@ -57,6 +59,10 @@ CCA: EQ LT GT Overflow
CCAP: EQ LT GT LT (AGHI, AHI)
CCAN: EQ LT GT GT (AGHI, AHI)
Condition codes for overflow checking resulting from signed adds/subs/mults
CCO: EQ EQ EQ NE (AGR, AGHI, SGR, MSC, ...)
Condition codes of unsigned adds and subs
CCL: EQ NE EQ NE (ALGF/R, ALG/R, AL/R/Y,
@ -98,6 +104,13 @@ If you know whether the used constant is positive or negative you can predict
the sign of the result even in case of an overflow.
CCO
This mode is used to check whether there was an overflow condition in
a signed add, sub, or mul operation. See (addv<mode>4, subv<mode>4,
mulv<mode>4 patterns).
CCT, CCT1, CCT2, CCT3
If bits of an integer masked with an AND instruction are checked, the test under
@ -204,6 +217,7 @@ CC_MODE (CCZ1);
CC_MODE (CCA);
CC_MODE (CCAP);
CC_MODE (CCAN);
CC_MODE (CCO);
CC_MODE (CCL);
CC_MODE (CCL1);
CC_MODE (CCL2);

View File

@ -1378,6 +1378,7 @@ s390_match_ccmode_set (rtx set, machine_mode req_mode)
case E_CCSRmode:
case E_CCUmode:
case E_CCURmode:
case E_CCOmode:
case E_CCLmode:
case E_CCL1mode:
case E_CCL2mode:
@ -2071,6 +2072,15 @@ s390_branch_condition_mask (rtx code)
}
break;
case E_CCOmode:
switch (GET_CODE (code))
{
case EQ: return CC0 | CC1 | CC2;
case NE: return CC3;
default: return -1;
}
break;
case E_CCSmode:
switch (GET_CODE (code))
{

View File

@ -5961,6 +5961,83 @@
"agh\t%0,%2"
[(set_attr "op_type" "RXY")])
; Jump to label OP3 if OP1 + OP2 results in a signed overflow
; addv_const_operand accepts all constants which can be handled
; without reloads. These will be handled primarily by
; "*addv<mode>3_ccoverflow_const" which doesn't provide a register
; alternative. Hence we have to match the operand exactly.
; For immediates we have to avoid the SIGN_EXTEND around OP2.
(define_expand "addv<mode>4"
[(parallel
[(set (reg:CCO CC_REGNUM)
(compare:CCO (plus:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "nonimmediate_operand"))
(match_dup 4))
(sign_extend:<DBL> (plus:GPR (match_dup 1)
(match_operand:GPR 2 "general_operand")))))
(set (match_operand:GPR 0 "nonimmediate_operand")
(plus:GPR (match_dup 1) (match_dup 2)))])
(set (pc)
(if_then_else (ne (reg:CCO CC_REGNUM) (const_int 0))
(label_ref (match_operand 3))
(pc)))]
""
{
if (CONSTANT_P (operands[2])
&& !addv_const_operand (operands[2], GET_MODE (operands[2])))
operands[2] = force_reg (<GPR:MODE>mode, operands[2]);
if (GET_MODE (operands[2]) != VOIDmode)
operands[4] = gen_rtx_SIGN_EXTEND (<DBL>mode, operands[2]);
else
/* This is what CSE does when propagating a constant into the pattern. */
operands[4] = simplify_unary_operation (SIGN_EXTEND, <GPR:DBL>mode, operands[2], <GPR:MODE>mode);
})
; ark, agrk, ar, ahi, ahik, aghik, a, ay, agr, aghi, ag, asi, agsi
(define_insn "*addv<mode>3_ccoverflow"
[(set (reg CC_REGNUM)
(compare (plus:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,d,0,0,0"))
(sign_extend:<DBL> (match_operand:GPR 2 "general_operand" " d,d,K,K,R,T,C")))
(sign_extend:<DBL> (plus:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "nonimmediate_operand" "=d,d,d,d,d,d,S")
(plus:GPR (match_dup 1) (match_dup 2)))]
"s390_match_ccmode (insn, CCOmode)"
"@
a<g>r\t%0,%2
a<g>rk\t%0,%1,%2
a<g>hi\t%0,%h2
a<g>hik\t%0,%1,%h2
a<g>\t%0,%2
a<y>\t%0,%2
a<g>si\t%0,%c2"
[(set_attr "op_type" "RR<E>,RRF,RI,RIE,RX<Y>,RXY,SIY")
(set_attr "cpu_facility" "*,z196,*,z196,*,longdisp,z10")
(set_attr "z10prop" "z10_super_E1,*,z10_super_E1,*,
z10_super_E1,z10_super_E1,z10_super_E1")])
; ahi, aghi, ahik, aghik, asi, agsi
(define_insn "*addv<mode>3_ccoverflow_const"
[(set (reg CC_REGNUM)
(compare (plus:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0"))
(match_operand:<DBL> 2 "addv_const_operand" "K,K,C"))
(sign_extend:<DBL> (plus:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "nonimmediate_operand" "=d,d,S")
(plus:GPR (match_dup 1) (match_dup 2)))]
"s390_match_ccmode (insn, CCOmode)"
"@
a<g>hi\t%0,%h2
a<g>hik\t%0,%1,%h2
a<g>si\t%0,%c2"
[(set_attr "op_type" "RI,RIE,SIY")
(set_attr "cpu_facility" "*,z196,z10")
(set_attr "z10prop" "z10_super_E1,*,z10_super_E1")])
;
; add(tf|df|sf|td|dd)3 instruction pattern(s).
;
@ -6370,6 +6447,41 @@
"sgh\t%0,%2"
[(set_attr "op_type" "RXY")])
; Jump to label OP3 if OP1 - OP2 results in a signed overflow
(define_expand "subv<mode>4"
[(parallel
[(set (reg:CCO CC_REGNUM)
(compare:CCO (minus:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "nonimmediate_operand"))
(sign_extend:<DBL> (match_operand:GPR 2 "nonimmediate_operand")))
(sign_extend:<DBL> (minus:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "nonimmediate_operand")
(minus:GPR (match_dup 1) (match_dup 2)))])
(set (pc)
(if_then_else (ne (reg:CCO CC_REGNUM) (const_int 0))
(label_ref (match_operand 3))
(pc)))]
"")
; sr, s, sy, sgr, sg, srk, sgrk
(define_insn "*subv<mode>3_ccoverflow"
[(set (reg CC_REGNUM)
(compare (minus:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "nonimmediate_operand" "0,d,0,0"))
(sign_extend:<DBL> (match_operand:GPR 2 "nonimmediate_operand" "d,d,R,T")))
(sign_extend:<DBL> (minus:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "register_operand" "=d,d,d,d")
(minus:GPR (match_dup 1) (match_dup 2)))]
"s390_match_ccmode (insn, CCOmode)"
"@
s<g>r\t%0,%2
s<g>rk\t%0,%1,%2
s<g>\t%0,%2
s<y>\t%0,%2"
[(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY")
(set_attr "cpu_facility" "*,z196,*,longdisp")
(set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")])
;
; sub(tf|df|sf|td|dd)3 instruction pattern(s).
@ -6888,6 +7000,38 @@
(set_attr "type" "imulsi")
(set_attr "cpu_facility" "*,*,z10")])
; Jump to label OP3 if OP1 * OP2 results in a signed overflow
(define_expand "mulv<mode>4"
[(parallel
[(set (reg:CCO CC_REGNUM)
(compare:CCO (mult:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "register_operand"))
(sign_extend:<DBL> (match_operand:GPR 2 "nonimmediate_operand")))
(sign_extend:<DBL> (mult:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "register_operand")
(mult:GPR (match_dup 1) (match_dup 2)))])
(set (pc)
(if_then_else (ne (reg:CCO CC_REGNUM) (const_int 0))
(label_ref (match_operand 3))
(pc)))]
"TARGET_Z14")
; msrkc, msc, msgrkc, msgc
(define_insn "*mulv<mode>3_ccoverflow"
[(set (reg CC_REGNUM)
(compare (mult:<DBL>
(sign_extend:<DBL> (match_operand:GPR 1 "register_operand" "%d,0"))
(sign_extend:<DBL> (match_operand:GPR 2 "nonimmediate_operand" " d,T")))
(sign_extend:<DBL> (mult:GPR (match_dup 1) (match_dup 2)))))
(set (match_operand:GPR 0 "register_operand" "=d,d")
(mult:GPR (match_dup 1) (match_dup 2)))]
"s390_match_ccmode (insn, CCOmode) && TARGET_Z14"
"@
ms<g>rkc\t%0,%1,%2
ms<g>c\t%0,%2"
[(set_attr "op_type" "RRF,RXY")])
;
; umul instruction pattern(s).
;

View File

@ -1,3 +1,10 @@
2019-07-24 Andreas Krebbel <krebbel@linux.ibm.com>
* gcc.target/s390/addsub-signed-overflow-1.c: New test.
* gcc.target/s390/addsub-signed-overflow-2.c: New test.
* gcc.target/s390/mul-signed-overflow-1.c: New test.
* gcc.target/s390/mul-signed-overflow-2.c: New test.
2019-07-24 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
PR middle-end/91166

View File

@ -0,0 +1,81 @@
/* { dg-do run } */
/* { dg-options "-O3 -mzarch --save-temps" } */
#include <stddef.h>
#include <limits.h>
int __attribute__((noinline,noclone))
sadd (int a, int b, int *res)
{
return __builtin_sadd_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
ssub (int a, int b, int *res)
{
return __builtin_ssub_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
saddl (long a, long b, long *res)
{
return __builtin_saddl_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
ssubl (long a, long b, long *res)
{
return __builtin_ssubl_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
saddll (long long a, long long b, long long *res)
{
return __builtin_saddll_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
ssubll (long long a, long long b, long long *res)
{
return __builtin_ssubll_overflow(a, b, res);
}
/* With the attribute at least main always uses the same instructions
regardless of the -march setting. This is necessary for the
scan-assembler-times directive below. */
int __attribute__ ((target("arch=z10")))
main ()
{
int ret = 0;
int result;
long lresult;
long long llresult;
ret += !!sadd (INT_MAX, 1, &result);
ret += !!ssub (INT_MIN, 1, &result);
ret += !!saddl (LONG_MAX, 1, &lresult);
ret += !!ssubl (LONG_MIN, 1, &lresult);
ret += !!saddll (LLONG_MAX, 1, &llresult);
ret += !!ssubll (LLONG_MIN, 1, &llresult);
if (ret != 6)
__builtin_abort ();
return 0;
}
/* Check that no compare or bitop instructions are emitted. */
/* { dg-final { scan-assembler-not "\tcr" } } */
/* { dg-final { scan-assembler-not "\txr" } } */
/* { dg-final { scan-assembler-not "\tnr" } } */
/* { dg-final { scan-assembler-not "\tcgr" } } */
/* { dg-final { scan-assembler-not "\txgr" } } */
/* { dg-final { scan-assembler-not "\tngr" } } */
/* On 31 bit the long long variants use risbgn to merge the 32 bit
regs into a 64 bit reg. */
/* { dg-final { scan-assembler-not "\trisbg" { target { lp64 } } } } */
/* Just one for the ret != 6 comparison. */
/* { dg-final { scan-assembler-times "ci" 1 } } */

View File

@ -0,0 +1,80 @@
/* { dg-do run } */
/* { dg-options "-O3 -mzarch --save-temps" } */
#include <stddef.h>
#include <limits.h>
int __attribute__((noinline,noclone))
sadd (int a, int *res)
{
return __builtin_sadd_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
ssub (int a, int *res)
{
return __builtin_ssub_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
saddl (long a, long *res)
{
return __builtin_saddl_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
ssubl (long a, long *res)
{
return __builtin_ssubl_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
saddll (long long a, long long *res)
{
return __builtin_saddll_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
ssubll (long long a, long long *res)
{
return __builtin_ssubll_overflow(a, -1, res);
}
/* With the attribute at least main always uses the same instructions
regardless of the -march setting. This is necessary for the
scan-assembler-times directive below. */
int __attribute__ ((target("arch=z10")))
main ()
{
int ret = 0;
int result;
long lresult;
long long llresult;
ret += !!sadd (INT_MIN, &result);
ret += !!ssub (INT_MIN, &result);
ret += !!saddl (LONG_MIN, &lresult);
ret += !!ssubl (LONG_MIN, &lresult);
ret += !!saddll (LLONG_MIN, &llresult);
ret += !!ssubll (LLONG_MIN, &llresult);
if (ret != 3)
__builtin_abort ();
return 0;
}
/* Check that no compare or bitop instructions are emitted. */
/* { dg-final { scan-assembler-not "\tcr" } } */
/* { dg-final { scan-assembler-not "\txr" } } */
/* { dg-final { scan-assembler-not "\tnr" } } */
/* { dg-final { scan-assembler-not "\tcgr" } } */
/* { dg-final { scan-assembler-not "\txgr" } } */
/* { dg-final { scan-assembler-not "\tngr" } } */
/* On 31 bit the long long variants use risbgn to merge the 32 bit
regs into a 64 bit reg. */
/* { dg-final { scan-assembler-not "\trisbg" { target { lp64 } } } } */
/* Just one for the ret != 3 comparison. */
/* { dg-final { scan-assembler-times "ci" 1 } } */

View File

@ -0,0 +1,56 @@
/* { dg-do run } */
/* z14 only because we need msrkc, msc, msgrkc, msgc */
/* { dg-options "-O3 -march=z14 -mzarch --save-temps" } */
#include <stddef.h>
#include <limits.h>
int __attribute__((noinline,noclone))
smul (int a, int b, int *res)
{
return __builtin_smul_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
smull (long a, long b, long *res)
{
return __builtin_smull_overflow(a, b, res);
}
int __attribute__((noinline,noclone))
smulll (long long a, long long b, long long *res)
{
return __builtin_smulll_overflow(a, b, res);
}
int
main ()
{
int ret = 0;
int result;
long lresult;
long long llresult;
ret += !!smul (INT_MAX, 2, &result);
ret += !!smull (LONG_MAX, 2, &lresult);
ret += !!smulll (LLONG_MAX, 2, &llresult);
if (ret != 3)
__builtin_abort ();
return 0;
}
/* Check that no compare or bitop instructions are emitted. */
/* { dg-final { scan-assembler-not "\tcr" } } */
/* { dg-final { scan-assembler-not "\txr" } } */
/* { dg-final { scan-assembler-not "\tnr" } } */
/* { dg-final { scan-assembler-not "\tcgr" } } */
/* { dg-final { scan-assembler-not "\txgr" } } */
/* { dg-final { scan-assembler-not "\tngr" } } */
/* On 31 bit the long long variants use risbgn to merge the 32 bit
regs into a 64 bit reg. */
/* { dg-final { scan-assembler-not "\trisbg" { target { lp64 } } } } */
/* Just one for the ret != 3 comparison. */
/* { dg-final { scan-assembler-times "ci" 1 } } */

View File

@ -0,0 +1,56 @@
/* { dg-do run } */
/* z14 only because we need msrkc, msc, msgrkc, msgc */
/* { dg-options "-O3 -march=z14 -mzarch --save-temps" } */
#include <stddef.h>
#include <limits.h>
int __attribute__((noinline,noclone))
smul (int a, int *res)
{
return __builtin_smul_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
smull (long a, long *res)
{
return __builtin_smull_overflow(a, -1, res);
}
int __attribute__((noinline,noclone))
smulll (long long a, long long *res)
{
return __builtin_smulll_overflow(a, -1, res);
}
int
main ()
{
int ret = 0;
int result;
long lresult;
long long llresult;
ret += !!smul (INT_MIN, &result);
ret += !!smull (LONG_MIN, &lresult);
ret += !!smulll (LLONG_MIN, &llresult);
if (ret != 3)
__builtin_abort ();
return 0;
}
/* Check that no compare or bitop instructions are emitted. */
/* { dg-final { scan-assembler-not "\tcr" } } */
/* { dg-final { scan-assembler-not "\txr" } } */
/* { dg-final { scan-assembler-not "\tnr" } } */
/* { dg-final { scan-assembler-not "\tcgr" } } */
/* { dg-final { scan-assembler-not "\txgr" } } */
/* { dg-final { scan-assembler-not "\tngr" } } */
/* On 31 bit the long long variants use risbgn to merge the 32 bit
regs into a 64 bit reg. */
/* { dg-final { scan-assembler-not "\trisbg" { target { lp64 } } } } */
/* Just one for the ret != 3 comparison. */
/* { dg-final { scan-assembler-times "ci" 1 } } */