On Thu, 16 May 2019, Jakub Jelinek wrote:

> Hi!
> 
> Fortran subroutines/functions that have CHARACTER arguments have also
> hidden arguments at the end of the argument list which hold the string
> length.  This is something all Fortran compilers I've tried do and is
> done in order to support calling unprototyped subroutines/functions
> where one can't know if the callee is using character(len=constant)
> or character(len=*) argument, where for the latter one has to pass
> the extra argument because there is no other way to propagate that info,
> while for the former it is kind of redundant but still part of the ABI;
> the compiler just uses the constant directly instead of asking about the
> real passed string length.
> 
> Another thing is that until PR87689 has been fixed, the Fortran FE has been
> broken, used vararg-ish prototypes in most cases and that prevented (all?)
> tail call optimizations in the Fortran code.
> 
> Apparently it is a common case that buggy C/C++ wrappers around the Fortran
> functions just ignore the ABI and don't pass the hidden string length
> arguments at all, which seemed to work fine in gfortran until recently
> (because the arguments weren't used).  When we started making tail calls
> from such functions, this has of course changed, because when making a tail
> call we overwrite the caller's argument slots with the arguments we want to
> pass to the callee.  Not really sure why it seemed to work with other
> compilers; trying https://fortran.godbolt.org/z/ckMt1t
> subroutine foo (a, b, c, d, e, f, g, h)
>   integer :: b, c, d, e, f, g, h
>   character(len=1) :: a
>   call bar (a, b, c, d, e, f, g, h)
> end subroutine foo
> both older/new gfortran and ifort 19.0.0 emit a tail call which overwrites
> the hidden string length argument with 1, but when trying the
> https://fortran.godbolt.org/z/xpsH8e LAPACK routine, ifort for some reason
> doesn't tail call it.
> 
> I'm not really happy to provide workarounds for undefined behavior,
> especially because that will mean it might take longer if ever if those
> buggy programs are fixed.  On the other side, the PR87689 bug fix has been
> backported to all release branches and so now not only trunk, but also 9.1,
> 8.3.1 and 7.4.1 are affected.  Instead of trying to disable all tail calls,
> this patch disables tail calls from functions/subroutines that have those
> hidden string length arguments and don't use character(len=*) (in that case
> the function wouldn't seem to work previously either, because the argument
> is really used), where those hidden string length arguments are passed
> on the stack and where the tail callee also would want to pass arguments
> on the stack (if we spent even more time on this, we could narrow it down
> further and check if the tail call would actually store anything overlapping
> the hidden string length arguments on the stack).
> 
> This workaround probably needs guarding with some Fortran FE specific
> option, so that it can be disabled, will defer that to the Fortran
> maintainers.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and
> release branches (not sure about LTO on the release branches, does one need
> to bump anything when changing the LTO format by streaming another bit)?

You need to bump the LTO_minor_version in lto-streamer.h

Which reminds me I forgot to update LTO_major_version on trunk - can
you do that?

I'm also worried that with this we just never fix those sources
but I agree with fixing the issue on now broken branches.

So, OK for trunk and branches (with the approrpiate lto-streamer.h
changes).  We should eventually revert the change for GCC 10
though.

Thanks,
Richard.

