[3/5,RISC-V] Generate Zicond instruction for select pattern with condition eq or neq to 0

Message ID 20230719101156.21771-4-zengxiao@eswincomputing.com
State Superseded
Delegated to: Jeff Law
Headers
Series Recognize Zicond extension |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 pending Test started

Commit Message

Xiao Zeng July 19, 2023, 10:11 a.m. UTC
  This patch completes the recognition of Zicond when the select pattern
with condition eq or neq to 0 (using equality as an example), namely:

1 rd = (rs2 == 0) ? non-imm : 0
2 rd = (rs2 == 0) ? non-imm : non-imm
3 rd = (rs2 == 0) ? reg : non-imm
4 rd = (rs2 == 0) ? reg : reg

gcc/ChangeLog:

        * config/riscv/riscv.cc (riscv_rtx_costs): IF_THEN_ELSE costs in Zicond.
        (riscv_expand_conditional_move): Recognize Zicond.
        * config/riscv/riscv.md: Zicond patterns.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c: New test.
	* gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c: New test.
	* gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c: New test.
	* gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c: New test.
---
 gcc/config/riscv/riscv.cc                     | 125 ++++++++++++++++++
 gcc/config/riscv/riscv.md                     |   2 +-
 .../zicond-primitiveSemantics_return_0_imm.c  |  65 +++++++++
 ...zicond-primitiveSemantics_return_imm_imm.c |  73 ++++++++++
 ...zicond-primitiveSemantics_return_imm_reg.c |  65 +++++++++
 ...zicond-primitiveSemantics_return_reg_reg.c |  65 +++++++++
 6 files changed, 394 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c
  

Comments

Jeff Law July 25, 2023, 5:32 p.m. UTC | #1
On 7/19/23 04:11, Xiao Zeng wrote:
> This patch completes the recognition of Zicond when the select pattern
> with condition eq or neq to 0 (using equality as an example), namely:
> 
> 1 rd = (rs2 == 0) ? non-imm : 0
> 2 rd = (rs2 == 0) ? non-imm : non-imm
> 3 rd = (rs2 == 0) ? reg : non-imm
> 4 rd = (rs2 == 0) ? reg : reg
> 
> gcc/ChangeLog:
> 
>          * config/riscv/riscv.cc (riscv_rtx_costs): IF_THEN_ELSE costs in Zicond.
>          (riscv_expand_conditional_move): Recognize Zicond.
>          * config/riscv/riscv.md: Zicond patterns.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c: New test.
> 	* gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c: New test.
> 	* gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c: New test.
> 	* gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c: New test.
> ---
>   gcc/config/riscv/riscv.cc                     | 125 ++++++++++++++++++
>   gcc/config/riscv/riscv.md                     |   2 +-
>   .../zicond-primitiveSemantics_return_0_imm.c  |  65 +++++++++
>   ...zicond-primitiveSemantics_return_imm_imm.c |  73 ++++++++++
>   ...zicond-primitiveSemantics_return_imm_reg.c |  65 +++++++++
>   ...zicond-primitiveSemantics_return_reg_reg.c |  65 +++++++++
>   6 files changed, 394 insertions(+), 1 deletion(-)
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c
>   create mode 100644 gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c
> 
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index 38d8eb2fcf5..7e6b24bd232 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -2448,6 +2448,17 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
>   	  *total = COSTS_N_INSNS (1);
>   	  return true;
>   	}
> +      else if (TARGET_ZICOND && outer_code == SET &&
> +               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
> +               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
> +                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
> +                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))
> +        {
> +          *total = 0;
> +          return true;
> +        }
So why *total = 0.  I would have expected *total = COSTS_N_INSNS (1).


I'm not entirely sure the changes to riscv_expand_conditional_move are 
desirable -- these are likely better done in the generic if-conversion 
pass.


> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 6b8c2e8e268..b4147c7a79c 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -2484,7 +2484,7 @@
>   	(if_then_else:GPR (match_operand 1 "comparison_operator")
>   			  (match_operand:GPR 2 "reg_or_0_operand")
>   			  (match_operand:GPR 3 "sfb_alu_operand")))]
> -  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV"
> +  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV || TARGET_ZICOND"
>   {
>     if (riscv_expand_conditional_move (operands[0], operands[1],
>   				     operands[2], operands[3]))
We had to do more than just slap on a TARGET_ZICOND.  I'm a bit 
surprised this worked as-is.  Though we also have bits to handle 
conditions other than eq/ne by first emitting an sCC style insn which 
might be adding complication or cases you hadn't encountered.


Jeff
  
Andreas Schwab July 25, 2023, 5:55 p.m. UTC | #2
On Jul 19 2023, Xiao Zeng wrote:

> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index 38d8eb2fcf5..7e6b24bd232 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -2448,6 +2448,17 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
>  	  *total = COSTS_N_INSNS (1);
>  	  return true;
>  	}
> +      else if (TARGET_ZICOND && outer_code == SET &&
> +               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
> +               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
> +                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
> +                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))

