https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111334

--- Comment #9 from Xi Ruoyao <xry111 at gcc dot gnu.org> ---
(In reply to chenglulu from comment #7)
> (In reply to Xi Ruoyao from comment #6)
> > (In reply to Xi Ruoyao from comment #5)
> > > (In reply to chenglulu from comment #3)
> > > > This involves the template <optab>di3_fake:
> > > > (define_insn "<optab>di3_fake"
> > > >   [(set (match_operand:DI 0 "register_operand" "=r,&r,&r")
> > > >         (sign_extend:DI
> > > >           (any_div:SI (match_operand:DI 1 "register_operand" "r,r,0")
> > > >                       (match_operand:DI 2 "register_operand" 
> > > > "r,r,r"))))]
> > > >   ""
> > > > {
> > > >   return loongarch_output_division ("<insn>.w<u>\t%0,%1,%2", operands);
> > > > }
> > > >   [(set_attr "type" "idiv")
> > > >    (set_attr "mode" "SI")
> > > >    (set (attr "enabled")
> > > >       (if_then_else
> > > >         (match_test "!!which_alternative == 
> > > > loongarch_check_zero_div_p()")
> > > >         (const_string "yes")
> > > >         (const_string "no")))])
> > > > 
> > > > 
> > > > I think there is a problem with the implementation of this template. 
> > > > First, the instructions generated in the template are [u]div.w[u], etc. 
> > > > The
> > > > description of such instructions in the instruction manual is that if 
> > > > the
> > > > upper 32 bits are not extended by the 31st bit sign then the result is
> > > > uncertain.
> > > 
> > > I think this reason alone makes the pattern looks very wrong.
> > > 
> > > I'll take a look...
> > 
> > Hmm, I guess we should just make di3_fake an UNSPEC because there is no way
> > to use div.w and its friends out of <optab:any_div><mode>3.
> 
> I agree with your idea, so I tried changing it to something like this.Do you
> think it's okay for me to change like this?
> 
> 
> (define_insn "<optab>di3_fake"
>   [(set (match_operand:DI 0 "register_operand" "=r,&r,&r")
>         (sign_extend:DI
>           (unspec:SI [(any_div:SI (match_operand:DI 1 "register_operand"
> "r,r,0")
>                       (match_operand:DI 2 "register_operand" "r,r,r"))]
>            UNSPEC_ANY_DIV)))]
>   ""
> {
>   return loongarch_output_division ("<insn>.w<u>\t%0,%1,%2", operands);
> }
>   [(set_attr "type" "idiv")
>    (set_attr "mode" "SI")
>    (set (attr "enabled")
>       (if_then_else
>         (match_test "!!which_alternative == loongarch_check_zero_div_p()")
>         (const_string "yes")
>         (const_string "no")))])

I'm not sure if we can use a single UNSPEC_ANY_DIV value for 4 different
operations (I'm afraid it will make a further CSE pass believe "a % b" is same
as "a / b" but maybe I'm wrong here).

I came up with a more detailed template:

diff --git a/gcc/config/loongarch/loongarch.md
b/gcc/config/loongarch/loongarch.md
index 75f641b38ee..d162013695f 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -64,6 +64,9 @@ (define_c_enum "unspec" [
   UNSPEC_CRC
   UNSPEC_CRCC

+  ;; {div,mod}.w{,u} with bad input
+  UNSPEC_BAD_DIVW
+
   UNSPEC_LOAD_FROM_GOT
   UNSPEC_PCALAU12I
   UNSPEC_ORI_L_LO12
@@ -880,7 +883,7 @@ (define_expand "<optab><mode>3"
                     (match_operand:GPR 2 "register_operand")))]
   ""
 {
- if (GET_MODE (operands[0]) == SImode)
+ if (TARGET_64BIT && GET_MODE (operands[0]) == SImode)
   {
     rtx reg1 = gen_reg_rtx (DImode);
     rtx reg2 = gen_reg_rtx (DImode);
@@ -917,10 +920,16 @@ (define_insn "*<optab><mode>3"

 (define_insn "<optab>di3_fake"
   [(set (match_operand:DI 0 "register_operand" "=r,&r,&r")
-       (sign_extend:DI
-         (any_div:SI (match_operand:DI 1 "register_operand" "r,r,0")
-                     (match_operand:DI 2 "register_operand" "r,r,r"))))]
-  ""
+       (if_then_else
+         (and (eq (match_operand:DI 1 "register_operand" "r,r,0")
+                  (sign_extend:DI (subreg:SI (match_dup 1) 0)))
+              (eq (match_operand:DI 2 "register_operand" "r,r,r")
+                  (sign_extend:DI (subreg:SI (match_dup 2) 0))))
+         (sign_extend:DI
+           (any_div:SI (subreg:SI (match_dup 1) 0)
+                       (subreg:SI (match_dup 2) 0)))
+         (unspec:DI [(const_int 0)] UNSPEC_BAD_DIVW)))]
+  "TARGET_64BIT"
 {
   return loongarch_output_division ("<insn>.w<u>\t%0,%1,%2", operands);
 }

Reply via email to