> 2019-05-16  Jakub Jelinek  <ja...@redhat.com>
> 
>       PR fortran/90329
>       * tree-core.h (struct tree_decl_common): Document
>       decl_nonshareable_flag for PARM_DECLs.
>       * tree.h (DECL_HIDDEN_STRING_LENGTH): Define.
>       * calls.c (expand_call): Don't try tail call if caller
>       has any DECL_HIDDEN_STRING_LENGTH PARM_DECLs that are or might be
>       passed on the stack and callee needs to pass any arguments on the
>       stack.
>       * tree-streamer-in.c (unpack_ts_decl_common_value_fields): Use
>       else if instead of series of mutually exclusive ifs.  Handle
>       DECL_HIDDEN_STRING_LENGTH for PARM_DECLs.
>       * tree-streamer-out.c (pack_ts_decl_common_value_fields): Likewise.
> 
>       * trans-decl.c (create_function_arglist): Set
>       DECL_HIDDEN_STRING_LENGTH on hidden string length PARM_DECLs if
>       len is constant.
> 
> --- gcc/tree-core.h.jj        2019-02-22 15:22:20.882919620 +0100
> +++ gcc/tree-core.h   2019-05-15 08:00:39.284668438 +0200
> @@ -1683,6 +1683,7 @@ struct GTY(()) tree_decl_common {
>    /* In a VAR_DECL and PARM_DECL, this is DECL_READ_P.  */
>    unsigned decl_read_flag : 1;
>    /* In a VAR_DECL or RESULT_DECL, this is DECL_NONSHAREABLE.  */
> +  /* In a PARM_DECL, this is DECL_HIDDEN_STRING_LENGTH.  */
>    unsigned decl_nonshareable_flag : 1;
>  
>    /* DECL_OFFSET_ALIGN, used only for FIELD_DECLs.  */
> --- gcc/tree.h.jj     2019-05-02 12:18:33.829078755 +0200
> +++ gcc/tree.h        2019-05-15 08:06:11.559171046 +0200
> @@ -904,6 +904,11 @@ extern void omp_clause_range_check_faile
>    (TREE_CHECK2 (NODE, VAR_DECL, \
>               RESULT_DECL)->decl_common.decl_nonshareable_flag)
>  
> +/* In a PARM_DECL, set for Fortran hidden string length arguments that some
> +   buggy callers don't pass to the callee.  */
> +#define DECL_HIDDEN_STRING_LENGTH(NODE) \
> +  (TREE_CHECK (NODE, PARM_DECL)->decl_common.decl_nonshareable_flag)
> +
>  /* In a CALL_EXPR, means that the call is the jump from a thunk to the
>     thunked-to function.  */
>  #define CALL_FROM_THUNK_P(NODE) (CALL_EXPR_CHECK (NODE)->base.protected_flag)
> --- gcc/calls.c.jj    2019-04-08 10:11:30.852182466 +0200
> +++ gcc/calls.c       2019-05-15 09:03:56.863754839 +0200
> @@ -3628,6 +3628,28 @@ expand_call (tree exp, rtx target, int i
>        || dbg_cnt (tail_call) == false)
>      try_tail_call = 0;
>  
> +  /* Workaround buggy C/C++ wrappers around Fortran routines with
> +     character(len=constant) arguments if the hidden string length arguments
> +     are passed on the stack; if the callers forget to pass those arguments,
> +     attempting to tail call in such routines leads to stack corruption.
> +     Avoid tail calls in functions where at least one such hidden string
> +     length argument is passed (partially or fully) on the stack in the
> +     caller and the callee needs to pass any arguments on the stack.
> +     See PR90329.  */
> +  if (try_tail_call && maybe_ne (args_size.constant, 0))
> +    for (tree arg = DECL_ARGUMENTS (current_function_decl);
> +      arg; arg = DECL_CHAIN (arg))
> +      if (DECL_HIDDEN_STRING_LENGTH (arg) && DECL_INCOMING_RTL (arg))
> +     {
> +       subrtx_iterator::array_type array;
> +       FOR_EACH_SUBRTX (iter, array, DECL_INCOMING_RTL (arg), NONCONST)
> +         if (MEM_P (*iter))
> +           {
> +             try_tail_call = 0;
> +             break;
> +           }
> +     }
> +
>    /* If the user has marked the function as requiring tail-call
>       optimization, attempt it.  */
>    if (must_tail_call)
> --- gcc/tree-streamer-in.c.jj 2019-01-01 12:37:21.184908879 +0100
> +++ gcc/tree-streamer-in.c    2019-05-15 08:58:02.123629519 +0200
> @@ -251,7 +251,7 @@ unpack_ts_decl_common_value_fields (stru
>        LABEL_DECL_UID (expr) = -1;
>      }
>  
> -  if (TREE_CODE (expr) == FIELD_DECL)
> +  else if (TREE_CODE (expr) == FIELD_DECL)
>      {
>        DECL_PACKED (expr) = (unsigned) bp_unpack_value (bp, 1);
>        DECL_NONADDRESSABLE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
> @@ -259,12 +259,15 @@ unpack_ts_decl_common_value_fields (stru
>        expr->decl_common.off_align = bp_unpack_value (bp, 8);
>      }
>  
> -  if (VAR_P (expr))
> +  else if (VAR_P (expr))
>      {
>        DECL_HAS_DEBUG_EXPR_P (expr) = (unsigned) bp_unpack_value (bp, 1);
>        DECL_NONLOCAL_FRAME (expr) = (unsigned) bp_unpack_value (bp, 1);
>      }
>  
> +  else if (TREE_CODE (expr) == PARM_DECL)
> +    DECL_HIDDEN_STRING_LENGTH (expr) = (unsigned) bp_unpack_value (bp, 1);
> +
>    if (TREE_CODE (expr) == RESULT_DECL
>        || TREE_CODE (expr) == PARM_DECL
>        || VAR_P (expr))
> --- gcc/tree-streamer-out.c.jj        2019-01-24 19:54:20.792500923 +0100
> +++ gcc/tree-streamer-out.c   2019-05-15 09:01:23.957287106 +0200
> @@ -212,7 +212,7 @@ pack_ts_decl_common_value_fields (struct
>        bp_pack_var_len_unsigned (bp, EH_LANDING_PAD_NR (expr));
>      }
>  
> -  if (TREE_CODE (expr) == FIELD_DECL)
> +  else if (TREE_CODE (expr) == FIELD_DECL)
>      {
>        bp_pack_value (bp, DECL_PACKED (expr), 1);
>        bp_pack_value (bp, DECL_NONADDRESSABLE_P (expr), 1);
> @@ -220,12 +220,15 @@ pack_ts_decl_common_value_fields (struct
>        bp_pack_value (bp, expr->decl_common.off_align, 8);
>      }
>  
> -  if (VAR_P (expr))
> +  else if (VAR_P (expr))
>      {
>        bp_pack_value (bp, DECL_HAS_DEBUG_EXPR_P (expr), 1);
>        bp_pack_value (bp, DECL_NONLOCAL_FRAME (expr), 1);
>      }
>  
> +  else if (TREE_CODE (expr) == PARM_DECL)
> +    bp_pack_value (bp, DECL_HIDDEN_STRING_LENGTH (expr), 1);
> +
>    if (TREE_CODE (expr) == RESULT_DECL
>        || TREE_CODE (expr) == PARM_DECL
>        || VAR_P (expr))
> --- gcc/fortran/trans-decl.c.jj       2019-05-15 08:18:16.000000000 +0200
> +++ gcc/fortran/trans-decl.c  2019-05-15 08:31:07.260388229 +0200
> @@ -2512,6 +2512,10 @@ create_function_arglist (gfc_symbol * sy
>         DECL_ARG_TYPE (length) = len_type;
>         TREE_READONLY (length) = 1;
>         gfc_finish_decl (length);
> +       if (f->sym->ts.u.cl
> +           && f->sym->ts.u.cl->length
> +           && f->sym->ts.u.cl->length->expr_type == EXPR_CONSTANT)
> +         DECL_HIDDEN_STRING_LENGTH (length) = 1;
>  
>         /* Remember the passed value.  */
>            if (!f->sym->ts.u.cl ||  f->sym->ts.u.cl->passed_length)
> 
>       Jakub
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany;
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah; HRB 21284 (AG Nürnberg)

Reply via email to