Line breaks before the operator, not after.
  
Xiao Zeng July 27, 2023, 5:44 a.m. UTC | #3
On Wed, Jul 26, 2023 at 01:55:00 AM Andreas Schwab <schwab@linux-m68k.org> wrote:
>
>On Jul 19 2023, Xiao Zeng wrote:
>
>> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
>> index 38d8eb2fcf5..7e6b24bd232 100644
>> --- a/gcc/config/riscv/riscv.cc
>> +++ b/gcc/config/riscv/riscv.cc
>> @@ -2448,6 +2448,17 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
>>    *total = COSTS_N_INSNS (1);
>>    return true;
>>  }
>> +      else if (TARGET_ZICOND && outer_code == SET &&
>> +               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
>> +               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>> +                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>> +                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))
>
>Line breaks before the operator, not after.
>
>--
>Andreas Schwab, schwab@linux-m68k.org
>GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
>"And now for something completely different." 

Thank you for pointing out the code format issue. I will fix it in the future patch.
  
Jeff Law July 28, 2023, 3:09 p.m. UTC | #4
On 7/25/23 11:55, Andreas Schwab wrote:
> On Jul 19 2023, Xiao Zeng wrote:
> 
>> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
>> index 38d8eb2fcf5..7e6b24bd232 100644
>> --- a/gcc/config/riscv/riscv.cc
>> +++ b/gcc/config/riscv/riscv.cc
>> @@ -2448,6 +2448,17 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
>>   	  *total = COSTS_N_INSNS (1);
>>   	  return true;
>>   	}
>> +      else if (TARGET_ZICOND && outer_code == SET &&
>> +               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
>> +               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>> +                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>> +                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))
> 
> Line breaks before the operator, not after.
Also note that && GET_CODE (XEXP (x, 2)) && that appears twice.

That just verifies the code isn't RTX_UNKNOWN which I suspect isn't what 
the author intended.  It probably needs to be adjusted for SUBREGs and 
the pointer equality issues with REGs after reload.

I'll take care of these goofs since the costing ought to be able to move 
forward independently of the improvements Xiao made to generating 
conditional move sequences.

Jeff
  
Jeff Law July 28, 2023, 8:59 p.m. UTC | #5
On 7/19/23 04:11, Xiao Zeng wrote:

> +  else if (TARGET_ZICOND
> +           && (code == EQ || code == NE)
> +           && GET_MODE_CLASS (mode) == MODE_INT)
> +    {
> +      need_eq_ne_p = true;
> +      /* 0 + imm  */
> +      if (GET_CODE (cons) == CONST_INT && cons == const0_rtx
> +          && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
> +        {
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +          alt = force_reg (mode, alt);
> +          emit_insn (gen_rtx_SET (dest,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                        cons, alt)));
> +          return true;
> +        }
> +      /* imm + imm  */
> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
> +        {
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +          alt = force_reg (mode, alt);
> +          rtx temp1 = gen_reg_rtx (mode);
> +          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
> +          riscv_emit_binary(PLUS, temp1, alt, temp2);
So in this sequence you're just computing a constant since both ALT and 
CONS are constants.  It's better to just form the constant directly, 
then force that into a register because it'll make the costing more 
correct, particularly if the resulting constant needs more than one 
instruction to synthesize.

And a nit.  There should always be a space between a function name and 
its argument list.



> +          emit_insn (gen_rtx_SET (dest,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                        const0_rtx, alt)));
> +          riscv_emit_binary(PLUS, dest, dest, cons);
> +          return true;
I don't see how this can be correct from a code generation standpoint. 
You compute ALT-CONS into TEMP1 earlier.  But you never use TEMP1 after 
that.  I think you meant to use TEMP1 instead of ALT as the false arm if 
the IF-THEN-ELSE you constructed.

In general you should be using CONST0_RTX (mode) rather than const0_rtx.

> +        }
> +      /* imm + reg  */
> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
> +               && GET_CODE (alt) == REG)
> +        {
> +          /* Optimize for register value of 0.  */
> +          if (op0 == alt && op1 == const0_rtx)
> +            {
> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +              cons = force_reg (mode, cons);
> +              emit_insn (gen_rtx_SET (dest,
> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                            cons, alt)));
> +              return true;
> +            }
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +          rtx temp1 = gen_reg_rtx (mode);
> +          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
> +          riscv_emit_binary(PLUS, temp1, alt, temp2);
Here you have to be careful if CONS is -2048.  You negate it resulting 
in +2048 which can't be used in an addi.  This will cause the entire 
sequence to fail due to an unrecognized insn.  It would be better to 
handle that scenario directly so the generated sequence is still valid.

