Bug 92123 - [F2018/array-descriptor] Scalar allocatable/pointer with array descriptor (via bind(C)): ICE with select rank or error scalar variable with POINTER or ALLOCATABLE in procedure with BIND(C) is not yet supported
Summary: [F2018/array-descriptor] Scalar allocatable/pointer with array descriptor (v...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Paul Thomas
URL:
Keywords: ice-on-valid-code, rejects-valid
Depends on:
Blocks:
 
Reported: 2019-10-16 13:08 UTC by Tobias Burnus
Modified: 2020-01-31 10:54 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-11-03 00:00:00


Attachments
A draft fix for the PR (1.14 KB, patch)
2019-11-03 08:36 UTC, Paul Thomas
Details | Diff
Patch to fix problem with ISO_Fortran_binding_15.* (639 bytes, patch)
2019-11-14 11:11 UTC, Paul Thomas
Details | Diff
A better fix (613 bytes, patch)
2019-11-14 12:23 UTC, Paul Thomas
Details | Diff
gcc10-fnspec-test.patch (1005 bytes, patch)
2019-11-27 13:54 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Burnus 2019-10-16 13:08:31 UTC
Found on https://mailman.j3-fortran.org/pipermail/j3/2019-October/011669.html

The following shall use the array descriptor – and should work (TM):

  subroutine Fsub( dat ) bind(C, name="Fsub")
    integer(c_int), allocatable, intent(out) :: dat

Currently, it fails with:
  Error: Scalar variable ‘dat’ at (1) with POINTER or ALLOCATABLE in procedure ‘fsub’ with BIND(C) is not yet supported

Test program, see link to the J3 mailing list.


 * * * 

If one uses assumed-rank variables and adds 'select rank', it give an ICE. That is with
    subroutine fsub( dat ) bind(C, name="fsub")
      integer(c_int), allocatable, intent(out) :: dat(..)    
      select rank (dat)
         rank (0)
         !dat = 42          !<-- A
         allocate( dat ) ; dat = 42      !<--- B
      end select 

It ICEs in gfc_conv_descriptor_data_get, at fortran/trans-array.c:145

 Breakpoint 3, trans_associate_var (sym=0x24d8ed0, block=0x7fffffffd5f0) at ../../repos/gcc/gcc/fortran/trans-stmt.c:1845
1845                  tmp = gfc_conv_descriptor_data_get (desc);

(gdb) p debug_tree(desc)
 <var_decl 0x7ffff7fc7ab0 __tmp_INTEGER_4_rank_0
    type <pointer_type 0x7ffff77429d8
        type <integer_type 0x7ffff773b5e8 integer(kind=4) public SI

which is a scalar without array descriptor.
Comment 1 Paul Thomas 2019-11-03 08:36:49 UTC
Created attachment 47158 [details]
A draft fix for the PR

Except for bind_c_usage_3.f03, which fails for obvious reasons, this patch regtests.

I must test if the branch in trans-stmt.c is necessary and that the code is not standard defying. I cannot see any reason why it should not be conforming but the originator must have put the errors in there for a reason.

Cheers

Paul
Comment 2 Tobias Burnus 2019-11-07 16:01:56 UTC
Submitted and approved patch: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00072.html – still to be committed by the author.
Comment 3 Paul Thomas 2019-11-10 18:33:31 UTC
Author: pault
Date: Sun Nov 10 18:33:00 2019
New Revision: 278025

URL: https://gcc.gnu.org/viewcvs?rev=278025&root=gcc&view=rev
Log:
2019-11-10  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/92123
	*decl.c (gfc_verify_c_interop_param): Remove error asserting
	that pointer or allocatable variables in a bind C procedure are
	not supported. Delete some trailing spaces.
	* trans-stmt.c (trans_associate_var): Correct the attempt to
	treat scalar pointer or allocatable temporaries as if they are
	array descriptors.

2019-11-10  Paul Thomas  <pault@gcc.gnu.org>

	PR fortran/92123
	* gfortran.dg/bind_c_procs_3.f90 : New test.
	* gfortran.dg/ISO_Fortran_binding_15.c : New test.
	* gfortran.dg/ISO_Fortran_binding_15.f90 : Additional source.


Added:
    trunk/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.c
    trunk/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90
    trunk/gcc/testsuite/gfortran.dg/bind_c_procs_3.f90
Modified:
    trunk/gcc/fortran/ChangeLog
    trunk/gcc/fortran/decl.c
    trunk/gcc/fortran/trans-stmt.c
    trunk/gcc/testsuite/ChangeLog
Comment 4 Thomas Schwinge 2019-11-11 10:02:33 UTC
(In reply to Paul Thomas from comment #3)
>     trunk/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90

On x86_64-pc-linux-gnu for '-m32' multilib testing, I'm seeing all execution tests FAIL (no message given), and likewise for powerpc64le-unknown-linux-gnu, but '-O0' only.
Comment 5 Jakub Jelinek 2019-11-12 10:10:01 UTC
Yeah, I think on x86_64-linux it works by pure accident.
The type used on the C side is:
typedef struct CFI_cdesc_t
 {
    void *base_addr;
    size_t elem_len;
    int version;
    CFI_rank_t rank;
    CFI_attribute_t attribute;
    CFI_type_t type;
    CFI_dim_t dim[];
 }
CFI_cdesc_t;

where void * and size_t are 64-bit on LP64 and 32-bit on ILP32, CFI_rank_t/CFI_attribute_t are 8-bit and CFI_type_t is 16-bit, while for
    integer(c_int), allocatable, intent(out) :: dat(..)
    select rank (dat)
      rank (0)
      allocate( dat )
      dat = 42
    end select
    return
seems to assume dat type is a structure containing pointer sized data,
array index offset, and dtype, which has size_t elem_len, int version, 8-bit rank, type and 16-bit attribute, so the Fortran FE assumption is there is extra 64-bit (or 32-bit) offset and type/attribute are swapped and have different types.
Which means from the C side, rank is at offset 20 bytes into the structure for LP64 and 12 bytes in ILP32, while the Fortran emitted code when it reads dat->dtype.rank reads it from offset 28 bytes into the structure or for ILP32 16 bytes.
Comment 6 Tobias Burnus 2019-11-12 10:23:01 UTC
Some side remarks:

(In reply to Jakub Jelinek from comment #5)
> The type used on the C side is:
This type is described in the Fortran standard; for Fortran 2018, it is described in "18.5.3  The CFI_cdesc_t structure type" on page 481, cf. https://j3-fortran.org/doc/year/18/18-007r1.pdf

That's defined on the gfortran side in libgfortran/ISO_Fortran_binding.h

> typedef struct CFI_cdesc_t
>  {
>     void *base_addr;
>     size_t elem_len;> where void * and size_t are 64-bit on LP64 and 32-bit on ILP32,
> CFI_rank_t/CFI_attribute_t are 8-bit and CFI_type_t is 16-bit, while for
>     integer(c_int), allocatable, intent(out) :: dat(..)> seems to assume dat type is a structure containing pointer sized data,
> array index offset, and dtype, which has size_t elem_len, int version, 8-bit
> rank, type and 16-bit attribute, so the Fortran FE assumption is there is
> extra 64-bit (or 32-bit) offset and type/attribute are swapped and have
> different types.

The conversion between gfortran's internal array descriptor and the one use with C binding ("CFI_") is done in libgfortran/runtime/ISO_Fortran_binding.c via cfi_desc_to_gfc_desc and gfc_desc_to_cfi_desc; this function is called from the Fortran side.  (When calling a bind(C) function or for implementing in Fortran a bind(C) function.)

The C side calls CFI_establish for a likewise purpose (same file).
Comment 7 Jakub Jelinek 2019-11-12 10:37:27 UTC
I don't see any conversion function to be called:
fsub (struct array15_integer(kind=4) & restrict dat)
{
  {
    integer(kind=4) * __tmp_INTEGER_4_rank_0;

    {
      signed char D.3928;
      integer(kind=8) D.3929;
      signed char D.3930;

      D.3928 = dat->dtype.rank;
      D.3929 = (integer(kind=8)) D.3928 + -1;
      D.3930 = D.3928 != 0 ? dat->dim[D.3929].ubound != -1 ? D.3928 : -1 : D.3928;
      if (D.3930 == 0)
        {
          try
            {
              __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) dat->data;
              if (__tmp_INTEGER_4_rank_0 != 0B)
                {
                  _gfortran_runtime_error_at (&"At line 15 of file /home/jakub/src/gcc/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90"[1]{lb: 1 sz: 1}, &"Attempting to allocate already allocated variable \'%s\'"[1]{lb: 1 sz: 1}, &"__tmp_INTEGER_4_rank_0"[1]{lb: 1 sz: 1});
                }
              else
                {
                  __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) __builtin_malloc (4);
                  if (__tmp_INTEGER_4_rank_0 == 0B)
                    {
                      _gfortran_os_error_at (&"In file \'/home/jakub/src/gcc/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90\', around line 16"[1]{lb: 1 sz: 1}, &"Error allocating %lu bytes"[1]{lb: 1 sz: 1}, 4);
                    }
                }
              if (__tmp_INTEGER_4_rank_0 != 0B) goto L.4;
              __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) __builtin_malloc (4);
              L.4:;
              *__tmp_INTEGER_4_rank_0 = 42;
              L.3:;
            }
          finally
            {
              dat->data = (void * restrict) __tmp_INTEGER_4_rank_0;
            }
          goto L.2;
        }
    }
    L.2:;
    L.1:;
    return;
  }
}
Comment 8 paul.richard.thomas@gmail.com 2019-11-12 17:31:29 UTC
Hi Jakub,

Thanks for spotting that. For whatever reason,

* trans-decl.c (gfc_get_symbol_decl): Assumed shape and assumed
rank dummies of bind C procs require deferred initialization.

in the patch for PR89843 has just disappeared. I'll have to do a bit
of detective work to find out how or why and come up with a fix.

Regards

Paul

On Tue, 12 Nov 2019 at 10:37, jakub at gcc dot gnu.org
<gcc-bugzilla@gcc.gnu.org> wrote:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92123
>
> --- Comment #7 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
> I don't see any conversion function to be called:
> fsub (struct array15_integer(kind=4) & restrict dat)
> {
>   {
>     integer(kind=4) * __tmp_INTEGER_4_rank_0;
>
>     {
>       signed char D.3928;
>       integer(kind=8) D.3929;
>       signed char D.3930;
>
>       D.3928 = dat->dtype.rank;
>       D.3929 = (integer(kind=8)) D.3928 + -1;
>       D.3930 = D.3928 != 0 ? dat->dim[D.3929].ubound != -1 ? D.3928 : -1 :
> D.3928;
>       if (D.3930 == 0)
>         {
>           try
>             {
>               __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) dat->data;
>               if (__tmp_INTEGER_4_rank_0 != 0B)
>                 {
>                   _gfortran_runtime_error_at (&"At line 15 of file
> /home/jakub/src/gcc/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90"[1]{lb:
> 1 sz: 1}, &"Attempting to allocate already allocated variable \'%s\'"[1]{lb: 1
> sz: 1}, &"__tmp_INTEGER_4_rank_0"[1]{lb: 1 sz: 1});
>                 }
>               else
>                 {
>                   __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) __builtin_malloc
> (4);
>                   if (__tmp_INTEGER_4_rank_0 == 0B)
>                     {
>                       _gfortran_os_error_at (&"In file
> \'/home/jakub/src/gcc/gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90\',
> around line 16"[1]{lb: 1 sz: 1}, &"Error allocating %lu bytes"[1]{lb: 1 sz: 1},
> 4);
>                     }
>                 }
>               if (__tmp_INTEGER_4_rank_0 != 0B) goto L.4;
>               __tmp_INTEGER_4_rank_0 = (integer(kind=4) *) __builtin_malloc
> (4);
>               L.4:;
>               *__tmp_INTEGER_4_rank_0 = 42;
>               L.3:;
>             }
>           finally
>             {
>               dat->data = (void * restrict) __tmp_INTEGER_4_rank_0;
>             }
>           goto L.2;
>         }
>     }
>     L.2:;
>     L.1:;
>     return;
>   }
> }
>
> --
> You are receiving this mail because:
> You are on the CC list for the bug.
> You are the assignee for the bug.
Comment 9 Paul Thomas 2019-11-14 11:11:02 UTC
Created attachment 47258 [details]
Patch to fix problem with ISO_Fortran_binding_15.*

