Bug 103266 - [12 regression] llvm-13 miscompilation: __builtin_assume_aligned causes over-aggressive dce
Summary: [12 regression] llvm-13 miscompilation: __builtin_assume_aligned causes over-...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 12.0
: P1 normal
Target Milestone: 12.0
Assignee: Jan Hubicka
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-11-15 19:24 UTC by Sergei Trofimovich
Modified: 2021-11-20 10:34 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-11-15 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sergei Trofimovich 2021-11-15 19:24:06 UTC
Noticed the bug initially on llvm-13 testsuite failure where 4 new tests fail when llvm is built with gcc-12:

  Failed Tests (4):
    LLVM :: CodeGen/AArch64/win64-jumptable.ll
    LLVM :: MC/AArch64/seh-packed-unwind.s
    LLVM :: tools/llvm-readobj/COFF/arm64-packed-symbol-name.yaml
    LLVM :: tools/llvm-readobj/COFF/arm64-packed-unwind.s

Here is the minimum reproducer extracted from llvm:

    $ cat llvm-readobj.cpp
    /*
     $ g++-12.0.0 -UBUGGY -O1 -std=c++14 -o a llvm-readobj.cpp && ./a
      # ok
      $ g++-12.0.0 -DBUGGY -O1 -std=c++14 -o a llvm-readobj.cpp && ./a
      Illegal instruction     (core dumped) ./a
    */

    typedef unsigned int u32;
    typedef unsigned char u8;

    static u32 pu8to32(const u8 * p8) __attribute__((noinline));
    static u32 pu8to32(const u8 * p8) {
      u32 v;
    
    #if BUGGY
      __builtin_memcpy(&v, __builtin_assume_aligned(p8, 1), sizeof(u32));
    #else
      __builtin_memcpy(&v,                          p8,     sizeof(u32));
    #endif
    
      return v;
    }
    
    int main(void) {
      // dse1 throws this store away
      u8 d[sizeof(u32)] = {
          0x07, 0x00, 0x00, 0x07,
      };
    
      if (pu8to32(d) != 0x07000007)
        __builtin_trap();
    }

Running:

$ g++-12.0.0 -UBUGGY -O1 -std=c++14 -o a llvm-readobj.cpp && ./a
# ok
$ g++-12.0.0 -DBUGGY -O1 -std=c++14 -o a llvm-readobj.cpp && ./a
Illegal instruction (core dumped)

It looks like the cause if failure is removed store to 'd':

--- g/a.S       2021-11-15 19:21:26.946265443 +0000
+++ b/a.S       2021-11-15 19:21:18.119173275 +0000
@@ -12,15 +12,14 @@
        .globl  main
        .type   main, @function
 main:
 .LFB1:
        .cfi_startproc
        subq    $16, %rsp
        .cfi_def_cfa_offset 24
-       movl    $117440519, 12(%rsp)
        leaq    12(%rsp), %rdi
        call    _ZL7pu8to32PKh
        cmpl    $117440519, %eax
        jne     .L5
        movl    $0, %eax
        addq    $16, %rsp
        .cfi_remember_state

Store is removed in '040t.dse1'. Could __builtin_assume_aligned() have problematic escape annotations?
Comment 1 Jakub Jelinek 2021-11-15 19:45:54 UTC
Started with r12-2353-g8da8ed435e9f01b37bf4ee57fa62509d44121c7d
Comment 2 Jan Hubicka 2021-11-15 21:15:20 UTC
Thanks for a nice testcase!

We use "1cX " fnspec string_for assume aligned which indeed is problematic since 'X' means that parameter is unused and thus we thus that it is not returned.
"1" however specifies that function returns first parameter which is bit in a contradiction with parameter being unused.  PTA codes takes precedence to "1" and thus works, while modref sees 'X' and ignore the parameters and thus propagation breaks.

In general we may have a specifier for pointer parameter that is used only as a scalar value and never read/written from.  Perhaps 'S'? However I can also teach modref to ignore unused for return handling.

Following untesed patch should fix the issue
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 7d0f61fc98b..baeaff6eb1f 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -11101,7 +11101,7 @@ builtin_fnspec (tree callee)
       case BUILT_IN_STACK_SAVE:
        return ".c";
       case BUILT_IN_ASSUME_ALIGNED:
-       return "1cX ";
+       return "1cW ";
       /* But posix_memalign stores a pointer into the memory pointed to
         by its first argument.  */
       case BUILT_IN_POSIX_MEMALIGN:
Comment 3 Jan Hubicka 2021-11-15 21:33:19 UTC
mine.
Comment 4 rguenther@suse.de 2021-11-16 07:26:01 UTC
On Mon, 15 Nov 2021, hubicka at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103266
> 
> Jan Hubicka <hubicka at gcc dot gnu.org> changed:
> 
>            What    |Removed                     |Added
> ----------------------------------------------------------------------------
>                  CC|                            |rguenther at suse dot de
> 
> --- Comment #2 from Jan Hubicka <hubicka at gcc dot gnu.org> ---
> Thanks for a nice testcase!
> 
> We use "1cX " fnspec string_for assume aligned which indeed is problematic
> since 'X' means that parameter is unused and thus we thus that it is not
> returned.
> "1" however specifies that function returns first parameter which is bit in a
> contradiction with parameter being unused.  PTA codes takes precedence to "1"
> and thus works, while modref sees 'X' and ignore the parameters and thus
> propagation breaks.
> 
> In general we may have a specifier for pointer parameter that is used only as a
> scalar value and never read/written from.  Perhaps 'S'? However I can also
> teach modref to ignore unused for return handling.

