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

Message ID 20230729091308.29792-1-zengxiao@eswincomputing.com
State Accepted
Headers
Series None |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Xiao Zeng July 29, 2023, 9:13 a.m. UTC
  This patch recognizes Zicond patterns when the select pattern
with condition eq or neq to 0 (using eq 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_expand_conditional_move): Recognize
        Zicond patterns
        * config/riscv/riscv.md: Recognize Zicond patterns through mov<mode>cc

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                     | 144 ++++++++++++++++++
 gcc/config/riscv/riscv.md                     |   4 +-
 .../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, 414 insertions(+), 2 deletions(-)
 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 Aug. 1, 2023, 6:06 a.m. UTC | #1
On 7/29/23 03:13, Xiao Zeng wrote:
> This patch recognizes Zicond patterns when the select pattern
> with condition eq or neq to 0 (using eq 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_expand_conditional_move): Recognize
>          Zicond patterns
>          * config/riscv/riscv.md: Recognize Zicond patterns through mov<mode>cc
> 
> 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                     | 144 ++++++++++++++++++
>   gcc/config/riscv/riscv.md                     |   4 +-
>   .../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, 414 insertions(+), 2 deletions(-)
>   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 941ea25e1f2..6ac39f63dd7 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -3516,6 +3516,150 @@ 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)
A couple nits.  Rather than test the GET_CODE (object) == CONST_INT, 
instead use CONST_INT_P (object).

Rather than using const0_rtx, use CONST0_RTX (mode).  That makes it more 
general.



> +        {
> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
Might as well use "true" rather than "need_eq_ne_p" here and for the 
other calls in your new code.

> +      /* imm + imm  */
> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
So same comments on using CONST_INT_P and 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);
> +          rtx reg = gen_reg_rtx (mode);
> +          rtx temp = GEN_INT (INTVAL (alt) - INTVAL (cons));
> +          emit_insn (gen_rtx_SET (reg, temp));
Use force_reg here rather than directly emitting the insn to initialize 
"reg".  What you're doing works when the difference is small but will 
not work when the difference does not fit into a signed 12bit value.

> +      /* imm + reg  */
> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
> +               && GET_CODE (alt) == REG)
Same comments about CONST_INT_P and CONST0_RTX.  And instead of using 
GET_CODE (object) == REG, use REG_P (object).