By generating recognizable code in that case we let the costing model 
determine if the conditional move sequence is better than the branching 
sequence.


> +          emit_insn (gen_rtx_SET (dest,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                        const0_rtx, alt)));
I think we have the same problem with the use of ALT here rather than 
TEMP1 that we had in the previous case.



> +      /* reg + imm  */
> +      else if (GET_CODE (cons) == REG
> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
> +        {
> +          /* Optimize for register value of 0.  */
> +          if (op0 == cons && op1 == const0_rtx)
> +            {
> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +              alt = force_reg (mode, alt);
> +              emit_insn (gen_rtx_SET (dest,
> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                            cons, alt)));
> +              return true;
> +            }
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +          rtx temp1 = gen_reg_rtx (mode);
> +          rtx temp2 = GEN_INT(-1 * INTVAL (alt));
> +          riscv_emit_binary(PLUS, temp1, cons, temp2);
> +          emit_insn (gen_rtx_SET (dest,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                        temp1, const0_rtx)));
> +          riscv_emit_binary(PLUS, dest, dest, alt);
> +          return true;
This has basically the same issues as the imm + reg case.


> +        }
> +      /* reg + reg  */
> +      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
> +        {
> +          rtx reg1 = gen_reg_rtx (mode);
> +          rtx reg2 = gen_reg_rtx (mode);
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
> +          rtx cond1 = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +          rtx cond2 = gen_rtx_fmt_ee (code == NE ? EQ : NE,
> +                                      GET_MODE (op0), op0, op1);
> +          emit_insn (gen_rtx_SET (reg2,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond2,
> +                                                        const0_rtx, cons)));
> +          emit_insn (gen_rtx_SET (reg1,
> +                                  gen_rtx_IF_THEN_ELSE (mode, cond1,
> +                                                        const0_rtx, alt)));
> +          riscv_emit_binary(IOR, dest, reg1, reg2);
> +          return true;
This probably should detect the case where alt or cons is the same as 
op0.  Otherwise I would expect the costing model to reject some cases 
that are actually profitable.


> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 6b8c2e8e268..b4147c7a79c 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -2484,7 +2484,7 @@
>   	(if_then_else:GPR (match_operand 1 "comparison_operator")
>   			  (match_operand:GPR 2 "reg_or_0_operand")
>   			  (match_operand:GPR 3 "sfb_alu_operand")))
> -  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV"
> +  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV || TARGET_ZICOND"
Note the predicate for operand2 will reject all constants except 0.

Anyway, I'm still cleaning this up and adding the bits from Ventana 
which handle the relational conditions as well as FP conditionals.

Jeff
  
Xiao Zeng July 29, 2023, 9:14 a.m. UTC | #6
On Sat, Jul 29, 2023 at 04:59:00 AM  Jeff Law <jeffreyalaw@gmail.com> wrote:
>
>
>
>On 7/19/23 04:11, Xiao Zeng wrote:
>
>> +  else if (TARGET_ZICOND
>> +           && (code == EQ || code == NE)
>> +           && GET_MODE_CLASS (mode) == MODE_INT)
>> +    {
>> +      need_eq_ne_p = true;
>> +      /* 0 + imm  */
>> +      if (GET_CODE (cons) == CONST_INT && cons == const0_rtx
>> +          && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
>> +        {
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          alt = force_reg (mode, alt);
>> +          emit_insn (gen_rtx_SET (dest,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                        cons, alt)));
>> +          return true;
>> +        }
>> +      /* imm + imm  */
>> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
>> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
>> +        {
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          alt = force_reg (mode, alt);
>> +          rtx temp1 = gen_reg_rtx (mode);
>> +          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
>> +          riscv_emit_binary(PLUS, temp1, alt, temp2);
>So in this sequence you're just computing a constant since both ALT and
>CONS are constants.  It's better to just form the constant directly,
>then force that into a register because it'll make the costing more
>correct, particularly if the resulting constant needs more than one
>instruction to synthesize. 

Fixed

>
>And a nit.  There should always be a space between a function name and
>its argument list. 

Fixed

>
>
>
>> +          emit_insn (gen_rtx_SET (dest,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                        const0_rtx, alt)));
>> +          riscv_emit_binary(PLUS, dest, dest, cons);
>> +          return true;
>I don't see how this can be correct from a code generation standpoint.
>You compute ALT-CONS into TEMP1 earlier.  But you never use TEMP1 after
>that.  I think you meant to use TEMP1 instead of ALT as the false arm if
>the IF-THEN-ELSE you constructed. 

Fixed

>
>In general you should be using CONST0_RTX (mode) rather than const0_rtx.
> 

Fixed

>> +        }
>> +      /* imm + reg  */
>> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
>> +               && GET_CODE (alt) == REG)
>> +        {
>> +          /* Optimize for register value of 0.  */
>> +          if (op0 == alt && op1 == const0_rtx)
>> +            {
>> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +              cons = force_reg (mode, cons);
>> +              emit_insn (gen_rtx_SET (dest,
>> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                            cons, alt)));
>> +              return true;
>> +            }
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          rtx temp1 = gen_reg_rtx (mode);
>> +          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
>> +          riscv_emit_binary(PLUS, temp1, alt, temp2);
>Here you have to be careful if CONS is -2048.  You negate it resulting
>in +2048 which can't be used in an addi.  This will cause the entire
>sequence to fail due to an unrecognized insn.  It would be better to
>handle that scenario directly so the generated sequence is still valid.
>
>By generating recognizable code in that case we let the costing model
>determine if the conditional move sequence is better than the branching
>sequence. 

