Bootstrap and regtest are still running. If those are successful and there are no further comments I will push this one in the coming days.
-- >8 -- gcc/ChangeLog: * config/s390/s390-protos.h (s390_emit_compare): Add mode parameter for the resulting RTX. * config/s390/s390.cc (s390_emit_compare): Dito. (s390_emit_compare_and_swap): Change. (s390_expand_vec_strlen): Change. (s390_expand_cs_hqi): Change. (s390_expand_split_stack_prologue): Change. * config/s390/s390.md (*add<mode>3_carry1_cc): Renamed to ... (add<mode>3_carry1_cc): this and in order to use the corresponding gen function, encode CC mode into pattern. (*sub<mode>3_borrow_cc): Renamed to ... (sub<mode>3_borrow_cc): this and in order to use the corresponding gen function, encode CC mode into pattern. (*add<mode>3_alc_carry1_cc): Renamed to ... (add<mode>3_alc_carry1_cc): this and in order to use the corresponding gen function, encode CC mode into pattern. (sub<mode>3_slb_borrow1_cc): New. (uaddc<mode>5): New. (usubc<mode>5): New. gcc/testsuite/ChangeLog: * gcc.target/s390/uaddc-1.c: New test. * gcc.target/s390/uaddc-2.c: New test. * gcc.target/s390/uaddc-3.c: New test. * gcc.target/s390/usubc-1.c: New test. * gcc.target/s390/usubc-2.c: New test. * gcc.target/s390/usubc-3.c: New test. --- gcc/config/s390/s390-protos.h | 2 +- gcc/config/s390/s390.cc | 20 +-- gcc/config/s390/s390.md | 115 +++++++++++++---- gcc/testsuite/gcc.target/s390/uaddc-1.c | 156 ++++++++++++++++++++++++ gcc/testsuite/gcc.target/s390/uaddc-2.c | 25 ++++ gcc/testsuite/gcc.target/s390/uaddc-3.c | 27 ++++ gcc/testsuite/gcc.target/s390/usubc-1.c | 156 ++++++++++++++++++++++++ gcc/testsuite/gcc.target/s390/usubc-2.c | 25 ++++ gcc/testsuite/gcc.target/s390/usubc-3.c | 29 +++++ 9 files changed, 519 insertions(+), 36 deletions(-) create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-1.c create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-2.c create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-3.c create mode 100644 gcc/testsuite/gcc.target/s390/usubc-1.c create mode 100644 gcc/testsuite/gcc.target/s390/usubc-2.c create mode 100644 gcc/testsuite/gcc.target/s390/usubc-3.c diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h index e7ac59d17da..b8604394391 100644 --- a/gcc/config/s390/s390-protos.h +++ b/gcc/config/s390/s390-protos.h @@ -86,7 +86,7 @@ extern int tls_symbolic_operand (rtx); extern bool s390_match_ccmode (rtx_insn *, machine_mode); extern machine_mode s390_tm_ccmode (rtx, rtx, bool); extern machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx); -extern rtx s390_emit_compare (enum rtx_code, rtx, rtx); +extern rtx s390_emit_compare (machine_mode, enum rtx_code, rtx, rtx); extern rtx_insn *s390_emit_jump (rtx, rtx); extern bool symbolic_reference_mentioned_p (rtx); extern bool tls_symbolic_reference_mentioned_p (rtx); diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc index c9172d1153a..4c8bf21539c 100644 --- a/gcc/config/s390/s390.cc +++ b/gcc/config/s390/s390.cc @@ -2029,9 +2029,9 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1, the IF_THEN_ELSE of the conditional branch testing the result. */ rtx -s390_emit_compare (enum rtx_code code, rtx op0, rtx op1) +s390_emit_compare (machine_mode mode, enum rtx_code code, rtx op0, rtx op1) { - machine_mode mode = s390_select_ccmode (code, op0, op1); + machine_mode cc_mode = s390_select_ccmode (code, op0, op1); rtx cc; /* Force OP1 into register in order to satisfy VXE TFmode patterns. */ @@ -2043,17 +2043,17 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1) /* Do not output a redundant compare instruction if a compare_and_swap pattern already computed the result and the machine modes are compatible. */ - gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), mode) + gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), cc_mode) == GET_MODE (op0)); cc = op0; } else { - cc = gen_rtx_REG (mode, CC_REGNUM); - emit_insn (gen_rtx_SET (cc, gen_rtx_COMPARE (mode, op0, op1))); + cc = gen_rtx_REG (cc_mode, CC_REGNUM); + emit_insn (gen_rtx_SET (cc, gen_rtx_COMPARE (cc_mode, op0, op1))); } - return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); + return gen_rtx_fmt_ee (code, mode, cc, const0_rtx); } /* If MEM is not a legitimate compare-and-swap memory operand, return a new @@ -2103,7 +2103,7 @@ s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, default: gcc_unreachable (); } - return s390_emit_compare (code, cc, const0_rtx); + return s390_emit_compare (VOIDmode, code, cc, const0_rtx); } /* Emit a jump instruction to TARGET and return it. If COND is @@ -6647,7 +6647,7 @@ s390_expand_vec_strlen (rtx target, rtx string, rtx alignment) Now we have to check whether the resulting index lies within the bytes actually part of the string. */ - cond = s390_emit_compare (GT, convert_to_mode (Pmode, len, 1), + cond = s390_emit_compare (VOIDmode, GT, convert_to_mode (Pmode, len, 1), highest_index_to_load_reg); s390_load_address (highest_index_to_load_reg, gen_rtx_PLUS (Pmode, highest_index_to_load_reg, @@ -7845,7 +7845,7 @@ s390_expand_cs_hqi (machine_mode mode, rtx btarget, rtx vtarget, rtx mem, tmp = copy_to_reg (val); force_expand_binop (SImode, and_optab, res, ac.modemaski, val, 1, OPTAB_DIRECT); - cc = s390_emit_compare (NE, val, tmp); + cc = s390_emit_compare (VOIDmode, NE, val, tmp); s390_emit_jump (csloop, cc); /* Failed. */ @@ -12694,7 +12694,7 @@ s390_expand_split_stack_prologue (void) } /* Compare the (maybe adjusted) guard with the stack pointer. */ - cc = s390_emit_compare (LT, stack_pointer_rtx, guard); + cc = s390_emit_compare (VOIDmode, LT, stack_pointer_rtx, guard); } call_done = gen_label_rtx (); diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md index 4a225ae24f3..5f2f97dad45 100644 --- a/gcc/config/s390/s390.md +++ b/gcc/config/s390/s390.md @@ -6001,14 +6001,14 @@ z10_super_E1,z10_super_E1,z10_super_E1")]) ; alr, alfi, slfi, al, aly, alrk, alhsik, algr, algfi, slgfi, alg, alsi, algsi, algrk, alghsik -(define_insn "*add<mode>3_carry1_cc" - [(set (reg CC_REGNUM) - (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 0, 0,d,0,0,0") - (match_operand:GPR 2 "general_operand" " d,d,Op,On,K,R,T,C")) - (match_dup 1))) - (set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,d,d,d,d") +(define_insn "add<mode>3_carry1_cc" + [(set (reg:CCL1 CC_REGNUM) + (compare:CCL1 (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 0, 0,d,0,0,0") + (match_operand:GPR 2 "general_operand" " d,d,Op,On,K,R,T,C")) + (match_dup 1))) + (set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,d,d,d,d") (plus:GPR (match_dup 1) (match_dup 2)))] - "s390_match_ccmode (insn, CCL1mode)" + "" "@ al<g>r\t%0,%2 al<g>rk\t%0,%1,%2 @@ -6541,14 +6541,14 @@ (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) ; slr, sl, sly, slgr, slg, slrk, slgrk -(define_insn "*sub<mode>3_borrow_cc" - [(set (reg CC_REGNUM) - (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") - (match_operand:GPR 2 "general_operand" "d,d,R,T")) - (match_dup 1))) - (set (match_operand:GPR 0 "register_operand" "=d,d,d,d") +(define_insn "sub<mode>3_borrow_cc" + [(set (reg:CCL2 CC_REGNUM) + (compare:CCL2 (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (match_dup 1))) + (set (match_operand:GPR 0 "register_operand" "=d,d,d,d") (minus:GPR (match_dup 1) (match_dup 2)))] - "s390_match_ccmode (insn, CCL2mode)" + "" "@ sl<g>r\t%0,%2 sl<g>rk\t%0,%1,%2 @@ -6754,22 +6754,43 @@ ; add(di|si)cc instruction pattern(s). ; +(define_expand "uaddc<mode>5" + [(match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "nonimmediate_operand") + (match_operand:GPR 2 "nonimmediate_operand") + (match_operand:GPR 3 "nonimmediate_operand") + (match_operand:GPR 4 "general_operand")] + "TARGET_Z196" +{ + if (operands[4] == const0_rtx) + emit_insn (gen_add<mode>3_carry1_cc (operands[0], operands[2], operands[3])); + else + { + operands[4] = force_reg (<MODE>mode, operands[4]); + rtx alc_cond = s390_emit_compare (<MODE>mode, GTU, operands[4], const0_rtx); + emit_insn (gen_add<mode>3_alc_carry1_cc (operands[0], operands[2], operands[3], alc_cond)); + } + rtx mov_cond = gen_rtx_LTU (<MODE>mode, gen_rtx_REG (CCL1mode, CC_REGNUM), const0_rtx); + emit_insn (gen_mov<mode>cc (operands[1], mov_cond, const1_rtx, const0_rtx)); + DONE; +}) + ; the following 4 patterns are used when the result of an add with ; carry is checked for an overflow condition ; op1 + op2 + c < op1 ; alcr, alc, alcgr, alcg -(define_insn "*add<mode>3_alc_carry1_cc" - [(set (reg CC_REGNUM) - (compare +(define_insn "add<mode>3_alc_carry1_cc" + [(set (reg:CCL1 CC_REGNUM) + (compare:CCL1 (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) (match_operand:GPR 2 "general_operand" "d,T")) (match_dup 1))) (set (match_operand:GPR 0 "register_operand" "=d,d") (plus:GPR (plus:GPR (match_dup 3) (match_dup 1)) (match_dup 2)))] - "s390_match_ccmode (insn, CCL1mode)" + "" "@ alc<g>r\t%0,%2 alc<g>\t%0,%2" @@ -6854,6 +6875,31 @@ alc<g>\t%0,%2" [(set_attr "op_type" "RRE,RXY")]) +(define_expand "usubc<mode>5" + [(match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "nonimmediate_operand") + (match_operand:GPR 2 "nonimmediate_operand") + (match_operand:GPR 3 "nonimmediate_operand") + (match_operand:GPR 4 "general_operand")] + "TARGET_Z196" +{ + if (operands[4] == const0_rtx) + { + operands[2] = force_reg (<MODE>mode, operands[2]); + emit_insn (gen_sub<mode>3_borrow_cc (operands[0], operands[2], operands[3])); + } + else + { + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_xor<mode>3 (tmp, operands[4], const1_rtx)); + rtx slb_cond = s390_emit_compare (<MODE>mode, LEU, tmp, const0_rtx); + emit_insn (gen_sub<mode>3_slb_borrow1_cc (operands[0], operands[2], operands[3], slb_cond)); + } + rtx mov_cond = gen_rtx_GTU (<MODE>mode, gen_rtx_REG (CCL2mode, CC_REGNUM), const0_rtx); + emit_insn (gen_mov<mode>cc (operands[1], mov_cond, const1_rtx, const0_rtx)); + DONE; +}) + ; slbr, slb, slbgr, slbg (define_insn "*sub<mode>3_slb_cc" [(set (reg CC_REGNUM) @@ -6885,6 +6931,23 @@ [(set_attr "op_type" "RRE,RXY") (set_attr "z10prop" "z10_c,*")]) +; slbr, slb, slbgr, slbg +(define_insn "sub<mode>3_slb_borrow1_cc" + [(set (reg:CCL2 CC_REGNUM) + (compare:CCL2 + (minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") + (match_operand:GPR 2 "general_operand" "d,T")) + (match_operand:GPR 3 "s390_slb_comparison" "")) + (match_dup 1))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (minus:GPR (minus:GPR (match_dup 1) (match_dup 2)) (match_dup 3)))] + "" + "@ + slb<g>r\t%0,%2 + slb<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_c,*")]) + (define_expand "add<mode>cc" [(match_operand:GPR 0 "register_operand" "") (match_operand 1 "comparison_operator" "") @@ -7018,7 +7081,8 @@ /* Emit the comparison insn in case we do not already have a comparison result. */ if (!s390_comparison (operands[1], VOIDmode)) - operands[1] = s390_emit_compare (GET_CODE (operands[1]), + operands[1] = s390_emit_compare (VOIDmode, + GET_CODE (operands[1]), XEXP (operands[1], 0), XEXP (operands[1], 1)); }) @@ -7036,7 +7100,8 @@ /* Emit the comparison insn in case we do not already have a comparison result. */ if (!s390_comparison (operands[1], VOIDmode)) - operands[1] = s390_emit_compare (GET_CODE (operands[1]), + operands[1] = s390_emit_compare (VOIDmode, + GET_CODE (operands[1]), XEXP (operands[1], 0), XEXP (operands[1], 1)); @@ -9424,7 +9489,7 @@ (pc)))] "" "s390_emit_jump (operands[3], - s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2])); + s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], operands[2])); DONE;") (define_expand "cbranch<mode>4" @@ -9436,7 +9501,7 @@ (pc)))] "TARGET_HARD_FLOAT" "s390_emit_jump (operands[3], - s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2])); + s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], operands[2])); DONE;") (define_expand "cbranchcc4" @@ -9591,7 +9656,7 @@ (match_operand 3 "const0_operand" ""))] "" { - rtx cond = s390_emit_compare (GET_CODE (operands[0]), + rtx cond = s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], operands[2]); emit_insn (gen_condtrap (cond, XEXP (cond, 0))); DONE; @@ -9604,7 +9669,7 @@ (match_operand 3 "const0_operand" ""))] "" { - rtx cond = s390_emit_compare (GET_CODE (operands[0]), + rtx cond = s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], operands[2]); emit_insn (gen_condtrap (cond, XEXP (cond, 0))); DONE; @@ -12245,7 +12310,7 @@ emit_insn (gen_subdi3 (reg, stack_pointer_rtx, operands[0])); else emit_insn (gen_subsi3 (reg, stack_pointer_rtx, operands[0])); - cc = s390_emit_compare (GT, reg, guard); + cc = s390_emit_compare (VOIDmode, GT, reg, guard); s390_emit_jump (operands[1], cc); DONE; diff --git a/gcc/testsuite/gcc.target/s390/uaddc-1.c b/gcc/testsuite/gcc.target/s390/uaddc-1.c new file mode 100644 index 00000000000..bec55942ea8 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/uaddc-1.c @@ -0,0 +1,156 @@ +/* { dg-do run { target { s390_useable_hw } } } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 4 "optimized" { target lp64 } } } */ +/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 2 "optimized" { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "\\talcr\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\talc\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\talcgr\\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "\\talcg\\t" 1 { target lp64 } } } */ + +#include <assert.h> + +unsigned int __attribute__ ((noipa)) +uaddc (unsigned int x, unsigned int y, _Bool carry_in, _Bool *carry_out) +{ + unsigned int r; + _Bool c1 = __builtin_add_overflow (x, y, &r); + _Bool c2 = __builtin_add_overflow (r, carry_in, &r); + *carry_out = c1 | c2; + return r; +} + +void +test_int (void) +{ + _Bool c; + unsigned int r; + + r = uaddc (0xf0000000u, 0x0fffffffu, 0, &c); + assert (r == 0xffffffffu && !c); + + r = uaddc (0xf0000000u, 0x0fffffffu, 1, &c); + assert (r == 0 && c); + + r = uaddc (0xf0000001u, 0x0fffffffu, 0, &c); + assert (r == 0 && c); + + r = uaddc (0xf0000001u, 0x0fffffffu, 1, &c); + assert (r == 1 && c); +} + +unsigned int __attribute__ ((noipa)) +uaddc_mem (unsigned int *x, unsigned int *y, _Bool carry_in, _Bool *carry_out) +{ + unsigned int r; + _Bool c1 = __builtin_add_overflow (*x, *y, &r); + _Bool c2 = __builtin_add_overflow (r, carry_in, &r); + *carry_out = c1 | c2; + return r; +} + +void +test_int_mem (void) +{ + _Bool c; + unsigned int r, x, y; + + x = 0xf0000000u; + y = 0x0fffffffu; + r = uaddc_mem (&x, &y, 0, &c); + assert (r == 0xffffffffu && !c); + + x = 0xf0000000u; + y = 0x0fffffffu; + r = uaddc_mem (&x, &y, 1, &c); + assert (r == 0 && c); + + x = 0xf0000001u; + y = 0x0fffffffu; + r = uaddc_mem (&x, &y, 0, &c); + assert (r == 0 && c); + + x = 0xf0000001u; + y = 0x0fffffffu; + r = uaddc_mem (&x, &y, 1, &c); + assert (r == 1 && c); +} + +#ifdef __s390x__ +unsigned long __attribute__ ((noipa)) +uaddcl (unsigned long x, unsigned long y, _Bool carry_in, _Bool *carry_out) +{ + unsigned long r; + _Bool c1 = __builtin_add_overflow (x, y, &r); + _Bool c2 = __builtin_add_overflow (r, carry_in, &r); + *carry_out = c1 | c2; + return r; +} + +void +test_long (void) +{ + _Bool c; + unsigned long r; + + r = uaddcl (0xf000000000000000u, 0x0fffffffffffffffu, 0, &c); + assert (r == 0xffffffffffffffffu && !c); + + r = uaddcl (0xf000000000000000u, 0x0fffffffffffffffu, 1, &c); + assert (r == 0 && c); + + r = uaddcl (0xf000000000000001u, 0x0fffffffffffffffu, 0, &c); + assert (r == 0 && c); + + r = uaddcl (0xf000000000000001u, 0x0fffffffffffffffu, 1, &c); + assert (r == 1 && c); +} + +unsigned long __attribute__ ((noipa)) +uaddcl_mem (unsigned long *x, unsigned long *y, _Bool carry_in, _Bool *carry_out) +{ + unsigned long r; + _Bool c1 = __builtin_add_overflow (*x, *y, &r); + _Bool c2 = __builtin_add_overflow (r, carry_in, &r); + *carry_out = c1 | c2; + return r; +} + +void +test_long_mem (void) +{ + _Bool c; + unsigned long r, x, y; + + x = 0xf000000000000000u; + y = 0x0fffffffffffffffu; + r = uaddcl_mem (&x, &y, 0, &c); + assert (r == 0xffffffffffffffffu && !c); + + x = 0xf000000000000000u; + y = 0x0fffffffffffffffu; + r = uaddcl_mem (&x, &y, 1, &c); + assert (r == 0 && c); + + x = 0xf000000000000001u; + y = 0x0fffffffffffffffu; + r = uaddcl_mem (&x, &y, 0, &c); + assert (r == 0 && c); + + x = 0xf000000000000001u; + y = 0x0fffffffffffffffu; + r = uaddcl_mem (&x, &y, 1, &c); + assert (r == 1 && c); +} +#endif + +int +main (void) +{ + test_int (); + test_int_mem (); +#ifdef __s390x__ + test_long (); + test_long_mem (); +#endif + return 0; +} diff --git a/gcc/testsuite/gcc.target/s390/uaddc-2.c b/gcc/testsuite/gcc.target/s390/uaddc-2.c new file mode 100644 index 00000000000..da88733c835 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/uaddc-2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 6 "optimized" } } */ +/* { dg-final { scan-assembler-times "\\talcr\\t" 6 { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "\\talcr\\t" 3 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "\\talcgr\\t" 3 { target lp64 } } } */ + +#define TEST(T, V, OP) \ + unsigned T \ + uaddc_##T##_##V (unsigned T x, unsigned T y, _Bool carry_in, _Bool *carry_out) \ + { \ + unsigned T r; \ + _Bool c1 = __builtin_add_overflow (x, y, &r); \ + _Bool c2 = __builtin_add_overflow (r, carry_in, &r); \ + *carry_out = c1 OP c2; \ + return r; \ + } + +TEST(int, 1, |) +TEST(int, 2, ||) +TEST(int, 3, ^) + +TEST(long, 1, |) +TEST(long, 2, ||) +TEST(long, 3, ^) diff --git a/gcc/testsuite/gcc.target/s390/uaddc-3.c b/gcc/testsuite/gcc.target/s390/uaddc-3.c new file mode 100644 index 00000000000..3237fc208ff --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/uaddc-3.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.UADDC \\(\[^,\]+, \[^,\]+, 0\\)" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "\\.UADDC \\(\[^,\]+, \[^,\]+, _.+\\)" 1 "optimized" } } */ +/* { dg-final { scan-assembler-times "alg\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "ag\t" 2 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "al\t" 1 { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "a\t" 2 { target { ! lp64 } } } } */ + +static unsigned long +uaddc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long *carry_out) +{ + unsigned long r; + unsigned long c1 = __builtin_add_overflow (x, y, &r); + unsigned long c2 = __builtin_add_overflow (r, carry_in, &r); + *carry_out = c1 + c2; + return r; +} + +void +test (unsigned long *p, unsigned long *q) +{ + unsigned long c; + p[0] = uaddc (p[0], q[0], 0, &c); + p[1] = uaddc (p[1], q[1], c, &c); +} + diff --git a/gcc/testsuite/gcc.target/s390/usubc-1.c b/gcc/testsuite/gcc.target/s390/usubc-1.c new file mode 100644 index 00000000000..608b5ea89bf --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/usubc-1.c @@ -0,0 +1,156 @@ +/* { dg-do run { target { s390_useable_hw } } } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 4 "optimized" { target lp64 } } } */ +/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 2 "optimized" { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "\\tslbr\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\tslb\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\tslbgr\\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "\\tslbg\\t" 1 { target lp64 } } } */ + +#include <assert.h> + +unsigned int __attribute__ ((noipa)) +usubc (unsigned int x, unsigned int y, _Bool borrow_in, _Bool *borrow_out) +{ + unsigned int r; + _Bool b1 = __builtin_sub_overflow (x, y, &r); + _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r); + *borrow_out = b1 | b2; + return r; +} + +void +test_int (void) +{ + _Bool b; + unsigned int r; + + r = usubc (0xfu, 0xfu, 0, &b); + assert (r == 0 && !b); + + r = usubc (0xfu, 0xfu, 1, &b); + assert (r == 0xffffffffu && b); + + r = usubc (0xfu, 0xffu, 0, &b); + assert (r == 0xffffff10u && b); + + r = usubc (0xfu, 0xffu, 1, &b); + assert (r == 0xffffff0fu && b); +} + +unsigned int __attribute__ ((noipa)) +usubc_mem (unsigned int *x, unsigned int *y, _Bool borrow_in, _Bool *borrow_out) +{ + unsigned int r; + _Bool b1 = __builtin_sub_overflow (*x, *y, &r); + _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r); + *borrow_out = b1 | b2; + return r; +} + +void +test_int_mem (void) +{ + _Bool b; + unsigned int r, x, y; + + x = 0xfu; + y = 0xfu; + r = usubc_mem (&x, &y, 0, &b); + assert (r == 0 && !b); + + x = 0xfu; + y = 0xfu; + r = usubc_mem (&x, &y, 1, &b); + assert (r == 0xffffffffu && b); + + x = 0xfu; + y = 0xffu; + r = usubc_mem (&x, &y, 0, &b); + assert (r == 0xffffff10u && b); + + x = 0xfu; + y = 0xffu; + r = usubc_mem (&x, &y, 1, &b); + assert (r == 0xffffff0fu && b); +} + +#ifdef __s390x__ +unsigned long __attribute__ ((noipa)) +usubcl (unsigned long x, unsigned long y, _Bool borrow_in, _Bool *borrow_out) +{ + unsigned long r; + _Bool b1 = __builtin_sub_overflow (x, y, &r); + _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r); + *borrow_out = b1 | b2; + return r; +} + +void +test_long (void) +{ + _Bool b; + unsigned long r; + + r = usubcl (0xfu, 0xfu, 0, &b); + assert (r == 0 && !b); + + r = usubcl (0xfu, 0xfu, 1, &b); + assert (r == 0xffffffffffffffffu && b); + + r = usubcl (0xfu, 0xffu, 0, &b); + assert (r == 0xffffffffffffff10u && b); + + r = usubcl (0xfu, 0xffu, 1, &b); + assert (r == 0xffffffffffffff0fu && b); +} + +unsigned long __attribute__ ((noipa)) +usubcl_mem (unsigned long *x, unsigned long *y, _Bool borrow_in, _Bool *borrow_out) +{ + unsigned long r; + _Bool b1 = __builtin_sub_overflow (*x, *y, &r); + _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r); + *borrow_out = b1 | b2; + return r; +} + +void +test_long_mem (void) +{ + _Bool b; + unsigned long r, x, y; + + x = 0xfu; + y = 0xfu; + r = usubcl_mem (&x, &y, 0, &b); + assert (r == 0 && !b); + + x = 0xfu; + y = 0xfu; + r = usubcl_mem (&x, &y, 1, &b); + assert (r == 0xffffffffffffffffu && b); + + x = 0xfu; + y = 0xffu; + r = usubcl_mem (&x, &y, 0, &b); + assert (r == 0xffffffffffffff10u && b); + + x = 0xfu; + y = 0xffu; + r = usubcl_mem (&x, &y, 1, &b); + assert (r == 0xffffffffffffff0fu && b); +} +#endif + +int +main (void) +{ + test_int (); + test_int_mem (); +#ifdef __s390x__ + test_long (); + test_long_mem (); +#endif + return 0; +} diff --git a/gcc/testsuite/gcc.target/s390/usubc-2.c b/gcc/testsuite/gcc.target/s390/usubc-2.c new file mode 100644 index 00000000000..382581e4d2b --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/usubc-2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 6 "optimized" } } */ +/* { dg-final { scan-assembler-times "\\tslbr\\t" 6 { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "\\tslbr\\t" 3 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "\\tslbgr\\t" 3 { target lp64 } } } */ + +#define TEST(T, V, OP) \ + unsigned T \ + uaddc_##T##_##V (unsigned T x, unsigned T y, _Bool carry_in, _Bool *carry_out) \ + { \ + unsigned T r; \ + _Bool c1 = __builtin_sub_overflow (x, y, &r); \ + _Bool c2 = __builtin_sub_overflow (r, carry_in, &r); \ + *carry_out = c1 OP c2; \ + return r; \ + } + +TEST(int, 1, |) +TEST(int, 2, ||) +TEST(int, 3, ^) + +TEST(long, 1, |) +TEST(long, 2, ||) +TEST(long, 3, ^) diff --git a/gcc/testsuite/gcc.target/s390/usubc-3.c b/gcc/testsuite/gcc.target/s390/usubc-3.c new file mode 100644 index 00000000000..04a3e82c686 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/usubc-3.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "\\.USUBC \\(\[^,\]+, \[^,\]+, 0\\)" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "\\.USUBC \\(\[^,\]+, \[^,\]+, _.+\\)" 1 "optimized" } } */ +/* { dg-final { scan-assembler-times "slg\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "alcgr\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "slbg\t" 1 { target lp64 } } } */ +/* { dg-final { scan-assembler-times "sl\t" 1 { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "alcr\t" 1 { target { ! lp64 } } } } */ +/* { dg-final { scan-assembler-times "slb\t" 1 { target { ! lp64 } } } } */ + +static unsigned long +usubc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long *carry_out) +{ + unsigned long r; + unsigned long c1 = __builtin_sub_overflow (x, y, &r); + unsigned long c2 = __builtin_sub_overflow (r, carry_in, &r); + *carry_out = c1 + c2; + return r; +} + + +void +test (unsigned long *p, unsigned long *q) +{ + unsigned long c; + p[0] = usubc (p[0], q[0], 0, &c); + p[1] = usubc (p[1], q[1], c, &c); +} -- 2.47.0