On Fri, Jan 31, 2025 at 02:29:57PM +0100, Jakub Jelinek wrote:
> >     }
> >   else
> >     {
> >       tree fntype1 = gimple_call_fntype (s1);
> >       tree fntype2 = gimple_call_fntype (s2);
> > 
> >       if ((fntype1 && !fntype2)
> >           || (!fntype1 && fntype2)
> >           || (fntype1 && !types_compatible_p (fntype1, fntype2)))
> >         return return_false_with_msg ("call function types are not 
> > compatible");
> >     }
> > 
> > I think in the else { fntype1 and fntype2 should never be NULL and thus
> > this should simplify even more.
> 
> This isn't possible because fntype{1,2} are used later on in the function;
> sure, that
>   if (fntype1 && fntype2 && comp_type_attributes (fntype1, fntype2) != 1)
>     return return_false_with_msg ("different fntype attributes");
> can be moved into the else, but the new checks to determine which args to
> check still use that.

So like this then (if it passes bootstrap/regtest)?

2025-01-31  Jakub Jelinek  <ja...@redhat.com>

        PR ipa/117432
        * ipa-icf-gimple.cc (func_checker::compare_asm_inputs_outputs):
        Also return_false if operands have incompatible types.
        (func_checker::compare_gimple_call): Check fntype1 vs. fntype2
        compatibility for all non-internal calls and assume fntype1 and
        fntype2 are non-NULL for those.  For calls to non-prototyped
        calls or for stdarg_p functions after the last named argument (if any)
        check type compatibility of call arguments.

        * gcc.c-torture/execute/pr117432.c: New test.
        * gcc.target/i386/pr117432.c: New test.

--- gcc/ipa-icf-gimple.cc.jj    2025-01-30 18:29:22.237190471 +0100
+++ gcc/ipa-icf-gimple.cc       2025-01-31 15:25:57.168535197 +0100
@@ -459,7 +459,9 @@ func_checker::compare_asm_inputs_outputs
        return false;
 
       if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2),
-                           get_operand_access_type (map, t1)))
+                           get_operand_access_type (map, t1))
+         || !types_compatible_p (TREE_TYPE (TREE_VALUE (t1)),
+                                 TREE_TYPE (TREE_VALUE (t2))))
        return return_false ();
 
       tree p1 = TREE_PURPOSE (t1);
@@ -709,26 +711,37 @@ func_checker::compare_gimple_call (gcall
       || gimple_call_alloca_for_var_p (s1) != gimple_call_alloca_for_var_p 
(s2))
     return false;
 
-  if (gimple_call_internal_p (s1)
-      && gimple_call_internal_fn (s1) != gimple_call_internal_fn (s2))
-    return false;
-
-  tree fntype1 = gimple_call_fntype (s1);
-  tree fntype2 = gimple_call_fntype (s2);
-
-  /* For direct calls we verify that types are compatible so if we matched
-     callees, callers must match, too.  For indirect calls however verify
-     function type.  */
-  if (!gimple_call_fndecl (s1))
+  unsigned check_arg_types_from = 0;
+  if (gimple_call_internal_p (s1))
     {
-      if ((fntype1 && !fntype2)
-         || (!fntype1 && fntype2)
-         || (fntype1 && !types_compatible_p (fntype1, fntype2)))
-       return return_false_with_msg ("call function types are not compatible");
+      if (gimple_call_internal_fn (s1) != gimple_call_internal_fn (s2))
+       return false;
     }
+  else
+    {
+      tree fntype1 = gimple_call_fntype (s1);
+      tree fntype2 = gimple_call_fntype (s2);
+      if (!types_compatible_p (fntype1, fntype2))
+       return return_false_with_msg ("call function types are not compatible");
+
+      if (comp_type_attributes (fntype1, fntype2) != 1)
+       return return_false_with_msg ("different fntype attributes");
 
-  if (fntype1 && fntype2 && comp_type_attributes (fntype1, fntype2) != 1)
-    return return_false_with_msg ("different fntype attributes");
+      check_arg_types_from = gimple_call_num_args (s1);
+      if (!prototype_p (fntype1) || !prototype_p (fntype2))
+       check_arg_types_from = 0;
+      else if (stdarg_p (fntype1))
+       {
+         check_arg_types_from = list_length (TYPE_ARG_TYPES (fntype1));
+         if (stdarg_p (fntype2))
+           {
+             unsigned n = list_length (TYPE_ARG_TYPES (fntype2));
+             check_arg_types_from = MIN (check_arg_types_from, n);
+           }
+       }
+      else if (stdarg_p (fntype2))
+       check_arg_types_from = list_length (TYPE_ARG_TYPES (fntype2));
+    }
 
   tree chain1 = gimple_call_chain (s1);
   tree chain2 = gimple_call_chain (s2);
@@ -746,6 +759,10 @@ func_checker::compare_gimple_call (gcall
 
       if (!compare_operand (t1, t2, get_operand_access_type (&map, t1)))
        return return_false_with_msg ("GIMPLE call operands are different");
+      if (i >= check_arg_types_from
+         && !types_compatible_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+       return return_false_with_msg ("GIMPLE call operand types are "
+                                     "different");
     }
 
   /* Return value checking.  */
--- gcc/testsuite/gcc.c-torture/execute/pr117432.c.jj   2025-01-31 
15:14:54.358852495 +0100
+++ gcc/testsuite/gcc.c-torture/execute/pr117432.c      2025-01-31 
15:14:54.358852495 +0100
@@ -0,0 +1,71 @@
+/* PR ipa/117432 */
+
+#include <stdarg.h>
+
+long long r;
+
+__attribute__((noipa)) void
+baz (int tag, ...)
+{
+  va_list ap;
+  va_start (ap, tag);
+  if (!r)
+    r = va_arg (ap, long long);
+  else
+    r = va_arg (ap, int);
+  va_end (ap);
+}
+
+void
+foo (void)
+{
+  baz (1, -1, 0);
+}
+
+void
+bar (void)
+{
+  baz (1, -1LL, 0);
+}
+
+__attribute__((noipa)) void
+qux (...)
+{
+  va_list ap;
+  va_start (ap);
+  if (!r)
+    r = va_arg (ap, long long);
+  else
+    r = va_arg (ap, int);
+  va_end (ap);
+}
+
+void
+corge (void)
+{
+  qux (-2, 0);
+}
+
+void
+fred (void)
+{
+  qux (-2LL, 0);
+}
+
+int
+main ()
+{
+  bar ();
+  if (r != -1LL)
+    __builtin_abort ();
+  foo ();
+  if (r != -1)
+    __builtin_abort ();
+  r = 0;
+  fred ();
+  if (r != -2LL)
+    __builtin_abort ();
+  corge ();
+  if (r != -2)
+    __builtin_abort ();
+}
--- gcc/testsuite/gcc.target/i386/pr117432.c.jj 2025-01-31 15:14:54.358852495 
+0100
+++ gcc/testsuite/gcc.target/i386/pr117432.c    2025-01-31 15:14:54.358852495 
+0100
@@ -0,0 +1,17 @@
+/* PR ipa/117432 */
+/* { dg-do compile { target lp64 } } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "# myinsn %rax" } } */
+/* { dg-final { scan-assembler "# myinsn %eax" } } */
+
+void
+foo (void)
+{
+  asm volatile ("# myinsn %0" : : "r" (-42L));
+}
+
+void
+bar (void)
+{
+  asm volatile ("# myinsn %0" : : "r" (-42));
+}


        Jakub

Reply via email to