Thank you for pointing out this special situation, it has been fixed

>
>
>> +          emit_insn (gen_rtx_SET (dest,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                        const0_rtx, alt)));
>I think we have the same problem with the use of ALT here rather than
>TEMP1 that we had in the previous case. 

Fixed

>
>
>
>> +      /* reg + imm  */
>> +      else if (GET_CODE (cons) == REG
>> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
>> +        {
>> +          /* Optimize for register value of 0.  */
>> +          if (op0 == cons && op1 == const0_rtx)
>> +            {
>> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +              alt = force_reg (mode, alt);
>> +              emit_insn (gen_rtx_SET (dest,
>> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                            cons, alt)));
>> +              return true;
>> +            }
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          rtx temp1 = gen_reg_rtx (mode);
>> +          rtx temp2 = GEN_INT(-1 * INTVAL (alt));
>> +          riscv_emit_binary(PLUS, temp1, cons, temp2);
>> +          emit_insn (gen_rtx_SET (dest,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                        temp1, const0_rtx)));
>> +          riscv_emit_binary(PLUS, dest, dest, alt);
>> +          return true;
>This has basically the same issues as the imm + reg case. 

Fixed

>
>
>> +        }
>> +      /* reg + reg  */
>> +      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
>> +        {
>> +          rtx reg1 = gen_reg_rtx (mode);
>> +          rtx reg2 = gen_reg_rtx (mode);
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond1 = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          rtx cond2 = gen_rtx_fmt_ee (code == NE ? EQ : NE,
>> +                                      GET_MODE (op0), op0, op1);
>> +          emit_insn (gen_rtx_SET (reg2,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond2,
>> +                                                        const0_rtx, cons)));
>> +          emit_insn (gen_rtx_SET (reg1,
>> +                                  gen_rtx_IF_THEN_ELSE (mode, cond1,
>> +                                                        const0_rtx, alt)));
>> +          riscv_emit_binary(IOR, dest, reg1, reg2);
>> +          return true;
>This probably should detect the case where alt or cons is the same as
>op0.  Otherwise I would expect the costing model to reject some cases
>that are actually profitable.
> 

Fixed

>
>> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
>> index 6b8c2e8e268..b4147c7a79c 100644
>> --- a/gcc/config/riscv/riscv.md
>> +++ b/gcc/config/riscv/riscv.md
>> @@ -2484,7 +2484,7 @@
>>   (if_then_else:GPR (match_operand 1 "comparison_operator")
>>     (match_operand:GPR 2 "reg_or_0_operand")
>>     (match_operand:GPR 3 "sfb_alu_operand")))
>> -  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV"
>> +  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV || TARGET_ZICOND"
>Note the predicate for operand2 will reject all constants except 0.
>
>Anyway, I'm still cleaning this up and adding the bits from Ventana
>which handle the relational conditions as well as FP conditionals.
>
>Jeff 

1 Thank you for Jeff's code review comments. I have made the modifications
and submitted the V2-patch[3/5].

2 For the calculation method of cost, I hope to submit a separate patch[cost]
after the V2-patch[3/5] merged into master, which will focus on explaining
the reasons for calculating cost in the same way as in patch[4/5].

3 At the same time, I realized that for Zicond's series of patches, it would be
better to split them into separate patches and submit them to the community
code review. Therefore, I plan to submit: 
V2-patch[3/5]
patch[cost]
V2-patch[4/5] 
V2-patch[5/5]
I will only submit subsequent patches after the previous patch enters the master.

4. In V2-patch[3/5], Zicond's cost calculation is not involved, therefore, all test
cases are skipped with "- O0" and "- Os". I will remove the "- Os" constraint from
the test case in patch[cost].

5 The address for V2-patch[3/5] is: https://gcc.gnu.org/pipermail/gcc-patches/2023-July/625781.html