Hi Jakub and Tobias,

Restoring the call to trans-decl.c(convert_CFI_desc) fixes the problem.

The TREE_STATIC for the two pointers is required for -O1 and above so that they are available in the finally block. I tried various other attributes without success. Is there a better way of doing this?

Cheers

Paul
Comment 10 Paul Thomas 2019-11-14 12:23:17 UTC
Created attachment 47261 [details]
A better fix

Hi Jakub and Tobias,

It turns out that the TREE_STATIC is only required for the gfc descriptor and not the pointers.

Is this OK?

Cheers

Paul
Comment 11 Jakub Jelinek 2019-11-14 12:31:01 UTC
The TREE_STATIC looks wrong to me, it will misbehave if the function is called recursively, or if the function is called several times concurrently (say in OpenMP).
If the reason for TREE_STATIC is that the artificial var is being declared in some inner scope and then used outside of that scope, perhaps it needs to be declared in some outer scope, perhaps in the outermost scope of the function.
Comment 12 Tobias Burnus 2019-11-14 14:46:08 UTC
(all related to gcc/testsuite/gfortran.dg/ISO_Fortran_binding_15.f90)

I tried - with Paul's attached patch (attachment 47261 [details])
   DECL_CONTEXT (gfc_desc) = current_function_decl;