I think 'X' means simply not dereferenced or escaping since this was all
PTA based.  'S' would still eventually allow escaping.  But yes, PTA
simply takes '1' literally.  So the patch below is IMHO too pessimizing.
Can you please fixup modref instead?

> Following untesed patch should fix the issue
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 7d0f61fc98b..baeaff6eb1f 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -11101,7 +11101,7 @@ builtin_fnspec (tree callee)
>        case BUILT_IN_STACK_SAVE:
>         return ".c";
>        case BUILT_IN_ASSUME_ALIGNED:
> -       return "1cX ";
> +       return "1cW ";
>        /* But posix_memalign stores a pointer into the memory pointed to
>          by its first argument.  */
>        case BUILT_IN_POSIX_MEMALIGN:
> 
>
Comment 5 hubicka 2021-11-16 10:03:12 UTC
> I think 'X' means simply not dereferenced or escaping since this was all
> PTA based.  'S' would still eventually allow escaping.  But yes, PTA
> simply takes '1' literally.  So the patch below is IMHO too pessimizing.
> Can you please fixup modref instead?

If X is not meant to be "completely unused" (that is bit useless for
anotating bulitins I think since most of them shoul dbe sane) but all
the other flags together, we should update docs and eaf_flags production
which would fix the issue too.
Comment 6 rguenther@suse.de 2021-11-16 10:23:02 UTC
On Tue, 16 Nov 2021, hubicka at kam dot mff.cuni.cz wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103266
> 
> --- Comment #5 from hubicka at kam dot mff.cuni.cz ---
> > I think 'X' means simply not dereferenced or escaping since this was all
> > PTA based.  'S' would still eventually allow escaping.  But yes, PTA
> > simply takes '1' literally.  So the patch below is IMHO too pessimizing.
> > Can you please fixup modref instead?
> 
> If X is not meant to be "completely unused" (that is bit useless for
> anotating bulitins I think since most of them shoul dbe sane) but all
> the other flags together, we should update docs and eaf_flags production
> which would fix the issue too.

Well, it was "completely irrelevant" for PTA purposes ;)
Comment 7 Jan Hubicka 2021-11-18 17:08:48 UTC
I am testing
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index c94f0589d44..e5d2b11025a 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -2033,10 +2033,7 @@ modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
                                           bool indirect)
 {
   int index = SSA_NAME_VERSION (name);
-
-  /* If value is not returned at all, do nothing.  */
-  if (!direct && !indirect)
-    return;
+  bool returned_directly = false;
 
   /* If there is no return value, no flags are affected.  */
   if (!gimple_call_lhs (call))
@@ -2047,10 +2044,23 @@ modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
   if (arg >= 0)
     {
       int flags = gimple_call_return_flags (call);
-      if ((flags & ERF_RETURNS_ARG)
-         && (flags & ERF_RETURN_ARG_MASK) != arg)
-       return;
+      if (flags & ERF_RETURNS_ARG)
+       {
+         if ((flags & ERF_RETURN_ARG_MASK) == arg)
+           returned_directly = true;
+         else
+          return;
+       }
+    }
+  /* Make ERF_RETURNS_ARG overwrite EAF_UNUSED.  */
+  if (returned_directly)
+    {
+      direct = true;
+      indirect = false;
     }
+  /* If value is not returned at all, do nothing.  */
+  else if (!direct && !indirect)
+    return;
 
   /* If return value is SSA name determine its flags.  */
   if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
@@ -2273,11 +2283,13 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
                if (gimple_call_arg (call, i) == name)
                  {
                    int call_flags = gimple_call_arg_flags (call, i);
-                   if (!ignore_retval && !(call_flags & EAF_UNUSED))
+                   if (!ignore_retval)
                      merge_call_lhs_flags
                              (call, i, name,
-                              !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
-                              !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
+                              !(call_flags & (EAF_NOT_RETURNED_DIRECTLY
+                                              | EAF_UNUSED)),
+                              !(call_flags & (EAF_NOT_RETURNED_INDIRECTLY
+                                              | EAF_UNUSED)));
                    if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
                      {
                        call_flags = callee_to_caller_flags
Comment 8 CVS Commits 2021-11-18 17:42:06 UTC
The master branch has been updated by Jan Hubicka <hubicka@gcc.gnu.org>:

https://gcc.gnu.org/g:c331a75d49b6043399f5ccce72a02ccf3b0ddc56

commit r12-5379-gc331a75d49b6043399f5ccce72a02ccf3b0ddc56
Author: Jan Hubicka <jh@suse.cz>
Date:   Thu Nov 18 18:41:43 2021 +0100

    Fix modref wrt __builtin_assume_aligned
    
    __builtin_assume_aligned has bit contraictionary fnspec description "1cX "
    which means that parameter 1 is returned but also unused.  PTA code takes
    precedence to parameter being returned, while modref takes the info that
    parameter is unused.  This patch tweaks modref to follow PTA semantics (as
    suggested by Richard in the PR log)
    
    gcc/ChangeLog:
    
    2021-11-18  Jan Hubicka  <hubicka@ucw.cz>
    
            PR ipa/103266
            * ipa-modref.c (modref_eaf_analysis::merge_call_lhs_flags): Unused
            parameter may still be returned.
            (modref_eaf_analysis::analyze_ssa_name): Call merge_call_lhs_flags
            even for unused function args.
    
    gcc/testsuite/ChangeLog:
    
    2021-11-18  Jan Hubicka  <hubicka@ucw.cz>
    
            PR ipa/103266
            * g++.dg/torture/pr103266.C: New test.
Comment 9 Jan Hubicka 2021-11-20 10:34:41 UTC
fixed.