Thanks
Xiao Zeng
  
Xiao Zeng July 29, 2023, 9:48 a.m. UTC | #7
On Fri, Jul 28, 2023 at 11:09:00 PM  Jeff Law <jeffreyalaw@gmail.com> wrote:
>
>
>
>On 7/25/23 11:55, Andreas Schwab wrote:
>> On Jul 19 2023, Xiao Zeng wrote:
>>
>>> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
>>> index 38d8eb2fcf5..7e6b24bd232 100644
>>> --- a/gcc/config/riscv/riscv.cc
>>> +++ b/gcc/config/riscv/riscv.cc
>>> @@ -2448,6 +2448,17 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
>>>     *total = COSTS_N_INSNS (1);
>>>     return true;
>>>   }
>>> +      else if (TARGET_ZICOND && outer_code == SET &&
>>> +               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
>>> +               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
>>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>>> +                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
>>> +               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
>>> +                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))
>>
>> Line breaks before the operator, not after.
>Also note that && GET_CODE (XEXP (x, 2)) && that appears twice. 

This is an error that I will fix in patch[cost] and provide a detailed explanation.

>
>That just verifies the code isn't RTX_UNKNOWN which I suspect isn't what
>the author intended.  It probably needs to be adjusted for SUBREGs and
>the pointer equality issues with REGs after reload.
>
>I'll take care of these goofs since the costing ought to be able to move
>forward independently of the improvements Xiao made to generating
>conditional move sequences.
>
>Jeff 

After V2-patch[3/5] is accepted, a patch[cost] will be submitted to provide detailed
explanation of this issue. Of course, as Jeff mentioned, some issues will also be fixed.

Thanks
Xiao Zeng
  
Jeff Law Aug. 2, 2023, 6:34 a.m. UTC | #8
On 7/19/23 04:11, Xiao Zeng wrote:
> This patch completes the recognition of Zicond when the select pattern
> with condition eq or neq to 0 (using equality as an example), namely:
[ ... ]
I've committed the attached patch which implements a simple cost model 
for using Zicond to implement conditional moves.

I've changed it to give the proper cost (COSTS_N_INSNS (1) and removed 
the extraneous GET_CODE (object) tests adjusted the ChangeLog a bit and 
pushed it to the trunk.

Thanks!

Jeff
commit 5b501863ac7da57858fdd464dfb7a776143f22a2
Author: Xiao Zeng <zengxiao@eswincomputing.com>
Date:   Wed Aug 2 00:17:12 2023 -0600

    [PATCH 3/5] [RISC-V] Cost model for Zicond.
    
    This patch implements a reasonable cost model for using Zicond to
    implement conditional moves.  Essentially the Zicond insns are always
    COSTS_N_INSNS (1).
    
    Note there is still a problem with the costing model in general that
    results in failure to if-convert as often as we should.  In simplest
    terms the insn costing model sums the cost of the SET_SRC and the
    cost of the SET_DEST.  Thus the conditional move is considered twice
    as costly as it should be.  That will have to be addressed separately.
    
    gcc/
            * config/riscv/riscv.cc (riscv_rtx_costs): Add costing for
            using Zicond to implement some conditional moves.

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 8c474503080..785e09c76ce 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -2518,6 +2518,20 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	  *total = COSTS_N_INSNS (1);
 	  return true;
 	}