instead of TREE_STATIC - but I have the feeling it is unrelated to the real problem. Or at least, if I have this, I observe the following:

If I remove the 'allocate(dat)', which is not needed as Fortran does (re)allocate on assignment, it works (-m64 -O1).

However, with the 'allocate(dat)' kept, there are two mallocs – and GCC will remove the assignment of '42'. Namely:

With -O0, I see in -fdump-tree-optimized:
  gfc_desc_ptr.1_15 = &gfc_desc.0;
  CFI_desc_ptr.2 = dat_12(D);
  _gfortran_cfi_desc_to_gfc_desc (gfc_desc_ptr.1_15, &CFI_desc_ptr.2);
  dat_18 = gfc_desc_ptr.1_15;

  __tmp_INTEGER_4_rank_0_26 = dat_3->data;
  __tmp_INTEGER_4_rank_0_28 = __builtin_malloc (4);
  __tmp_INTEGER_4_rank_0_30 = __builtin_malloc (4);

  # __tmp_INTEGER_4_rank_0_4 = PHI <__tmp_INTEGER_4_rank_0_28(15), __tmp_INTEGER_4_rank_0_30(16)>
  *__tmp_INTEGER_4_rank_0_4 = 42;    /// <<<<<<<<<<<<
  dat_3->data = __tmp_INTEGER_4_rank_0_4;

  _gfortran_gfc_desc_to_cfi_desc (&CFI_desc_ptr.2, gfc_desc_ptr.1_5);

