Message ID | 20230523135007.682279-1-juzhe.zhong@rivai.ai |
---|---|
State | New |
Headers | show |
Series | [V2] RISC-V: Add RVV comparison autovectorization | expand |
> +(define_expand "vec_cmp<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VI 2 "register_operand") > + (match_operand:VI 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3]); > + DONE; > + } > +) > + > +(define_expand "vec_cmpu<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VI 2 "register_operand") > + (match_operand:VI 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3]); > + DONE; > + } > +) > + > +(define_expand "vec_cmp<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VF 2 "register_operand") > + (match_operand:VF 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp_float (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3], false); > + DONE; > + } > +) Don't you want to use your shiny new operand passing style here as with the other expanders? > + /* We have a maximum of 11 operands for RVV instruction patterns according to > + * vector.md. */ > + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, > + /*FULLY_UNMASKED_P*/ false, > + /*USE_REAL_MERGE_P*/ false, /*HAS_AVL_P*/ true, > + /*VLMAX_P*/ true, > + /*DEST_MODE*/ data_mode, /*MASK_MODE*/ mask_mode); > + e.set_policy (TAIL_ANY); > + e.emit_insn ((enum insn_code) icode, ops); > +} I don't think we need the same comment in each of these. Same for /*DEST_MODE*/ and /*MASK_MODE*/ which would be redundant if data_mode were called dest_mode. > +/* Expand an RVV comparison. */ > + > +void > +expand_vec_cmp (rtx target, rtx_code code, rtx op0, rtx op1) > +{ > + machine_mode mask_mode = GET_MODE (target); > + machine_mode data_mode = GET_MODE (op0); > + insn_code icode = get_cmp_insn_code (code, data_mode); > + > + if (code == LTGT) > + { > + rtx gt = gen_reg_rtx (mask_mode); > + rtx lt = gen_reg_rtx (mask_mode); > + expand_vec_cmp (gt, GT, op0, op1); > + expand_vec_cmp (lt, LT, op0, op1); > + icode = code_for_pred (IOR, mask_mode); > + rtx ops[3] = {target, gt, lt}; > + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); > + return; > + } Swap lt and gt here for consistency's sake. Regards Robin
Hi, Robin. >> Don't you want to use your shiny new operand passing style here as >> with the other expanders? Hmmmm, I do this just following ARM code style. You can see I do pass rtx[] for expand_vcond and pass rtx,rtx,rtx for expand_vec_cmp. Well, I just follow ARM SVE implementation (You can check aarch64-sve.md, we are the same) :) If don't like it, could give me more information then I change it for you. >> I don't think we need the same comment in each of these. Same for >> /*DEST_MODE*/ and /*MASK_MODE*/ which would be redundant if data_mode >> were called dest_mode. Ok >> Swap lt and gt here for consistency's sake. Ok. I have fixed as you suggested. Would you mind review V3 patch: https://gcc.gnu.org/pipermail/gcc-patches/2023-May/619324.html Thanks. juzhe.zhong@rivai.ai From: Robin Dapp Date: 2023-05-23 22:12 To: juzhe.zhong; gcc-patches CC: rdapp.gcc; kito.cheng; kito.cheng; palmer; palmer; jeffreyalaw; Richard Sandiford Subject: Re: [PATCH V2] RISC-V: Add RVV comparison autovectorization > +(define_expand "vec_cmp<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VI 2 "register_operand") > + (match_operand:VI 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3]); > + DONE; > + } > +) > + > +(define_expand "vec_cmpu<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VI 2 "register_operand") > + (match_operand:VI 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3]); > + DONE; > + } > +) > + > +(define_expand "vec_cmp<mode><vm>" > + [(set (match_operand:<VM> 0 "register_operand") > + (match_operator:<VM> 1 "comparison_operator" > + [(match_operand:VF 2 "register_operand") > + (match_operand:VF 3 "register_operand")]))] > + "TARGET_VECTOR" > + { > + riscv_vector::expand_vec_cmp_float (operands[0], GET_CODE (operands[1]), > + operands[2], operands[3], false); > + DONE; > + } > +) Don't you want to use your shiny new operand passing style here as with the other expanders? > + /* We have a maximum of 11 operands for RVV instruction patterns according to > + * vector.md. */ > + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, > + /*FULLY_UNMASKED_P*/ false, > + /*USE_REAL_MERGE_P*/ false, /*HAS_AVL_P*/ true, > + /*VLMAX_P*/ true, > + /*DEST_MODE*/ data_mode, /*MASK_MODE*/ mask_mode); > + e.set_policy (TAIL_ANY); > + e.emit_insn ((enum insn_code) icode, ops); > +} I don't think we need the same comment in each of these. Same for /*DEST_MODE*/ and /*MASK_MODE*/ which would be redundant if data_mode were called dest_mode. > +/* Expand an RVV comparison. */ > + > +void > +expand_vec_cmp (rtx target, rtx_code code, rtx op0, rtx op1) > +{ > + machine_mode mask_mode = GET_MODE (target); > + machine_mode data_mode = GET_MODE (op0); > + insn_code icode = get_cmp_insn_code (code, data_mode); > + > + if (code == LTGT) > + { > + rtx gt = gen_reg_rtx (mask_mode); > + rtx lt = gen_reg_rtx (mask_mode); > + expand_vec_cmp (gt, GT, op0, op1); > + expand_vec_cmp (lt, LT, op0, op1); > + icode = code_for_pred (IOR, mask_mode); > + rtx ops[3] = {target, gt, lt}; > + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); > + return; > + } Swap lt and gt here for consistency's sake. Regards Robin ail-LinkSize:2273655 QQMail-LineLen:76 QQMail-BreakType:1 QQMail-Key:cbdff912c7f03cb40444ad0dccf1f041 QQMail-MD5:6754fd07de754a129fff82b243962497 QQMail-LinkEnd ------=_Part_2195_841924464.1657529212753--0eWxlPSJjb2xvcjojMDAwMDAwIj48Zm9udCB5YWhlaT0i Ij48c3Ryb25nPlRlbDo8L3N0cm9uZz4mbmJzcDs4Ni0yOC02ODM3MzE2NiA2ODM3MzE4OCZuYnNw OzxiciAvPgo8c3Ryb25nPkZheDo8L3N0cm9uZz4mbmJzcDs4Ni0yOC02ODM3MzE2Ni04MDQmbmJz cDs8YnIgLz4KPHN0cm9uZz5BZGQ6PC9zdHJvbmc+NzE4LE5vLjEwLDEgTm9ydGgsMiBSaW5nLENo ZW5nZHUsQ2hpbmEsPC9mb250PjwvZm9udD48L2ZvbnQ+PGJyIG1pY3Jvc29mdD0iIiBzdHlsZT0i Y29sb3I6IzAwMDAwMCIgeWFoZWk9IiIgLz4KPGZvbnQgbWljcm9zb2Z0PSIiPjxmb250IHN0eWxl PSJjb2xvcjojMDAwMDAwIj48Zm9udCB5YWhlaT0iIj48c3Ryb25nPlBvc3RhbCBjb2RlOjwvc3Ry b25nPjYxMDAzMTwvZm9udD48L2ZvbnQ+PC9mb250PjxiciBtaWNyb3NvZnQ9IiIgc3R5bGU9ImNv bG9yOiMwMDAwMDAiIHlhaGVpPSIiIC8+CiZuYnNwOzxpbWcgYWx0PSIiIHNyYz0iL2VudHNvZnQv RXRBY3Rpb24uZW50Y3JtP21ldGhvZD10ZSZtYWlsSUQ9ODgwNTgzJmFzcF9jb2Q9JmNfdGFza051 bT0iIGhlaWdodD0wIHdpZHRoPTA+PC9CT0RZPjwvSFRNTD4= ------=_Part_8340_683676631.1684738404743-- ------=_Part_8339_2046897854.1684738404722 Content-Type: image/jpeg;name="1669700265737.jpg.jpeg" Content-Transfer-Encoding: base64 Content-ID: <2023052214532474264814710@entsoft.net> /9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcU FhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgo KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAG1AfQDASIA AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3 ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3 uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCXgZxj P0qVQMjGB+FMCucEdfepCpOCetYG4YGcHH5VMqrgcD8qiwe3X1xUq7uh5poQIgz90flU2xT/AAim 8kcCnop75zTbC1x0can+EflU6oB2FNVPepFWi4WFCA9h+VSIig9vypBtAp4wegoAXC+1OCr6CjHT pT0HfANAChFxwBTwi+gpUGeope+KAHKi+gqQIuOgpBj2/KnA9qLgLsXso/KlCrj7o/KnDpSgZPIF FwFjRfQflTmRfSlC4HGKeT6/ypiZFsX2p6oPSlFOwc9KGCAIvoPypwjX0H5Uo54OBSbgv3mA+tQx MeEUDoPyo2r6CkWWI8b0zTtyEfeSkITavoKAq+gP4UheMdWSm/aIl/iX8KAJNq/3R+VPCLj7o/Kq 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 cw96KKQxPOY0hc0UUBYN5ooooJP/2Q==
>>> Don't you want to use your shiny new operand passing style here as >>> with the other expanders? > Hmmmm, I do this just following ARM code style. > You can see I do pass rtx[] for expand_vcond and pass rtx,rtx,rtx for expand_vec_cmp. > Well, I just follow ARM SVE implementation (You can check aarch64-sve.md, we are the same) :) > If don't like it, could give me more information then I change it for you. It doesn't matter that much in the end. I just wondered that we just introduced a new style of passing operands to the insn_expander and then immediately not use it in the first follow up :) Nit: + e.set_policy (op_num == RVV_CMP_OP ? MASK_UNDISTURBED : MASK_ANY); This looks weird in an emit__cmp_insn. Without a comment it's unclear why anything else but a CMP_OP would ever be used here. The double meaning of the enum (that I wanted to be an instruction type rather than a "number of operands") doesn't help. But well, fixable in the future. We just need to make sure not to accumulate too many of these warts. From the expander side V3 looks clean now. The integer parts look OK to me but I haven't checked the FP side at all. Regards Robin
Ok. Let's wait for Kito's more comments. Thanks. juzhe.zhong@rivai.ai From: Robin Dapp Date: 2023-05-24 05:07 To: 钟居哲; gcc-patches CC: rdapp.gcc; kito.cheng; kito.cheng; palmer; palmer; Jeff Law; richard.sandiford Subject: Re: [PATCH V2] RISC-V: Add RVV comparison autovectorization >>> Don't you want to use your shiny new operand passing style here as >>> with the other expanders? > Hmmmm, I do this just following ARM code style. > You can see I do pass rtx[] for expand_vcond and pass rtx,rtx,rtx for expand_vec_cmp. > Well, I just follow ARM SVE implementation (You can check aarch64-sve.md, we are the same) :) > If don't like it, could give me more information then I change it for you. It doesn't matter that much in the end. I just wondered that we just introduced a new style of passing operands to the insn_expander and then immediately not use it in the first follow up :) Nit: + e.set_policy (op_num == RVV_CMP_OP ? MASK_UNDISTURBED : MASK_ANY); This looks weird in an emit__cmp_insn. Without a comment it's unclear why anything else but a CMP_OP would ever be used here. The double meaning of the enum (that I wanted to be an instruction type rather than a "number of operands") doesn't help. But well, fixable in the future. We just need to make sure not to accumulate too many of these warts. From the expander side V3 looks clean now. The integer parts look OK to me but I haven't checked the FP side at all. Regards Robin
> +void > +expand_vec_cmp (rtx target, rtx_code code, rtx mask, rtx maskoff, rtx op0, > + rtx op1) > ... > + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); > + rtx ops[RVV_CMP_OP + 2] = {target, mask, maskoff, cmp, op0, op1}; > + emit_vlmax_cmp_insn (icode, RVV_CMP_OP + 2, ops); It's too magic. > +/* This function emits cmp instruction. */ > +void > +emit_vlmax_cmp_insn (unsigned icode, int op_num, rtx *ops) > +{ > + machine_mode mode = GET_MODE (ops[0]); > + bool fully_unmasked_p = op_num == RVV_CMP_OP ? true : false; > + bool use_real_merge_p = op_num == RVV_CMP_OP ? false : true; Don't do that, plz separate break this function into two. > + /* We have a maximum of 11 operands for RVV instruction patterns according to > + * vector.md. */ > + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, > + /*FULLY_UNMASKED_P*/ fully_unmasked_p, > + /*USE_REAL_MERGE_P*/ use_real_merge_p, > + /*HAS_AVL_P*/ true, > + /*VLMAX_P*/ true, > + /*DEST_MODE*/ mode, /*MASK_MODE*/ mode); > + e.set_policy (op_num == RVV_CMP_OP ? MASK_UNDISTURBED : MASK_ANY); > + e.emit_insn ((enum insn_code) icode, ops); > +} > + > /* Expand series const vector. */ > > void > +void > +expand_vec_cmp (rtx target, rtx_code code, rtx op0, rtx op1) > +{ > + machine_mode mask_mode = GET_MODE (target); > + machine_mode data_mode = GET_MODE (op0); > + insn_code icode = get_cmp_insn_code (code, data_mode); > + > + if (code == LTGT) > + { > + rtx gt = gen_reg_rtx (mask_mode); > + rtx lt = gen_reg_rtx (mask_mode); > + expand_vec_cmp (gt, GT, op0, op1); > + expand_vec_cmp (lt, LT, op0, op1); > + icode = code_for_pred (IOR, mask_mode); > + rtx ops[3] = {target, gt, lt}; rtx ops[] = {target, gt, lt}; > + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); > + return; > + } > + > + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); > + rtx ops[RVV_CMP_OP] = {target, cmp, op0, op1}; rtx ops[] = {target, cmp, op0, op1}; > + emit_vlmax_cmp_insn (icode, RVV_CMP_OP, ops); > +} > + > + /* There is native support for the inverse comparison. */ > + code = reverse_condition_maybe_unordered (code); > + if (code == ORDERED) > + emit_move_insn (target, eq0); > + else > + expand_vec_cmp (eq0, code, eq0, eq0, op0, op1); > + > + if (can_invert_p) > + { > + emit_move_insn (target, eq0); > + return true; > + } > + insn_code icode = code_for_pred_not (mask_mode); > + rtx ops[RVV_UNOP] = {target, eq0}; > + emit_vlmax_insn (icode, RVV_UNOP, ops); rtx ops[] = {target, eq0};
Thanks a lot. Part of the comments has already been fixed in V4. But forget about V4 patch. Could you continue review V5 patch that I just send ? https://gcc.gnu.org/pipermail/gcc-patches/2023-May/619366.html with all comments from you have been fixed. Thanks. juzhe.zhong@rivai.ai From: Kito Cheng Date: 2023-05-24 11:20 To: juzhe.zhong CC: gcc-patches; kito.cheng; palmer; palmer; jeffreyalaw; rdapp.gcc; Richard Sandiford Subject: Re: [PATCH V2] RISC-V: Add RVV comparison autovectorization > +void > +expand_vec_cmp (rtx target, rtx_code code, rtx mask, rtx maskoff, rtx op0, > + rtx op1) > ... > + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); > + rtx ops[RVV_CMP_OP + 2] = {target, mask, maskoff, cmp, op0, op1}; > + emit_vlmax_cmp_insn (icode, RVV_CMP_OP + 2, ops); It's too magic. > +/* This function emits cmp instruction. */ > +void > +emit_vlmax_cmp_insn (unsigned icode, int op_num, rtx *ops) > +{ > + machine_mode mode = GET_MODE (ops[0]); > + bool fully_unmasked_p = op_num == RVV_CMP_OP ? true : false; > + bool use_real_merge_p = op_num == RVV_CMP_OP ? false : true; Don't do that, plz separate break this function into two. > + /* We have a maximum of 11 operands for RVV instruction patterns according to > + * vector.md. */ > + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, > + /*FULLY_UNMASKED_P*/ fully_unmasked_p, > + /*USE_REAL_MERGE_P*/ use_real_merge_p, > + /*HAS_AVL_P*/ true, > + /*VLMAX_P*/ true, > + /*DEST_MODE*/ mode, /*MASK_MODE*/ mode); > + e.set_policy (op_num == RVV_CMP_OP ? MASK_UNDISTURBED : MASK_ANY); > + e.emit_insn ((enum insn_code) icode, ops); > +} > + > /* Expand series const vector. */ > > void > +void > +expand_vec_cmp (rtx target, rtx_code code, rtx op0, rtx op1) > +{ > + machine_mode mask_mode = GET_MODE (target); > + machine_mode data_mode = GET_MODE (op0); > + insn_code icode = get_cmp_insn_code (code, data_mode); > + > + if (code == LTGT) > + { > + rtx gt = gen_reg_rtx (mask_mode); > + rtx lt = gen_reg_rtx (mask_mode); > + expand_vec_cmp (gt, GT, op0, op1); > + expand_vec_cmp (lt, LT, op0, op1); > + icode = code_for_pred (IOR, mask_mode); > + rtx ops[3] = {target, gt, lt}; rtx ops[] = {target, gt, lt}; > + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); > + return; > + } > + > + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); > + rtx ops[RVV_CMP_OP] = {target, cmp, op0, op1}; rtx ops[] = {target, cmp, op0, op1}; > + emit_vlmax_cmp_insn (icode, RVV_CMP_OP, ops); > +} > + > + /* There is native support for the inverse comparison. */ > + code = reverse_condition_maybe_unordered (code); > + if (code == ORDERED) > + emit_move_insn (target, eq0); > + else > + expand_vec_cmp (eq0, code, eq0, eq0, op0, op1); > + > + if (can_invert_p) > + { > + emit_move_insn (target, eq0); > + return true; > + } > + insn_code icode = code_for_pred_not (mask_mode); > + rtx ops[RVV_UNOP] = {target, eq0}; > + emit_vlmax_insn (icode, RVV_UNOP, ops); rtx ops[] = {target, eq0};
diff --git a/gcc/config/riscv/autovec.md b/gcc/config/riscv/autovec.md index 04b4459222a..e0258e8b798 100644 --- a/gcc/config/riscv/autovec.md +++ b/gcc/config/riscv/autovec.md @@ -162,3 +162,115 @@ riscv_vector::RVV_BINOP, operands); DONE; }) + +;; ========================================================================= +;; == Comparisons and selects +;; ========================================================================= + +;; ------------------------------------------------------------------------- +;; ---- [INT,FP] Select based on masks +;; ------------------------------------------------------------------------- +;; Includes merging patterns for: +;; - vmerge.vv +;; - vmerge.vx +;; - vfmerge.vf +;; ------------------------------------------------------------------------- + +(define_expand "@vcond_mask_<mode><vm>" + [(match_operand:V 0 "register_operand") + (match_operand:<VM> 3 "register_operand") + (match_operand:V 1 "nonmemory_operand") + (match_operand:V 2 "register_operand")] + "TARGET_VECTOR" + { + /* The order of vcond_mask is opposite to pred_merge. */ + std::swap (operands[1], operands[2]); + riscv_vector::emit_vlmax_merge_insn (code_for_pred_merge (<MODE>mode), + riscv_vector::RVV_MERGE_OP, operands); + DONE; + } +) + +;; ------------------------------------------------------------------------- +;; ---- [INT,FP] Comparisons +;; ------------------------------------------------------------------------- +;; Includes: +;; - vms<eq/ne/ltu/lt/leu/le/gtu/gt>.<vv/vx/vi> +;; ------------------------------------------------------------------------- + +(define_expand "vec_cmp<mode><vm>" + [(set (match_operand:<VM> 0 "register_operand") + (match_operator:<VM> 1 "comparison_operator" + [(match_operand:VI 2 "register_operand") + (match_operand:VI 3 "register_operand")]))] + "TARGET_VECTOR" + { + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), + operands[2], operands[3]); + DONE; + } +) + +(define_expand "vec_cmpu<mode><vm>" + [(set (match_operand:<VM> 0 "register_operand") + (match_operator:<VM> 1 "comparison_operator" + [(match_operand:VI 2 "register_operand") + (match_operand:VI 3 "register_operand")]))] + "TARGET_VECTOR" + { + riscv_vector::expand_vec_cmp (operands[0], GET_CODE (operands[1]), + operands[2], operands[3]); + DONE; + } +) + +(define_expand "vec_cmp<mode><vm>" + [(set (match_operand:<VM> 0 "register_operand") + (match_operator:<VM> 1 "comparison_operator" + [(match_operand:VF 2 "register_operand") + (match_operand:VF 3 "register_operand")]))] + "TARGET_VECTOR" + { + riscv_vector::expand_vec_cmp_float (operands[0], GET_CODE (operands[1]), + operands[2], operands[3], false); + DONE; + } +) + +;; ------------------------------------------------------------------------- +;; ---- [INT,FP] Compare and select +;; ------------------------------------------------------------------------- +;; The patterns in this section are synthetic. +;; ------------------------------------------------------------------------- + +(define_expand "vcond<V:mode><VI:mode>" + [(set (match_operand:V 0 "register_operand") + (if_then_else:V + (match_operator 3 "comparison_operator" + [(match_operand:VI 4 "register_operand") + (match_operand:VI 5 "register_operand")]) + (match_operand:V 1 "register_operand") + (match_operand:V 2 "register_operand")))] + "TARGET_VECTOR && known_eq (GET_MODE_NUNITS (<V:MODE>mode), + GET_MODE_NUNITS (<VI:MODE>mode))" + { + riscv_vector::expand_vcond (operands); + DONE; + } +) + +(define_expand "vcondu<V:mode><VI:mode>" + [(set (match_operand:V 0 "register_operand") + (if_then_else:V + (match_operator 3 "comparison_operator" + [(match_operand:VI 4 "register_operand") + (match_operand:VI 5 "register_operand")]) + (match_operand:V 1 "register_operand") + (match_operand:V 2 "register_operand")))] + "TARGET_VECTOR && known_eq (GET_MODE_NUNITS (<V:MODE>mode), + GET_MODE_NUNITS (<VI:MODE>mode))" + { + riscv_vector::expand_vcond (operands); + DONE; + } +) diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 0ae4656befb..58e55c234cb 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -137,6 +137,8 @@ enum insn_type RVV_MISC_OP = 1, RVV_UNOP = 2, RVV_BINOP = 3, + RVV_MERGE_OP = 4, + RVV_CMP_OP = 4, }; enum vlmul_type { @@ -174,6 +176,8 @@ void emit_vlmax_vsetvl (machine_mode, rtx); void emit_hard_vlmax_vsetvl (machine_mode, rtx); void emit_vlmax_insn (unsigned, int, rtx *); void emit_nonvlmax_insn (unsigned, int, rtx *); +void emit_vlmax_merge_insn (unsigned, int, rtx *); +void emit_vlmax_cmp_insn (unsigned, int, rtx *); enum vlmul_type get_vlmul (machine_mode); unsigned int get_ratio (machine_mode); unsigned int get_nf (machine_mode); @@ -204,6 +208,8 @@ bool simm5_p (rtx); bool neg_simm5_p (rtx); #ifdef RTX_CODE bool has_vi_variant_p (rtx_code, rtx); +void expand_vec_cmp (rtx, rtx_code, rtx, rtx); +bool expand_vec_cmp_float (rtx, rtx_code, rtx, rtx, bool); #endif bool sew64_scalar_helper (rtx *, rtx *, rtx, machine_mode, bool, void (*)(rtx *, rtx)); @@ -226,6 +232,7 @@ machine_mode preferred_simd_mode (scalar_mode); opt_machine_mode get_mask_mode (machine_mode); void expand_vec_series (rtx, rtx, rtx); void expand_vec_init (rtx, rtx); +void expand_vcond (rtx *); /* Rounding mode bitfield for fixed point VXRM. */ enum vxrm_field_enum { diff --git a/gcc/config/riscv/riscv-v.cc b/gcc/config/riscv/riscv-v.cc index 478a052a779..5e77554c635 100644 --- a/gcc/config/riscv/riscv-v.cc +++ b/gcc/config/riscv/riscv-v.cc @@ -169,7 +169,7 @@ public: if (m_needs_avl_p) { - rtx len = ops[m_op_num]; + rtx len; if (m_vlmax_p) { if (const_vlmax_p (m_dest_mode)) @@ -185,6 +185,16 @@ public: len = gen_reg_rtx (Pmode); emit_vlmax_vsetvl (m_dest_mode, len); } + else + { + gcc_assert (ops[m_op_num]); + len = ops[m_op_num]; + } + } + else + { + gcc_assert (ops[m_op_num]); + len = ops[m_op_num]; } add_input_operand (len, Pmode); } @@ -374,6 +384,42 @@ emit_nonvlmax_insn (unsigned icode, int op_num, rtx *ops) e.emit_insn ((enum insn_code) icode, ops); } +/* This function emits merge instruction. */ +void +emit_vlmax_merge_insn (unsigned icode, int op_num, rtx *ops) +{ + machine_mode data_mode = GET_MODE (ops[0]); + machine_mode mask_mode = get_mask_mode (data_mode).require (); + /* We have a maximum of 11 operands for RVV instruction patterns according to + * vector.md. */ + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, + /*FULLY_UNMASKED_P*/ false, + /*USE_REAL_MERGE_P*/ false, /*HAS_AVL_P*/ true, + /*VLMAX_P*/ true, + /*DEST_MODE*/ data_mode, /*MASK_MODE*/ mask_mode); + e.set_policy (TAIL_ANY); + e.emit_insn ((enum insn_code) icode, ops); +} + +/* This function emits cmp instruction. */ +void +emit_vlmax_cmp_insn (unsigned icode, int op_num, rtx *ops) +{ + machine_mode mode = GET_MODE (ops[0]); + bool fully_unmasked_p = op_num == RVV_CMP_OP ? true : false; + bool use_real_merge_p = op_num == RVV_CMP_OP ? false : true; + /* We have a maximum of 11 operands for RVV instruction patterns according to + * vector.md. */ + insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true, + /*FULLY_UNMASKED_P*/ fully_unmasked_p, + /*USE_REAL_MERGE_P*/ use_real_merge_p, + /*HAS_AVL_P*/ true, + /*VLMAX_P*/ true, + /*DEST_MODE*/ mode, /*MASK_MODE*/ mode); + e.set_policy (op_num == RVV_CMP_OP ? MASK_UNDISTURBED : MASK_ANY); + e.emit_insn ((enum insn_code) icode, ops); +} + /* Expand series const vector. */ void @@ -1318,4 +1364,214 @@ expand_vec_init (rtx target, rtx vals) expand_vector_init_insert_elems (target, v, nelts); } +/* Get insn code for corresponding comparison. */ + +static insn_code +get_cmp_insn_code (rtx_code code, machine_mode mode) +{ + insn_code icode; + switch (code) + { + case EQ: + case NE: + case LE: + case LEU: + case GT: + case GTU: + case LTGT: + icode = code_for_pred_cmp (mode); + break; + case LT: + case LTU: + case GE: + case GEU: + if (FLOAT_MODE_P (mode)) + icode = code_for_pred_cmp (mode); + else + icode = code_for_pred_ltge (mode); + break; + default: + gcc_unreachable (); + } + return icode; +} + +/* Expand an RVV comparison. */ + +void +expand_vec_cmp (rtx target, rtx_code code, rtx op0, rtx op1) +{ + machine_mode mask_mode = GET_MODE (target); + machine_mode data_mode = GET_MODE (op0); + insn_code icode = get_cmp_insn_code (code, data_mode); + + if (code == LTGT) + { + rtx gt = gen_reg_rtx (mask_mode); + rtx lt = gen_reg_rtx (mask_mode); + expand_vec_cmp (gt, GT, op0, op1); + expand_vec_cmp (lt, LT, op0, op1); + icode = code_for_pred (IOR, mask_mode); + rtx ops[3] = {target, gt, lt}; + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); + return; + } + + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); + rtx ops[RVV_CMP_OP] = {target, cmp, op0, op1}; + emit_vlmax_cmp_insn (icode, RVV_CMP_OP, ops); +} + +void +expand_vec_cmp (rtx target, rtx_code code, rtx mask, rtx maskoff, rtx op0, + rtx op1) +{ + machine_mode mask_mode = GET_MODE (target); + machine_mode data_mode = GET_MODE (op0); + insn_code icode = get_cmp_insn_code (code, data_mode); + + if (code == LTGT) + { + rtx gt = gen_reg_rtx (mask_mode); + rtx lt = gen_reg_rtx (mask_mode); + expand_vec_cmp (gt, GT, mask, maskoff, op0, op1); + expand_vec_cmp (lt, LT, mask, maskoff, op0, op1); + icode = code_for_pred (IOR, mask_mode); + rtx ops[RVV_BINOP] = {target, gt, lt}; + emit_vlmax_insn (icode, RVV_BINOP, ops); + return; + } + + rtx cmp = gen_rtx_fmt_ee (code, mask_mode, op0, op1); + rtx ops[RVV_CMP_OP + 2] = {target, mask, maskoff, cmp, op0, op1}; + emit_vlmax_cmp_insn (icode, RVV_CMP_OP + 2, ops); +} + +/* Expand an RVV floating-point comparison: + + If CAN_INVERT_P is true, the caller can also handle inverted results; + return true if the result is in fact inverted. */ + +bool +expand_vec_cmp_float (rtx target, rtx_code code, rtx op0, rtx op1, + bool can_invert_p) +{ + machine_mode mask_mode = GET_MODE (target); + machine_mode data_mode = GET_MODE (op0); + + /* If can_invert_p = true: + It suffices to implement a u>= b as !(a < b) but with the NaNs masked off: + + vmfeq.vv v0, va, va + vmfeq.vv v1, vb, vb + vmand.mm v0, v0, v1 + vmflt.vv v0, va, vb, v0.t + vmnot.m v0, v0 + + And, if !HONOR_SNANS, then you can remove the vmand.mm by masking the + second vmfeq.vv: + + vmfeq.vv v0, va, va + vmfeq.vv v0, vb, vb, v0.t + vmflt.vv v0, va, vb, v0.t + vmnot.m v0, v0 + + If can_invert_p = false: + + # Example of implementing isgreater() + vmfeq.vv v0, va, va # Only set where A is not NaN. + vmfeq.vv v1, vb, vb # Only set where B is not NaN. + vmand.mm v0, v0, v1 # Only set where A and B are ordered, + vmfgt.vv v0, va, vb, v0.t # so only set flags on ordered values. + */ + + rtx eq0 = gen_reg_rtx (mask_mode); + rtx eq1 = gen_reg_rtx (mask_mode); + switch (code) + { + case EQ: + case NE: + case LT: + case LE: + case GT: + case GE: + case LTGT: + /* There is native support for the comparison. */ + expand_vec_cmp (target, code, op0, op1); + return false; + case UNEQ: + case ORDERED: + case UNORDERED: + case UNLT: + case UNLE: + case UNGT: + case UNGE: + /* vmfeq.vv v0, va, va */ + expand_vec_cmp (eq0, EQ, op0, op0); + if (HONOR_SNANS (data_mode)) + { + /* + vmfeq.vv v1, vb, vb + vmand.mm v0, v0, v1 + */ + expand_vec_cmp (eq1, EQ, op1, op1); + insn_code icode = code_for_pred (AND, mask_mode); + rtx ops[3] = {eq0, eq0, eq1}; + emit_vlmax_insn (icode, riscv_vector::RVV_BINOP, ops); + } + else + { + /* vmfeq.vv v0, vb, vb, v0.t */ + expand_vec_cmp (eq0, EQ, eq0, eq0, op1, op1); + } + break; + default: + gcc_unreachable (); + } + + if (code == ORDERED) + { + emit_move_insn (target, eq0); + return false; + } + + /* There is native support for the inverse comparison. */ + code = reverse_condition_maybe_unordered (code); + if (code == ORDERED) + emit_move_insn (target, eq0); + else + expand_vec_cmp (eq0, code, eq0, eq0, op0, op1); + + if (can_invert_p) + { + emit_move_insn (target, eq0); + return true; + } + insn_code icode = code_for_pred_not (mask_mode); + rtx ops[RVV_UNOP] = {target, eq0}; + emit_vlmax_insn (icode, RVV_UNOP, ops); + return false; +} + +/* Expand an RVV vcond pattern with operands OPS. DATA_MODE is the mode + of the data being merged and CMP_MODE is the mode of the values being + compared. */ + +void +expand_vcond (rtx *ops) +{ + machine_mode cmp_mode = GET_MODE (ops[4]); + machine_mode data_mode = GET_MODE (ops[1]); + machine_mode mask_mode = get_mask_mode (cmp_mode).require (); + rtx mask = gen_reg_rtx (mask_mode); + if (FLOAT_MODE_P (cmp_mode)) + { + if (expand_vec_cmp_float (mask, GET_CODE (ops[3]), ops[4], ops[5], true)) + std::swap (ops[1], ops[2]); + } + else + expand_vec_cmp (mask, GET_CODE (ops[3]), ops[4], ops[5]); + emit_insn (gen_vcond_mask (data_mode, data_mode, ops[0], ops[1], ops[2], mask)); +} + } // namespace riscv_vector diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-1.c new file mode 100644 index 00000000000..c882654cb49 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-1.c @@ -0,0 +1,157 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */ + +#include <stdint-gcc.h> + +#define DEF_VCOND_VAR(DATA_TYPE, CMP_TYPE, COND, SUFFIX) \ + void __attribute__ ((noinline, noclone)) \ + vcond_var_##CMP_TYPE##_##SUFFIX (DATA_TYPE *__restrict__ r, \ + DATA_TYPE *__restrict__ x, \ + DATA_TYPE *__restrict__ y, \ + CMP_TYPE *__restrict__ a, \ + CMP_TYPE *__restrict__ b, \ + int n) \ + { \ + for (int i = 0; i < n; i++) \ + { \ + DATA_TYPE xval = x[i], yval = y[i]; \ + CMP_TYPE aval = a[i], bval = b[i]; \ + r[i] = aval COND bval ? xval : yval; \ + } \ + } + +#define DEF_VCOND_IMM(DATA_TYPE, CMP_TYPE, COND, IMM, SUFFIX) \ + void __attribute__ ((noinline, noclone)) \ + vcond_imm_##CMP_TYPE##_##SUFFIX (DATA_TYPE *__restrict__ r, \ + DATA_TYPE *__restrict__ x, \ + DATA_TYPE *__restrict__ y, \ + CMP_TYPE *__restrict__ a, \ + int n) \ + { \ + for (int i = 0; i < n; i++) \ + { \ + DATA_TYPE xval = x[i], yval = y[i]; \ + CMP_TYPE aval = a[i]; \ + r[i] = aval COND (CMP_TYPE) IMM ? xval : yval; \ + } \ + } + +#define TEST_COND_VAR_SIGNED_ALL(T, COND, SUFFIX) \ + T (int8_t, int8_t, COND, SUFFIX) \ + T (int16_t, int16_t, COND, SUFFIX) \ + T (int32_t, int32_t, COND, SUFFIX) \ + T (int64_t, int64_t, COND, SUFFIX) \ + T (float, int32_t, COND, SUFFIX##_float) \ + T (double, int64_t, COND, SUFFIX##_double) + +#define TEST_COND_VAR_UNSIGNED_ALL(T, COND, SUFFIX) \ + T (uint8_t, uint8_t, COND, SUFFIX) \ + T (uint16_t, uint16_t, COND, SUFFIX) \ + T (uint32_t, uint32_t, COND, SUFFIX) \ + T (uint64_t, uint64_t, COND, SUFFIX) \ + T (float, uint32_t, COND, SUFFIX##_float) \ + T (double, uint64_t, COND, SUFFIX##_double) + +#define TEST_COND_VAR_ALL(T, COND, SUFFIX) \ + TEST_COND_VAR_SIGNED_ALL (T, COND, SUFFIX) \ + TEST_COND_VAR_UNSIGNED_ALL (T, COND, SUFFIX) + +#define TEST_VAR_ALL(T) \ + TEST_COND_VAR_ALL (T, >, _gt) \ + TEST_COND_VAR_ALL (T, <, _lt) \ + TEST_COND_VAR_ALL (T, >=, _ge) \ + TEST_COND_VAR_ALL (T, <=, _le) \ + TEST_COND_VAR_ALL (T, ==, _eq) \ + TEST_COND_VAR_ALL (T, !=, _ne) + +#define TEST_COND_IMM_SIGNED_ALL(T, COND, IMM, SUFFIX) \ + T (int8_t, int8_t, COND, IMM, SUFFIX) \ + T (int16_t, int16_t, COND, IMM, SUFFIX) \ + T (int32_t, int32_t, COND, IMM, SUFFIX) \ + T (int64_t, int64_t, COND, IMM, SUFFIX) \ + T (float, int32_t, COND, IMM, SUFFIX##_float) \ + T (double, int64_t, COND, IMM, SUFFIX##_double) + +#define TEST_COND_IMM_UNSIGNED_ALL(T, COND, IMM, SUFFIX) \ + T (uint8_t, uint8_t, COND, IMM, SUFFIX) \ + T (uint16_t, uint16_t, COND, IMM, SUFFIX) \ + T (uint32_t, uint32_t, COND, IMM, SUFFIX) \ + T (uint64_t, uint64_t, COND, IMM, SUFFIX) \ + T (float, uint32_t, COND, IMM, SUFFIX##_float) \ + T (double, uint64_t, COND, IMM, SUFFIX##_double) + +#define TEST_COND_IMM_ALL(T, COND, IMM, SUFFIX) \ + TEST_COND_IMM_SIGNED_ALL (T, COND, IMM, SUFFIX) \ + TEST_COND_IMM_UNSIGNED_ALL (T, COND, IMM, SUFFIX) + +#define TEST_IMM_ALL(T) \ + /* Expect immediates to make it into the encoding. */ \ + TEST_COND_IMM_ALL (T, >, 5, _gt) \ + TEST_COND_IMM_ALL (T, <, 5, _lt) \ + TEST_COND_IMM_ALL (T, >=, 5, _ge) \ + TEST_COND_IMM_ALL (T, <=, 5, _le) \ + TEST_COND_IMM_ALL (T, ==, 5, _eq) \ + TEST_COND_IMM_ALL (T, !=, 5, _ne) \ + \ + TEST_COND_IMM_SIGNED_ALL (T, >, 15, _gt2) \ + TEST_COND_IMM_SIGNED_ALL (T, <, 15, _lt2) \ + TEST_COND_IMM_SIGNED_ALL (T, >=, 15, _ge2) \ + TEST_COND_IMM_SIGNED_ALL (T, <=, 15, _le2) \ + TEST_COND_IMM_ALL (T, ==, 15, _eq2) \ + TEST_COND_IMM_ALL (T, !=, 15, _ne2) \ + \ + TEST_COND_IMM_SIGNED_ALL (T, >, 16, _gt3) \ + TEST_COND_IMM_SIGNED_ALL (T, <, 16, _lt3) \ + TEST_COND_IMM_SIGNED_ALL (T, >=, 16, _ge3) \ + TEST_COND_IMM_SIGNED_ALL (T, <=, 16, _le3) \ + TEST_COND_IMM_ALL (T, ==, 16, _eq3) \ + TEST_COND_IMM_ALL (T, !=, 16, _ne3) \ + \ + TEST_COND_IMM_SIGNED_ALL (T, >, -16, _gt4) \ + TEST_COND_IMM_SIGNED_ALL (T, <, -16, _lt4) \ + TEST_COND_IMM_SIGNED_ALL (T, >=, -16, _ge4) \ + TEST_COND_IMM_SIGNED_ALL (T, <=, -16, _le4) \ + TEST_COND_IMM_ALL (T, ==, -16, _eq4) \ + TEST_COND_IMM_ALL (T, !=, -16, _ne4) \ + \ + TEST_COND_IMM_SIGNED_ALL (T, >, -17, _gt5) \ + TEST_COND_IMM_SIGNED_ALL (T, <, -17, _lt5) \ + TEST_COND_IMM_SIGNED_ALL (T, >=, -17, _ge5) \ + TEST_COND_IMM_SIGNED_ALL (T, <=, -17, _le5) \ + TEST_COND_IMM_ALL (T, ==, -17, _eq5) \ + TEST_COND_IMM_ALL (T, !=, -17, _ne5) \ + \ + TEST_COND_IMM_UNSIGNED_ALL (T, >, 0, _gt6) \ + /* Testing if an unsigned value >= 0 or < 0 is pointless as it will \ + get folded away by the compiler. */ \ + TEST_COND_IMM_UNSIGNED_ALL (T, <=, 0, _le6) \ + \ + TEST_COND_IMM_UNSIGNED_ALL (T, >, 127, _gt7) \ + TEST_COND_IMM_UNSIGNED_ALL (T, <, 127, _lt7) \ + TEST_COND_IMM_UNSIGNED_ALL (T, >=, 127, _ge7) \ + TEST_COND_IMM_UNSIGNED_ALL (T, <=, 127, _le7) \ + \ + /* Expect immediates to NOT make it into the encoding, and instead be \ + forced into a register. */ \ + TEST_COND_IMM_UNSIGNED_ALL (T, >, 128, _gt8) \ + TEST_COND_IMM_UNSIGNED_ALL (T, <, 128, _lt8) \ + TEST_COND_IMM_UNSIGNED_ALL (T, >=, 128, _ge8) \ + TEST_COND_IMM_UNSIGNED_ALL (T, <=, 128, _le8) + +TEST_VAR_ALL (DEF_VCOND_VAR) +TEST_IMM_ALL (DEF_VCOND_IMM) + +/* { dg-final { scan-assembler-times {\tvmseq\.vi} 42 } } */ +/* { dg-final { scan-assembler-times {\tvmsne\.vi} 42 } } */ +/* { dg-final { scan-assembler-times {\tvmsgt\.vi} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmsgtu\.vi} 12 } } */ +/* { dg-final { scan-assembler-times {\tvmslt\.vi} 8 } } */ +/* { dg-final { scan-assembler-times {\tvmsge\.vi} 8 } } */ +/* { dg-final { scan-assembler-times {\tvmsle\.vi} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmsleu\.vi} 12 } } */ +/* { dg-final { scan-assembler-times {\tvmseq} 78 } } */ +/* { dg-final { scan-assembler-times {\tvmsne} 78 } } */ +/* { dg-final { scan-assembler-times {\tvmsgt} 82 } } */ +/* { dg-final { scan-assembler-times {\tvmslt} 38 } } */ +/* { dg-final { scan-assembler-times {\tvmsge} 38 } } */ +/* { dg-final { scan-assembler-times {\tvmsle} 82 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-2.c new file mode 100644 index 00000000000..738f978c5a1 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-2.c @@ -0,0 +1,75 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */ + +#include <stdint-gcc.h> + +#define eq(A, B) ((A) == (B)) +#define ne(A, B) ((A) != (B)) +#define olt(A, B) ((A) < (B)) +#define ole(A, B) ((A) <= (B)) +#define oge(A, B) ((A) >= (B)) +#define ogt(A, B) ((A) > (B)) +#define ordered(A, B) (!__builtin_isunordered (A, B)) +#define unordered(A, B) (__builtin_isunordered (A, B)) +#define ueq(A, B) (!__builtin_islessgreater (A, B)) +#define ult(A, B) (__builtin_isless (A, B)) +#define ule(A, B) (__builtin_islessequal (A, B)) +#define uge(A, B) (__builtin_isgreaterequal (A, B)) +#define ugt(A, B) (__builtin_isgreater (A, B)) +#define nueq(A, B) (__builtin_islessgreater (A, B)) +#define nult(A, B) (!__builtin_isless (A, B)) +#define nule(A, B) (!__builtin_islessequal (A, B)) +#define nuge(A, B) (!__builtin_isgreaterequal (A, B)) +#define nugt(A, B) (!__builtin_isgreater (A, B)) + +#define TEST_LOOP(TYPE1, TYPE2, CMP) \ + void __attribute__ ((noinline, noclone)) \ + test_##TYPE1##_##TYPE2##_##CMP##_var (TYPE1 *restrict dest, \ + TYPE1 *restrict src, \ + TYPE1 fallback, \ + TYPE2 *restrict a, \ + TYPE2 *restrict b, \ + int count) \ + { \ + for (int i = 0; i < count; ++i) \ + {\ + TYPE2 aval = a[i]; \ + TYPE2 bval = b[i]; \ + TYPE1 srcval = src[i]; \ + dest[i] = CMP (aval, bval) ? srcval : fallback; \ + }\ + } + +#define TEST_CMP(CMP) \ + TEST_LOOP (int32_t, float, CMP) \ + TEST_LOOP (uint32_t, float, CMP) \ + TEST_LOOP (float, float, CMP) \ + TEST_LOOP (int64_t, double, CMP) \ + TEST_LOOP (uint64_t, double, CMP) \ + TEST_LOOP (double, double, CMP) + +TEST_CMP (eq) +TEST_CMP (ne) +TEST_CMP (olt) +TEST_CMP (ole) +TEST_CMP (oge) +TEST_CMP (ogt) +TEST_CMP (ordered) +TEST_CMP (unordered) +TEST_CMP (ueq) +TEST_CMP (ult) +TEST_CMP (ule) +TEST_CMP (uge) +TEST_CMP (ugt) +TEST_CMP (nueq) +TEST_CMP (nult) +TEST_CMP (nule) +TEST_CMP (nuge) +TEST_CMP (nugt) + +/* { dg-final { scan-assembler-times {\tvmfeq} 150 } } */ +/* { dg-final { scan-assembler-times {\tvmfne} 6 } } */ +/* { dg-final { scan-assembler-times {\tvmfgt} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmflt} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmfge} 18 } } */ +/* { dg-final { scan-assembler-times {\tvmfle} 18 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-3.c new file mode 100644 index 00000000000..53384829e64 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-trapping-math" } */ + +/* The difference here is that nueq can use LTGT. */ + +#include "vcond-2.c" + +/* { dg-final { scan-assembler-times {\tvmfeq} 90 } } */ +/* { dg-final { scan-assembler-times {\tvmfne} 6 } } */ +/* { dg-final { scan-assembler-times {\tvmfgt} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmflt} 30 } } */ +/* { dg-final { scan-assembler-times {\tvmfge} 18 } } */ +/* { dg-final { scan-assembler-times {\tvmfle} 18 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-1.c new file mode 100644 index 00000000000..a84d22d2a73 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-1.c @@ -0,0 +1,49 @@ +/* { dg-do run { target { riscv_vector } } } */ +/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */ + +#include "vcond-1.c" + +#define N 97 + +#define TEST_VCOND_VAR(DATA_TYPE, CMP_TYPE, COND, SUFFIX) \ +{ \ + DATA_TYPE x[N], y[N], r[N]; \ + CMP_TYPE a[N], b[N]; \ + for (int i = 0; i < N; ++i) \ + { \ + x[i] = i; \ + y[i] = (i & 1) + 5; \ + a[i] = i - N / 3; \ + b[i] = N - N / 3 - i; \ + asm volatile ("" ::: "memory"); \ + } \ + vcond_var_##CMP_TYPE##_##SUFFIX (r, x, y, a, b, N); \ + for (int i = 0; i < N; ++i) \ + if (r[i] != (a[i] COND b[i] ? x[i] : y[i])) \ + __builtin_abort (); \ +} + +#define TEST_VCOND_IMM(DATA_TYPE, CMP_TYPE, COND, IMM, SUFFIX) \ +{ \ + DATA_TYPE x[N], y[N], r[N]; \ + CMP_TYPE a[N]; \ + for (int i = 0; i < N; ++i) \ + { \ + x[i] = i; \ + y[i] = (i & 1) + 5; \ + a[i] = IMM - N / 3 + i; \ + asm volatile ("" ::: "memory"); \ + } \ + vcond_imm_##CMP_TYPE##_##SUFFIX (r, x, y, a, N); \ + for (int i = 0; i < N; ++i) \ + if (r[i] != (a[i] COND (CMP_TYPE) IMM ? x[i] : y[i])) \ + __builtin_abort (); \ +} + +int __attribute__ ((optimize (1))) +main (int argc, char **argv) +{ + TEST_VAR_ALL (TEST_VCOND_VAR) + TEST_IMM_ALL (TEST_VCOND_IMM) + return 0; +} diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-2.c new file mode 100644 index 00000000000..56fd39f4691 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-2.c @@ -0,0 +1,76 @@ +/* { dg-do run { target { riscv_vector } } } */ +/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */ +/* { dg-require-effective-target fenv_exceptions } */ + +#include "vcond-2.c" + +#ifndef TEST_EXCEPTIONS +#define TEST_EXCEPTIONS 1 +#endif + +#include <fenv.h> + +#define N 401 + +#define RUN_LOOP(TYPE1, TYPE2, CMP, EXPECT_INVALID) \ + { \ + TYPE1 dest[N], src[N]; \ + TYPE2 a[N], b[N]; \ + for (int i = 0; i < N; ++i) \ + { \ + src[i] = i * i; \ + if (i % 5 == 0) \ + a[i] = 0; \ + else if (i % 3) \ + a[i] = i * 0.1; \ + else \ + a[i] = i; \ + if (i % 7 == 0) \ + b[i] = __builtin_nan (""); \ + else if (i % 6) \ + b[i] = i * 0.1; \ + else \ + b[i] = i; \ + asm volatile ("" ::: "memory"); \ + } \ + feclearexcept (FE_ALL_EXCEPT); \ + test_##TYPE1##_##TYPE2##_##CMP##_var (dest, src, 11, a, b, N); \ + if (TEST_EXCEPTIONS \ + && !fetestexcept (FE_INVALID) != !(EXPECT_INVALID)) \ + __builtin_abort (); \ + for (int i = 0; i < N; ++i) \ + if (dest[i] != (CMP (a[i], b[i]) ? src[i] : 11)) \ + __builtin_abort (); \ + } + +#define RUN_CMP(CMP, EXPECT_INVALID) \ + RUN_LOOP (int32_t, float, CMP, EXPECT_INVALID) \ + RUN_LOOP (uint32_t, float, CMP, EXPECT_INVALID) \ + RUN_LOOP (float, float, CMP, EXPECT_INVALID) \ + RUN_LOOP (int64_t, double, CMP, EXPECT_INVALID) \ + RUN_LOOP (uint64_t, double, CMP, EXPECT_INVALID) \ + RUN_LOOP (double, double, CMP, EXPECT_INVALID) + +int __attribute__ ((optimize (1))) +main (void) +{ + RUN_CMP (eq, 0) + RUN_CMP (ne, 0) + RUN_CMP (olt, 1) + RUN_CMP (ole, 1) + RUN_CMP (oge, 1) + RUN_CMP (ogt, 1) + RUN_CMP (ordered, 0) + RUN_CMP (unordered, 0) + RUN_CMP (ueq, 0) + RUN_CMP (ult, 0) + RUN_CMP (ule, 0) + RUN_CMP (uge, 0) + RUN_CMP (ugt, 0) + RUN_CMP (nueq, 0) + RUN_CMP (nult, 0) + RUN_CMP (nule, 0) + RUN_CMP (nuge, 0) + RUN_CMP (nugt, 0) + return 0; +} diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-3.c new file mode 100644 index 00000000000..e50d561bd98 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-3.c @@ -0,0 +1,6 @@ +/* { dg-do run { target { riscv_vector } } } */ +/* { dg-additional-options "--param=riscv-autovec-preference=scalable -fno-trapping-math" } */ +/* { dg-require-effective-target fenv_exceptions } */ + +#define TEST_EXCEPTIONS 0 +#include "vcond_run-2.c" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp b/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp index bc99cc0c3cf..9809a421fc8 100644 --- a/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp +++ b/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp @@ -63,6 +63,8 @@ foreach op $AUTOVEC_TEST_OPTS { "" "$op" dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/autovec/binop/*.\[cS\]]] \ "" "$op" + dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/autovec/cmp/*.\[cS\]]] \ + "" "$op" } # VLS-VLMAX tests
From: Juzhe-Zhong <juzhe.zhong@rivai.ai> This patch enable RVV auto-vectorization including floating-point unorder and order comparison. The testcases are leveraged from Richard. So include Richard as co-author. Co-Authored-By: Richard Sandiford <richard.sandiford@arm.com> gcc/ChangeLog: * config/riscv/autovec.md (@vcond_mask_<mode><vm>): New pattern. (vec_cmp<mode><vm>): Ditto. (vec_cmpu<mode><vm>): Ditto. (vcond<V:mode><VI:mode>): Ditto. (vcondu<V:mode><VI:mode>): Ditto. * config/riscv/riscv-protos.h (enum insn_type): Add new enum. (emit_vlmax_merge_insn): New function. (emit_vlmax_cmp_insn): Ditto. (expand_vec_cmp): Ditto. (expand_vec_cmp_float):Ditto. (expand_vcond):Ditto. * config/riscv/riscv-v.cc (emit_vlmax_merge_insn): Ditto. (emit_vlmax_cmp_insn): Ditto. (get_cmp_insn_code): Ditto. (expand_vec_cmp): Ditto. (expand_vec_cmp_float): Ditto. (expand_vcond): Ditto. gcc/testsuite/ChangeLog: * gcc.target/riscv/rvv/rvv.exp: Add RVV comparison testcases. * gcc.target/riscv/rvv/autovec/cmp/vcond-1.c: New test. * gcc.target/riscv/rvv/autovec/cmp/vcond-2.c: New test. * gcc.target/riscv/rvv/autovec/cmp/vcond-3.c: New test. * gcc.target/riscv/rvv/autovec/cmp/vcond_run-1.c: New test. * gcc.target/riscv/rvv/autovec/cmp/vcond_run-2.c: New test. * gcc.target/riscv/rvv/autovec/cmp/vcond_run-3.c: New test. --- gcc/config/riscv/autovec.md | 112 ++++++++ gcc/config/riscv/riscv-protos.h | 7 + gcc/config/riscv/riscv-v.cc | 258 +++++++++++++++++- .../riscv/rvv/autovec/cmp/vcond-1.c | 157 +++++++++++ .../riscv/rvv/autovec/cmp/vcond-2.c | 75 +++++ .../riscv/rvv/autovec/cmp/vcond-3.c | 13 + .../riscv/rvv/autovec/cmp/vcond_run-1.c | 49 ++++ .../riscv/rvv/autovec/cmp/vcond_run-2.c | 76 ++++++ .../riscv/rvv/autovec/cmp/vcond_run-3.c | 6 + gcc/testsuite/gcc.target/riscv/rvv/rvv.exp | 2 + 10 files changed, 754 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-1.c create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-2.c create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond-3.c create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-1.c create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-2.c create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/cmp/vcond_run-3.c