+      else if (TARGET_ZICOND
+	       && outer_code == SET
+	       && ((GET_CODE (XEXP (x, 1)) == REG
+		    && XEXP (x, 2) == CONST0_RTX (GET_MODE (XEXP (x, 1))))
+		   || (GET_CODE (XEXP (x, 2)) == REG
+		       && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 2))))
+		   || (GET_CODE (XEXP (x, 1)) == REG
+		       && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 0), 0)))
+		   || (GET_CODE (XEXP (x, 1)) == REG
+		       && rtx_equal_p (XEXP (x, 2), XEXP (XEXP (x, 0), 0)))))
+	{
+	  *total = COSTS_N_INSNS (1);
+	  return true;
+	}
       else if (LABEL_REF_P (XEXP (x, 1)) && XEXP (x, 2) == pc_rtx)
 	{
 	  if (equality_operator (XEXP (x, 0), mode)
  
Jeff Law Aug. 3, 2023, 4:59 a.m. UTC | #9
On 7/29/23 03:14, Xiao Zeng wrote:

> 
> 1 Thank you for Jeff's code review comments. I have made the modifications
> and submitted the V2-patch[3/5].
Yea.  I'm adjusting my tree based on those updates.  For testing I've 
actually got my compiler generating zicond by default and qemu allowing 
zicond by default.  I can then run the execute.exp tests which validate 
code correctness to a reasonable degree.


> 
> 2 For the calculation method of cost, I hope to submit a separate patch[cost]
> after the V2-patch[3/5] merged into master, which will focus on explaining
> the reasons for calculating cost in the same way as in patch[4/5].
I think the costing problem is going to require its own little 
subproject.  GCC's approach to costing is a bit crazy with multiple APIs 
that behave differently and in some cases do some rather surprising 
things.  It's a long standing design flaw.

The point being that I think we'll probably move forward with the 
functional bits, perhaps initially without the basic functionality 
tests.  That allows folks to start utilizing the core functionality 
while we audit and likely adjust the risc-v cost hook implementation.


> 
> 4. In V2-patch[3/5], Zicond's cost calculation is not involved, therefore, all test
> cases are skipped with "- O0" and "- Os". I will remove the "- Os" constraint from
> the test case in patch[cost].
We may need to avoid for -Og as well.  I've got that change here 
locally, but I wanted to go back and review that as well.

jeff
  

Patch

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 38d8eb2fcf5..7e6b24bd232 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -2448,6 +2448,17 @@  riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN
 	  *total = COSTS_N_INSNS (1);
 	  return true;
 	}
+      else if (TARGET_ZICOND && outer_code == SET &&
+               ((GET_CODE (XEXP (x, 1)) == REG && XEXP (x, 2) == const0_rtx) ||
+               (GET_CODE (XEXP (x, 2)) == REG && XEXP (x, 1) == const0_rtx) ||
+               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
+                XEXP (x, 1) == XEXP (XEXP (x, 0), 0)) ||
+               (GET_CODE (XEXP (x, 1)) == REG && GET_CODE (XEXP (x, 2)) &&
+                XEXP (x, 2) == XEXP (XEXP (x, 0), 0))))
+        {
+          *total = 0;
+          return true;
+        }
       else if (LABEL_REF_P (XEXP (x, 1)) && XEXP (x, 2) == pc_rtx)
 	{
 	  if (equality_operator (XEXP (x, 0), mode)
@@ -3501,6 +3512,120 @@  riscv_expand_conditional_move (rtx dest, rtx op, rtx cons, rtx alt)
 							  cond, cons, alt)));
       return true;
     }
+  else if (TARGET_ZICOND
+           && (code == EQ || code == NE)
+           && GET_MODE_CLASS (mode) == MODE_INT)
+    {
+      need_eq_ne_p = true;
+      /* 0 + imm  */
+      if (GET_CODE (cons) == CONST_INT && cons == const0_rtx
+          && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
+        {
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          alt = force_reg (mode, alt);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                        cons, alt)));
+          return true;
+        }
+      /* imm + imm  */
+      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
+               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
+        {
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          alt = force_reg (mode, alt);
+          rtx temp1 = gen_reg_rtx (mode);
+          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
+          riscv_emit_binary(PLUS, temp1, alt, temp2);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                        const0_rtx, alt)));
+          riscv_emit_binary(PLUS, dest, dest, cons);
+          return true;
+        }
+      /* imm + reg  */
+      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
+               && GET_CODE (alt) == REG)
+        {
+          /* Optimize for register value of 0.  */
+          if (op0 == alt && op1 == const0_rtx)
+            {
+              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+              cons = force_reg (mode, cons);
+              emit_insn (gen_rtx_SET (dest,
+                                      gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                            cons, alt)));
+              return true;
+            }
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          rtx temp1 = gen_reg_rtx (mode);
+          rtx temp2 = GEN_INT(-1 * INTVAL (cons));
+          riscv_emit_binary(PLUS, temp1, alt, temp2);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                        const0_rtx, alt)));
+          riscv_emit_binary(PLUS, dest, dest, cons);
+          return true;
+        }
+      /* imm + 0  */
+      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
+               && GET_CODE (alt) == CONST_INT && alt == const0_rtx)
+        {
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          cons = force_reg (mode, cons);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                        cons, alt)));
+          return true;
+        }
+      /* reg + imm  */
+      else if (GET_CODE (cons) == REG
+               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
+        {
+          /* Optimize for register value of 0.  */
+          if (op0 == cons && op1 == const0_rtx)
+            {
+              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+              alt = force_reg (mode, alt);
+              emit_insn (gen_rtx_SET (dest,
+                                      gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                            cons, alt)));
+              return true;
+            }
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          rtx temp1 = gen_reg_rtx (mode);
+          rtx temp2 = GEN_INT(-1 * INTVAL (alt));
+          riscv_emit_binary(PLUS, temp1, cons, temp2);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                        temp1, const0_rtx)));
+          riscv_emit_binary(PLUS, dest, dest, alt);
+          return true;
+        }
+      /* reg + reg  */
+      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
+        {
+          rtx reg1 = gen_reg_rtx (mode);
+          rtx reg2 = gen_reg_rtx (mode);
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond1 = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          rtx cond2 = gen_rtx_fmt_ee (code == NE ? EQ : NE,
+                                      GET_MODE (op0), op0, op1);
+          emit_insn (gen_rtx_SET (reg2,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond2,
+                                                        const0_rtx, cons)));
+          emit_insn (gen_rtx_SET (reg1,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond1,
+                                                        const0_rtx, alt)));
+          riscv_emit_binary(IOR, dest, reg1, reg2);
+          return true;
+        }
+    }
 
   return false;
 }
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 6b8c2e8e268..b4147c7a79c 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -2484,7 +2484,7 @@ 
 	(if_then_else:GPR (match_operand 1 "comparison_operator")
 			  (match_operand:GPR 2 "reg_or_0_operand")
 			  (match_operand:GPR 3 "sfb_alu_operand")))]