But with -O1, one has:
  CFI_desc_ptr.2 = dat_14(D);
  _gfortran_cfi_desc_to_gfc_desc (&gfc_desc.0, &CFI_desc_ptr.2);
  # DEBUG dat => &gfc_desc.0

  __tmp_INTEGER_4_rank_0_20 = gfc_desc.0.data;
  __tmp_INTEGER_4_rank_0_22 = __builtin_malloc (4);
  gfc_desc.0.data = __tmp_INTEGER_4_rank_0_22;
  _gfortran_gfc_desc_to_cfi_desc (&CFI_desc_ptr.2, &gfc_desc.0);
Comment 13 Jakub Jelinek 2019-11-25 12:14:00 UTC
Yeah, I believe we don't need TREE_STATIC.  For DECL_CONTEXT, isn't the context set already by pushdecl that gfc_create_var calls?
The reason why the *... = 42; store is removed is I believe invalid fn spec:
  gfor_fndecl_cfi_to_gfc = gfc_build_library_function_decl_with_spec (
        get_identifier (PREFIX("cfi_desc_to_gfc_desc")), ".ww",
        void_type_node, 2, pvoid_type_node, ppvoid_type_node);

  gfor_fndecl_gfc_to_cfi = gfc_build_library_function_decl_with_spec (
        get_identifier (PREFIX("gfc_desc_to_cfi_desc")), ".wR",
        void_type_node, 2, ppvoid_type_node, pvoid_type_node);