> +        {
> +          /* 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;
> +            }
Isn't this only valid for NE?  Also use CONST0_RTX in that test.


> +          /* Handle the special situation of: -2048 == INTVAL (alt)
> +             to avoid failure due to an unrecognized insn. Let the costing
> +             model determine if the conditional move sequence is better
> +             than the branching sequence.  */
> +          if (-2048 == INTVAL (cons))
So instead of checking for explicit values, we have SMALL_OPERAND to do 
it for us.  Also note that for !SMALL_OPERAND we can just force the 
value into a register using "force_reg" and all the right things will 
happen.

So just add something like this to your original code:

   if (!SMALL_OPERAND (INTVAL (cons))
     cons = force_reg (mode, cons);

That will result in CONS being either a simple integer constant (when it 
is suitable for addi) or a register (all other cases).  At that point 
you can use it in an arithmetic instruction.



> +      /* 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;
> +            }
So in this case isn't it only valid for EQ?

> +          if (-2048 == INTVAL (alt))
Similar comment as above.  When ALT doesn't fit into a SMALL_OPERAND_P, 
just force it into a register.

> +      /* reg + reg  */
> +      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
> +        {
> +          /* Optimize for op0 == cons && op1 == const0_rtx.  */
> +          if (op0 == cons && op1 == const0_rtx)
> +            {
> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +              emit_insn (gen_rtx_SET (dest,
> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
> +                                                            CONST0_RTX (mode),
> +                                                            alt)));
> +              return true;
> +            }
> +          /* Optimize for op0 == alt && op1 == const0_rtx.  */
> +          if (op0 == alt && op1 == const0_rtx)
> +            {
> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
> +              emit_insn (gen_rtx_SET (dest,
> +                                      gen_rtx_IF_THEN_ELSE (mode, cond, cons,
> +                                                            CONST0_RTX (mode))));
> +              return true;
> +            }
I suspect we need some testing of the code in the two sequences above. 
I'd bet one is only valid for EQ and the other for NE.

It's improving considerably.

WRT costing.  I'm still seeing some weirdness in how conditional moves 
are being costed from the if-converter.  I think your patch that set the 
cost to COSTS_N_INSNS (0) was just working around a deeper bug in the 
costing code.  I'm still trying to figure out the best way to clean that up.


jeff
  
Xiao Zeng Aug. 2, 2023, 1:39 a.m. UTC | #2
On Tue, Aug 01, 2023 at 02:06:00 PM Jeff Law <jeffreyalaw@gmail.com> wrote:
>
>
>
>On 7/29/23 03:13, Xiao Zeng wrote:
>> This patch recognizes Zicond patterns when the select pattern
>> with condition eq or neq to 0 (using eq 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_expand_conditional_move): Recognize
>>          Zicond patterns
>>          * config/riscv/riscv.md: Recognize Zicond patterns through mov<mode>cc
>>
>> 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                     | 144 ++++++++++++++++++
>>   gcc/config/riscv/riscv.md                     |   4 +-
>>   .../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, 414 insertions(+), 2 deletions(-)
>>   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 941ea25e1f2..6ac39f63dd7 100644
>> --- a/gcc/config/riscv/riscv.cc
>> +++ b/gcc/config/riscv/riscv.cc
>> @@ -3516,6 +3516,150 @@ 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)
>A couple nits.  Rather than test the GET_CODE (object) == CONST_INT,
>instead use CONST_INT_P (object). 
fixed

>
>Rather than using const0_rtx, use CONST0_RTX (mode).  That makes it more
>general. 
fixed

>
>
>
>> +        {
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>Might as well use "true" rather than "need_eq_ne_p" here and for the
>other calls in your new code.
> 
fixed

>> +      /* imm + imm  */
>> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
>> +               && GET_CODE (alt) == CONST_INT && alt != const0_rtx)
>So same comments on using CONST_INT_P and CONST0_RTX 
fixed

>> +        {
>> +          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
>> +          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +          rtx reg = gen_reg_rtx (mode);
>> +          rtx temp = GEN_INT (INTVAL (alt) - INTVAL (cons));
>> +          emit_insn (gen_rtx_SET (reg, temp));
>Use force_reg here rather than directly emitting the insn to initialize
>"reg".  What you're doing works when the difference is small but will
>not work when the difference does not fit into a signed 12bit value. 
fixed

>
>> +      /* imm + reg  */
>> +      else if (GET_CODE (cons) == CONST_INT && cons != const0_rtx
>> +               && GET_CODE (alt) == REG)
>Same comments about CONST_INT_P and CONST0_RTX.  And instead of using
>GET_CODE (object) == REG, use REG_P (object).
>
>
>> +        {
>> +          /* 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;
>> +            }
>Isn't this only valid for NE?
Here is what I didn't express clearly, please see the following patterns in zicond.md:

(define_insn "*czero.eqz.<GPR:mode><ANYI:mode>.opt2"
  [(set (match_operand:GPR 0 "register_operand"                   "=r")
        (if_then_else:GPR (eq (match_operand:ANYI 1 "register_operand" "r")
                              (const_int 0))
                          (match_operand:GPR 2 "register_operand" "r")
                          (match_operand:GPR 3 "register_operand" "1")))]
  "TARGET_ZICOND && rtx_equal_p (operands[1],  operands[3])"
  "czero.nez\t%0,%2,%1"
)
Note that the EQ in the pattern becomes the opposite nez in assembly instruction emission.

(define_insn "*czero.nez.<GPR:mode><ANYI:mode>.opt3"
  [(set (match_operand:GPR 0 "register_operand"                   "=r")
        (if_then_else:GPR (ne (match_operand:ANYI 1 "register_operand" "r")
                              (const_int 0))
                          (match_operand:GPR 2 "register_operand" "r")
                          (match_operand:GPR 3 "register_operand" "1")))]
  "TARGET_ZICOND && rtx_equal_p (operands[1], operands[3])"
  "czero.eqz\t%0,%2,%1"
)
Note that the NE in the pattern becomes the opposite eqz in assembly instruction emission.

Therefore, EQ/NE can be used here.

>Also use CONST0_RTX in that test. 
fixed

>
>> +          /* Handle the special situation of: -2048 == INTVAL (alt)
>> +             to avoid failure due to an unrecognized insn. Let the costing
>> +             model determine if the conditional move sequence is better
>> +             than the branching sequence.  */
>> +          if (-2048 == INTVAL (cons))
>So instead of checking for explicit values, we have SMALL_OPERAND to do
>it for us.  Also note that for !SMALL_OPERAND we can just force the
>value into a register using "force_reg" and all the right things will
>happen.
>
>So just add something like this to your original code:
>
>   if (!SMALL_OPERAND (INTVAL (cons))
>     cons = force_reg (mode, cons);
>
>That will result in CONS being either a simple integer constant (when it
>is suitable for addi) or a register (all other cases).  At that point
>you can use it in an arithmetic instruction. 
First, I modified the constraints of mov<mode>cc in riscv.md:
-                         (match_operand:GPR 2 "reg_or_0_operand")
+                         (match_operand:GPR 2 "sfb_alu_operand")
So both cons and alt will satisfy the condition: sfb_alu_operand.

The value of cons satisfies SMALL_OPERAND, but taking a negative
number on it may not satisfy SMALL_OPERAND. The only corner case here
is that is -2048 == INTVAL (cons). So in the corner case, it must be force_reg.
Then the state at this time becomes reg+reg. Rather than the case
imm+reg handled by this branch. So for the corner case I call
riscv_expand_conditional_move recursively.

Or, do you have a better suggestion for this place?
>
>
>
>> +      /* 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;
>> +            }
>So in this case isn't it only valid for EQ? 
As explained before, both EQ/NE are possible here.

>
>> +          if (-2048 == INTVAL (alt))
>Similar comment as above.  When ALT doesn't fit into a SMALL_OPERAND_P,
>just force it into a register.
>
>> +      /* reg + reg  */
>> +      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
>> +        {
>> +          /* Optimize for op0 == cons && op1 == const0_rtx.  */
>> +          if (op0 == cons && op1 == const0_rtx)
>> +            {
>> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +              emit_insn (gen_rtx_SET (dest,
>> +                                      gen_rtx_IF_THEN_ELSE (mode, cond,
>> +                                                            CONST0_RTX (mode),
>> +                                                            alt)));
>> +              return true;
>> +            }
>> +          /* Optimize for op0 == alt && op1 == const0_rtx.  */
>> +          if (op0 == alt && op1 == const0_rtx)
>> +            {
>> +              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
>> +              emit_insn (gen_rtx_SET (dest,
>> +                                      gen_rtx_IF_THEN_ELSE (mode, cond, cons,
>> +                                                            CONST0_RTX (mode))));
>> +              return true;
>> +            }
>I suspect we need some testing of the code in the two sequences above.
>I'd bet one is only valid for EQ and the other for NE. 
As explained above, they work fine.

>
>It's improving considerably. 
Your code review comments push this code to be better.

>
>WRT costing.  I'm still seeing some weirdness in how conditional moves
>are being costed from the if-converter.  I think your patch that set the
>cost to COSTS_N_INSNS (0) was just working around a deeper bug in the
>costing code.  I'm still trying to figure out the best way to clean that up. 
Yes, this place deserves deep thought.

By the way, the reason why I did this *total = COSTS_N_INSNS (0) is that
the result of seq_cost in ifcvt.cc is not feasible, specifically, the result is too
large. You can test, for a simple ADDI insn, if I remember correctly, its
result is 8. Perhaps, this result is no problem in the combine pass. As for
the Zicond's instruction, this value is larger than that of ADDI.
This causes the ifcvt pass to reject profitable optimizations at -Os.

>
>
>jeff

1 Let's clarify cost issue after v3 patch[3/5] is done.
2 v3 patch[3/5] address is: https://gcc.gnu.org/pipermail/gcc-patches/2023-August/626015.html

Thanks
Xiao Zeng
  

Patch

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 941ea25e1f2..6ac39f63dd7 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -3516,6 +3516,150 @@  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);
+          rtx reg = gen_reg_rtx (mode);
+          rtx temp = GEN_INT (INTVAL (alt) - INTVAL (cons));
+          emit_insn (gen_rtx_SET (reg, temp));
+          emit_insn (gen_rtx_SET (dest, gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                              CONST0_RTX (mode),
+                                                              reg)));
+          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;
+            }
+          /* Handle the special situation of: -2048 == INTVAL (alt)
+             to avoid failure due to an unrecognized insn. Let the costing
+             model determine if the conditional move sequence is better
+             than the branching sequence.  */
+          if (-2048 == INTVAL (cons))
+            {
+              rtx reg = gen_reg_rtx (mode);
+              emit_insn (gen_rtx_SET (reg, cons));
+              return riscv_expand_conditional_move (dest, op, reg, alt);
+            }
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          rtx temp = GEN_INT (-1 * INTVAL (cons));
+          riscv_emit_binary (PLUS, alt, alt, temp);
+          emit_insn (gen_rtx_SET (dest, gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                              CONST0_RTX (mode),
+                                                              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;
+            }
+          if (-2048 == INTVAL (alt))
+            {
+              rtx reg = gen_reg_rtx (mode);
+              emit_insn (gen_rtx_SET (reg, alt));
+              return riscv_expand_conditional_move (dest, op, cons, reg);
+            }
+          riscv_emit_int_compare (&code, &op0, &op1, need_eq_ne_p);
+          rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+          rtx temp = GEN_INT (-1 * INTVAL (alt));
+          riscv_emit_binary (PLUS, cons, cons, temp);
+          emit_insn (gen_rtx_SET (dest,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond, cons,
+                                                        CONST0_RTX (mode))));
+          riscv_emit_binary (PLUS, dest, dest, alt);
+          return true;
+        }
+      /* reg + reg  */
+      else if (GET_CODE (cons) == REG && GET_CODE (alt) == REG)
+        {
+          /* Optimize for op0 == cons && op1 == const0_rtx.  */
+          if (op0 == cons && op1 == const0_rtx)
+            {
+              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+              emit_insn (gen_rtx_SET (dest,
+                                      gen_rtx_IF_THEN_ELSE (mode, cond,
+                                                            CONST0_RTX (mode),
+                                                            alt)));
+              return true;
+            }
+          /* Optimize for op0 == alt && op1 == const0_rtx.  */
+          if (op0 == alt && op1 == const0_rtx)
+            {
+              rtx cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+              emit_insn (gen_rtx_SET (dest,
+                                      gen_rtx_IF_THEN_ELSE (mode, cond, cons,
+                                                            CONST0_RTX (mode))));
+              return true;
+            }
+          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 (mode),
+                                                        cons)));
+          emit_insn (gen_rtx_SET (reg1,
+                                  gen_rtx_IF_THEN_ELSE (mode, cond1,
+                                                        CONST0_RTX (mode),
+                                                        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 8d8fc93bb14..4b6875aa340 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -2484,9 +2484,9 @@ 
 (define_expand "mov<mode>cc"
   [(set (match_operand:GPR 0 "register_operand")
 	(if_then_else:GPR (match_operand 1 "comparison_operator")
-			  (match_operand:GPR 2 "reg_or_0_operand")
+			  (match_operand:GPR 2 "sfb_alu_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..6e45b3f8ae5
--- /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" "-Os"} } */
+
+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" } } */