-  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV"
+  "TARGET_SFB_ALU || TARGET_XTHEADCONDMOV || TARGET_ZICOND"
 {
   if (riscv_expand_conditional_move (operands[0], operands[1],
 				     operands[2], operands[3]))
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c
new file mode 100644
index 00000000000..4948558a187
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_0_imm.c
@@ -0,0 +1,65 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zicond -mabi=ilp32f" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } {"-O0"} } */
+
+long primitiveSemantics_return_0_imm_00(long a, long b) {
+  return a == 0 ? 0 : 3;
+}
+
+long primitiveSemantics_return_0_imm_01(long a, long b) {
+  return a != 0 ? 0 : 3;
+}
+
+long primitiveSemantics_return_0_imm_02(long a, long b) {
+  return a == 0 ? 3 : 0;
+}
+
+long primitiveSemantics_return_0_imm_03(long a, long b) {
+  return a != 0 ? 3 : 0;
+}
+
+long primitiveSemantics_return_0_imm_04(long a, long b) {
+  if (a)
+    b = 0;
+  else
+    b = 3;
+  return b;
+}
+
+long primitiveSemantics_return_0_imm_05(long a, long b) {
+  if (!a)
+    b = 0;
+  else
+    b = 3;
+  return b;
+}
+
+int primitiveSemantics_return_0_imm_06(int a, int b) { return a == 0 ? 0 : 3; }
+
+int primitiveSemantics_return_0_imm_07(int a, int b) { return a != 0 ? 0 : 3; }
+
+int primitiveSemantics_return_0_imm_08(int a, int b) { return a == 0 ? 3 : 0; }
+
+int primitiveSemantics_return_0_imm_09(int a, int b) { return a != 0 ? 3 : 0; }
+
+int primitiveSemantics_return_0_imm_10(int a, int b) {
+  if (a)
+    b = 0;
+  else
+    b = 3;
+  return b;
+}
+
+int primitiveSemantics_return_0_imm_11(int a, int b) {
+  if (!a)
+    b = 0;
+  else
+    b = 3;
+  return b;
+}
+
+/* { dg-final { scan-assembler-times "czero.eqz" 6 } } */
+/* { dg-final { scan-assembler-times "czero.nez" 6 } } */
+/* { dg-final { scan-assembler-not "beq" } } */
+/* { dg-final { scan-assembler-not "bne" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c
new file mode 100644
index 00000000000..ebdca521373
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_imm.c
@@ -0,0 +1,73 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zicond -mabi=ilp32f" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-Os"} } */
+
+long primitiveSemantics_return_imm_imm_00(long a, long b) {
+  return a == 0 ? 4 : 6;
+}
+
+long primitiveSemantics_return_imm_imm_01(long a, long b) {
+  return a != 0 ? 4 : 6;
+}
+
+long primitiveSemantics_return_imm_imm_02(long a, long b) {
+  return a == 0 ? 6 : 4;
+}
+
+long primitiveSemantics_return_imm_imm_03(long a, long b) {
+  return a != 0 ? 6 : 4;
+}
+
+long primitiveSemantics_return_imm_imm_04(long a, long b) {
+  if (a)
+    b = 4;
+  else
+    b = 6;
+  return b;
+}
+
+long primitiveSemantics_return_imm_imm_05(long a, long b) {
+  if (!a)
+    b = 4;
+  else
+    b = 6;
+  return b;
+}
+
+int primitiveSemantics_return_imm_imm_06(int a, int b) {
+  return a == 0 ? 4 : 6;
+}
+
+int primitiveSemantics_return_imm_imm_07(int a, int b) {
+  return a != 0 ? 4 : 6;
+}
+
+int primitiveSemantics_return_imm_imm_08(int a, int b) {
+  return a == 0 ? 6 : 4;
+}
+
+int primitiveSemantics_return_imm_imm_09(int a, int b) {
+  return a != 0 ? 6 : 4;
+}
+
+int primitiveSemantics_return_imm_imm_10(int a, int b) {
+  if (a)
+    b = 4;
+  else
+    b = 6;
+  return b;
+}
+
+int primitiveSemantics_return_imm_imm_11(int a, int b) {
+  if (!a)
+    b = 4;
+  else
+    b = 6;
+  return b;
+}
+
+/* { dg-final { scan-assembler-times "czero.eqz" 6 } } */
+/* { dg-final { scan-assembler-times "czero.nez" 6 } } */
+/* { dg-final { scan-assembler-not "beq" } } */
+/* { dg-final { scan-assembler-not "bne" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c
new file mode 100644
index 00000000000..12c351dbc16
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_imm_reg.c
@@ -0,0 +1,65 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zicond -mabi=ilp32f" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-Os"} } */
+
+long primitiveSemantics_return_imm_reg_00(long a, long b) {
+  return a == 0 ? 1 : b;
+}
+
+long primitiveSemantics_return_imm_reg_01(long a, long b) {
+  return a != 0 ? 1 : b;
+}
+
+long primitiveSemantics_return_imm_reg_02(long a, long b) {
+  return a == 0 ? b : 1;
+}
+
+long primitiveSemantics_return_imm_reg_03(long a, long b) {
+  return a != 0 ? b : 1;
+}
+
+long primitiveSemantics_return_imm_reg_04(long a, long b) {
+  if (a)
+    b = 1;
+  return b;
+}
+
+long primitiveSemantics_return_imm_reg_05(long a, long b) {
+  if (!a)
+    b = 1;
+  return b;
+}
+
+int primitiveSemantics_return_imm_reg_06(int a, int b) {
+  return a == 0 ? 1 : b;
+}
+
+int primitiveSemantics_return_imm_reg_07(int a, int b) {
+  return a != 0 ? 1 : b;
+}
+
+int primitiveSemantics_return_imm_reg_08(int a, int b) {
+  return a == 0 ? b : 1;
+}
+
+int primitiveSemantics_return_imm_reg_09(int a, int b) {
+  return a != 0 ? b : 1;
+}
+
+int primitiveSemantics_return_imm_reg_10(int a, int b) {
+  if (a)
+    b = 1;
+  return b;
+}
+
+int primitiveSemantics_return_imm_reg_11(int a, int b) {
+  if (!a)
+    b = 1;
+  return b;
+}
+
+/* { dg-final { scan-assembler-times "czero.eqz" 6 } } */
+/* { dg-final { scan-assembler-times "czero.nez" 6 } } */
+/* { dg-final { scan-assembler-not "beq" } } */
+/* { dg-final { scan-assembler-not "bne" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c
new file mode 100644
index 00000000000..4708afa645b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicond-primitiveSemantics_return_reg_reg.c
@@ -0,0 +1,65 @@ 
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zicond -mabi=ilp32f" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } {"-O0" "-Os"} } */
+
+long primitiveSemantics_return_reg_reg_00(long a, long b, long c) {
+  return a == 0 ? c : b;
+}
+
+long primitiveSemantics_return_reg_reg_01(long a, long b, long c) {
+  return a != 0 ? c : b;
+}
+
+long primitiveSemantics_return_reg_reg_02(long a, long b, long c) {
+  return a == 0 ? b : c;
+}
+
+long primitiveSemantics_return_reg_reg_03(long a, long b, long c) {
+  return a != 0 ? b : c;
+}
+
+long primitiveSemantics_return_reg_reg_04(long a, long b, long c) {
+  if (a)
+    b = c;
+  return b;
+}
+
+long primitiveSemantics_return_reg_reg_05(long a, long b, long c) {
+  if (!a)
+    b = c;
+  return b;
+}
+
+int primitiveSemantics_return_reg_reg_06(int a, int b, int c) {
+  return a == 0 ? c : b;
+}
+
+int primitiveSemantics_return_reg_reg_07(int a, int b, int c) {
+  return a != 0 ? c : b;
+}
+
+int primitiveSemantics_return_reg_reg_08(int a, int b, int c) {
+  return a == 0 ? b : c;
+}
+
+int primitiveSemantics_return_reg_reg_09(int a, int b, int c) {
+  return a != 0 ? b : c;
+}
+
+int primitiveSemantics_return_reg_reg_10(int a, int b, int c) {
+  if (a)
+    b = c;
+  return b;
+}
+
+int primitiveSemantics_return_reg_reg_11(int a, int b, int c) {
+  if (!a)
+    b = c;
+  return b;
+}
+
+/* { dg-final { scan-assembler-times "czero.eqz" 12 } } */
+/* { dg-final { scan-assembler-times "czero.nez" 12 } } */
+/* { dg-final { scan-assembler-not "beq" } } */
+/* { dg-final { scan-assembler-not "bne" } } */