The first one looks weird, reading libgfortran sources, it doesn't look like the second argument is ever stored into by the function, so shouldn't it be ".wr" instead?  And the ".wR" seems to be the reason why the testcase is miscompiled with the #c10 patch minus TREE_STATIC hunk.  R means that the argument is not written into (correct), that it does not escape (I'd say incorrect) and that it is only accessed directly, not indirectly.
With incremental:
--- gcc/fortran/trans-decl.c.jj	2019-11-11 21:04:05.217259240 +0100
+++ gcc/fortran/trans-decl.c	2019-11-25 12:54:31.577424800 +0100
@@ -3739,11 +3742,11 @@ gfc_build_builtin_function_decls (void)
 	void_type_node, 2, pvoid_type_node, pvoid_type_node);
 
   gfor_fndecl_cfi_to_gfc = gfc_build_library_function_decl_with_spec (
-	get_identifier (PREFIX("cfi_desc_to_gfc_desc")), ".ww",
+	get_identifier (PREFIX("cfi_desc_to_gfc_desc")), ".wr",
 	void_type_node, 2, pvoid_type_node, ppvoid_type_node);
 
   gfor_fndecl_gfc_to_cfi = gfc_build_library_function_decl_with_spec (
-	get_identifier (PREFIX("gfc_desc_to_cfi_desc")), ".wR",
+	get_identifier (PREFIX("gfc_desc_to_cfi_desc")), ".wr",
 	void_type_node, 2, ppvoid_type_node, pvoid_type_node);
 
   gfor_fndecl_associated = gfc_build_library_function_decl_with_spec (
it seems to work.  That r in there pretends it reads also e.g. *GFC_DESCRIPTOR_DATA (s) and thus keeps the *... = 42; store.  Though, in reality, for both functions what the second argument points to (the data pointed by the descriptor) escapes, it doesn't escape to some global memory, but to the pointer in the structure pointed by the first argument, in the second call e.g.
void
gfc_desc_to_cfi_desc (CFI_cdesc_t **d_ptr, const gfc_array_void *s)
{
  CFI_cdesc_t *d;
  if (*d_ptr == NULL)
    d = malloc (...);
  else
    d = *d_ptr;
  d->base_addr = s->base_addr;
  ...
  if (*d_ptr == NULL)
    *d_ptr = d;
}

Richard, is ".wr" ok for that, even when it is lying, or should it be
".w." ?  I think there is no letter for doesn't store anything to the memory directly and doesn't escape the address directly, but does escape the content and thus indirect addresses.
Comment 14 Richard Biener 2019-11-25 12:26:07 UTC
Well, lying means that for non-escaped desctiptors A and B doing

  A.data = malloc();
  gfc_desc_to_cfi_desc (&B, &A)

B.data and A.data are not considered aliasing.

So I'd recommend to not lie here.  Yes, there's a letter variant missing
for 'r' plus the direct object not escaping.  I can see how that might
be useful indeed (but with points-to constructing a testcase where this
makes a difference won't be easy).

So I'd recommend ".w." for correctness, maybe with a comment about what
we can guarantee (we also have no "letter" to say indirect pointed-to
objects transfer from one argument to another, likely usable for
memcpy-like functions as well for example)
Comment 15 Jakub Jelinek 2019-11-25 13:04:38 UTC
I think other fn spec attributes in trans-decl.c should be checked.
E.g. for internal_pack, I see ".r", when the function sometimes returns a pointer to a field pointed by the first argument.  The address of the descriptor doesn't escape then, but there is indirect escape.  What about internal_unpack?
Both cfi_desc_to_gfc_desc and gfc_desc_to_cfi_desc should be ".w." as Richi said.
Comment 16 Thomas Koenig 2019-11-26 16:54:37 UTC
Is there a specification (or even description) for fn spec somewhere?
I can't say I understand exactly what it does.
Comment 17 Tobias Burnus 2019-11-26 17:10:28 UTC
(In reply to Thomas Koenig from comment #16)
> Is there a specification (or even description) for fn spec somewhere?
> I can't say I understand exactly what it does.

Maybe gimple.c's gimple_call_arg_flags:
    case 'x':
    case 'X':
      return EAF_UNUSED;

    case 'R':
      return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE;

    case 'r':
      return EAF_NOCLOBBER | EAF_NOESCAPE;

    case 'W':
      return EAF_DIRECT | EAF_NOESCAPE;

    case 'w':
      return EAF_NOESCAPE;

    case '.':
    default:
      return 0;

+ tree-into-ssa.c's pass_build_ssa::execute
          if (TREE_STRING_POINTER (fnspec)[i]  == 'R'
              || TREE_STRING_POINTER (fnspec)[i] == 'r')
            {
              tree name = ssa_default_def (fun, arg);
              if (name)
                SSA_NAME_POINTS_TO_READONLY_MEMORY (name) = 1;
            }

+ decl_return_flags in calls.c:
  switch (TREE_STRING_POINTER (attr)[0])
    {
    case '1':
    case '2':
    case '3':
    case '4':
      return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');

    case 'm':
      return ERF_NOALIAS;

    case '.':
    default:
      return 0;
    }

The constants are defined in tree-core.h.

The name 'fn spec' contains a space to make it only internally available, hence, also not well documented.
Comment 18 Jakub Jelinek 2019-11-26 17:16:31 UTC
https://gcc.gnu.org/ml/gcc-patches/2010-04/msg00895.html
contained documentation but in the end we went with an internal "fn spec" attribute rather than user visible fnspec:
https://gcc.gnu.org/ml/gcc-patches/2010-05/msg00383.html
Comment 19 Richard Biener 2019-11-27 07:50:13 UTC
There is also tree-core.h:

/* Call argument flags.  */
/* Nonzero if the argument is not dereferenced recursively, thus only
   directly reachable memory is read or written.  */
#define EAF_DIRECT              (1 << 0)

/* Nonzero if memory reached by the argument is not clobbered.  */
#define EAF_NOCLOBBER           (1 << 1)

/* Nonzero if the argument does not escape.  */
#define EAF_NOESCAPE            (1 << 2)

/* Nonzero if the argument is not used by the function.  */
#define EAF_UNUSED              (1 << 3)

/* Call return flags.  */
/* Mask for the argument number that is returned.  Lower two bits of
   the return flags, encodes argument slots zero to three.  */
#define ERF_RETURN_ARG_MASK     (3)

/* Nonzero if the return value is equal to the argument number
   flags & ERF_RETURN_ARG_MASK.  */
#define ERF_RETURNS_ARG         (1 << 2)

but eventually specifying some terminology and then consistently using that
might help...  Eventually the documentation bits could go to the internals
manual somewhere ('no vops' is also not documented).
Comment 20 Jakub Jelinek 2019-11-27 13:54:28 UTC
Created attachment 47377 [details]
gcc10-fnspec-test.patch

Just for archival purposes, here is a short gcc plugin that allows testing "fn spec" attribute (on direct function calls only, not on indirect calls), by registering a fn_spec attribute and remapping it to "fn spec" when finish_decl is called.
Comment 21 paul.richard.thomas@gmail.com 2019-11-28 07:45:41 UTC
Hi All,

I took one of the other fn_spec's as a template - it might well have
been internal_pack.

Thanks for looking at this.

Cheers

Paul

On Mon, 25 Nov 2019 at 13:04, jakub at gcc dot gnu.org
<gcc-bugzilla@gcc.gnu.org> wrote:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92123
>
> --- Comment #15 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
> I think other fn spec attributes in trans-decl.c should be checked.
> E.g. for internal_pack, I see ".r", when the function sometimes returns a
> pointer to a field pointed by the first argument.  The address of the
> descriptor doesn't escape then, but there is indirect escape.  What about
> internal_unpack?
> Both cfi_desc_to_gfc_desc and gfc_desc_to_cfi_desc should be ".w." as Richi
> said.
>
> --
> You are receiving this mail because:
> You are on the CC list for the bug.
> You are the assignee for the bug.
Comment 22 rguenther@suse.de 2019-11-28 08:16:10 UTC
On Wed, 27 Nov 2019, jakub at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92123
> 
> --- Comment #20 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
> Created attachment 47377 [details]
>   --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=47377&action=edit
> gcc10-fnspec-test.patch
> 
> Just for archival purposes, here is a short gcc plugin that allows testing "fn
> spec" attribute (on direct function calls only, not on indirect calls), by
> registering a fn_spec attribute and remapping it to "fn spec" when finish_decl
> is called.

I guess sth like -fenable-internal-attributes registering both
fn_spec and no_vops would be nice for testing those.  Or better
an (undocumented) --param.  There's also "asan odr inidicator"
and various "omp declare .." ones, eventually generically adjusting
matching of the attribute names with the param/flag set can be done.
Comment 23 Tobias Burnus 2019-11-28 10:28:40 UTC
I have the feeling that some other use also disagrees between ME and FE/Fortran semantics assumptions.

I just run into PR 92703: if one comments the unrelated 'foo', with -O0 one gets the expected 'stop 2' but with -O1 one gets 'stop 21' as (effectively) the 'class.20._data = &var;' has been optimized away. — For that PR, to properly handle Fortran semantic, a copy of 'var' had to be created and used instead. I think that would have solved the alias/ME problem for *that* usage/test case.

Still, I fear that similar test cases can be created where for -O0 the executable produces the correct result – but where with optimization, the result will be wrong.

Based on the test case in PR 92703, I wonder about types like:
  type t
    integer, allocatable :: A
    integer, pointer :: P
  end type t

  type(t) :: var
  call foo(var)

'var' has no pointer/target attribute and, hence, it cannot alias (or if it does as in 'call bar(var, var)' it may not be modified in bar). – Also 'var%A' cannot alias – but 'var%P' can – as it has the pointer attribute.

If 'foo' has 'intent(in)' for 'var', 'var' and 'var%A' may not be modified nor the pointer address (pointer association) of 'var%P'. But the value of 'var%P' may.

With CLASS and descriptor handling with BIND(C) [cf. this PR], I fear there are extra issues due to the auxiliary variables/wrappers generated by the FE.  

[Besides the general issue of Fortran semantic and mapping it to TYPE_RESTRICT and 'fn spec', I have also the feeling that such auxiliary vars cause breakage as they do not always follow the Fortran semantic.]
Comment 24 Richard Biener 2019-11-28 14:27:26 UTC
(In reply to Tobias Burnus from comment #23)
> I have the feeling that some other use also disagrees between ME and
> FE/Fortran semantics assumptions.
> 
> I just run into PR 92703: if one comments the unrelated 'foo', with -O0 one
> gets the expected 'stop 2' but with -O1 one gets 'stop 21' as (effectively)
> the 'class.20._data = &var;' has been optimized away. — For that PR, to
> properly handle Fortran semantic, a copy of 'var' had to be created and used
> instead. I think that would have solved the alias/ME problem for *that*
> usage/test case.

Note it's not the semantic of the Fortran language that matters but the
actual semantics of the GFortran frontend generated IL that does.  If
the Fortran language says for INTENT(IN) the variable isn't modified but
the underlying IL GFortran creates does exactly that then this is what
matters when you compute what to describe to the middle-end since the
middle-end cannot distinguish between "The Fortran Code" and the
"GFortran Implementation Details".

For example array descriptor handling is an important implementation detail
and INTENT() probably does _not_ talk about modifications/ownership of those.
Comment 25 GCC Commits 2020-01-30 08:37:22 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:2595f25cdaf4f16d04a1078a487b2ecc126cae29

commit r10-6344-g2595f25cdaf4f16d04a1078a487b2ecc126cae29
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Jan 30 09:35:03 2020 +0100

    fortran: Fix up ISO_Fortran_binding_15.f90 failures [PR92123]
    
    This is something that has been discussed already a few months ago, but
    seems to have stalled.  Here is Paul's patch from the PR except for the
    TREE_STATIC hunk which is wrong, and does the most conservative fn spec
    tweak for the problematic two builtins we are aware of (to repeat what is in
    the PR, both .wR and .ww are wrong for these builtins that transform one
    layout of an descriptor to another one; while the first pointer is properly
    marked that we only store to what it points to, from the second pointer
    we copy and reshuffle the content and store into the first one; if there
    wouldn't be any pointers, ".wr" would be just fine, but as there is a
    pointer and that pointer is copied to the area pointed by first argument,
    the pointer effectively leaks that way, so we e.g. can't optimize stores
    into what the data pointer in the descriptor points to).  I haven't
    analyzed other fn spec attributes in the FE, but think it is better to
    fix at least this one we have analyzed.
    
    2020-01-30  Paul Thomas �<pault@gcc.gnu.org>
    	    Jakub Jelinek  <jakub@redhat.com>
    
    	PR fortran/92123
    	* trans-decl.c (gfc_get_symbol_decl): Call gfc_defer_symbol_init for
    	CFI descs.
    	(gfc_build_builtin_function_decls): Use ".w." instead of ".ww" or ".wR"
    	for gfor_fndecl_{cfi_to_gfc,gfc_to_cfi}.
    	(convert_CFI_desc): Handle references to CFI descriptors.
    
    Co-authored-by: Paul Thomas <pault@gcc.gnu.org>
Comment 26 Jakub Jelinek 2020-01-31 10:54:28 UTC
Should be fixed now.