On Sat, Apr 5, 2014 at 3:52 PM, Marc Glisse <marc.gli...@inria.fr> wrote:
> Hello,
>
> we have front-end warnings about returning the address of a local variable.
> However, quite often in C++, people don't directly return the address of a
> temporary, it goes through a few functions which hide that fact. After some
> inlining, the fact that we are returning the address of a local variable can
> become obvious to the compiler, to the point where I have used, for
> debugging purposes, grep 'return &' on the optimized dump produced with -O3
> -fkeep-inline-functions (I then had to sort through the global/local
> variables).
>
> fold_stmt looks like a good place for this, but it could go almost anywhere.
> It has to happen after enough inlining / copy propagation to make it useful
> though.
>
> One hard part is avoiding duplicate warnings. Replacing the address with 0
> is a convenient way to do that, so I did it both for my new warning and for
> the existing C/C++ ones. The patch breaks
> gfortran.dg/warn_target_lifetime_2.f90 because it ends up warning twice. I
> didn't touch that front-end because I don't know fortran, and the warning
> message "Pointer at .1. in pointer assignment might outlive the pointer
> target" doesn't seem very confident that the thing really is broken enough
> to be replaced by 0. I only tested (bootstrap+regression) the default
> languages, so ada/go may have a similar issue, to be handled if the approach
> seems ok. (I personally wouldn't care about duplicate warnings, but I know
> some people can't help complaining about it)
>
> This doesn't actually fix PR 60517, for that I was thinking of checking for
> memory reads if the first stop of walk_aliased_vdefs is a clobber (could
> also check __builtin_free), though I don't know in which pass to do that
> yet.

Note that this will break "working" programs where inlining causes
those references to no longer be "returned" (though maybe they
already break with the clobbers we insert).  I remember that POOMA/PETE
was full of this ... (and made it reliably work by flattening all call trees
with such calls).

Richard.

> 2014-04-05  Marc Glisse  <marc.gli...@inria.fr>
>
>         PR c++/60517
> gcc/c/
>         * c-typeck.c (c_finish_return): Return 0 instead of the address of
>         a local variable.
> gcc/cp/
>         * typeck.c (check_return_expr): Likewise.
>         (maybe_warn_about_returning_address_of_local): Tell the caller if
>         we warned.
> gcc/
>         * gimple-fold.c (fold_stmt_1): Warn if returning the address of a
>         local variable.
> gcc/testsuite/
>         * c-c++-common/addrtmp.c: New testcase.
>         * c-c++-common/uninit-G.c: Adjust.
>
> --
> Marc Glisse
> Index: gcc/c/c-typeck.c
> ===================================================================
> --- gcc/c/c-typeck.c    (revision 209157)
> +++ gcc/c/c-typeck.c    (working copy)
> @@ -9254,23 +9254,25 @@ c_finish_return (location_t loc, tree re
>               inner = TREE_OPERAND (inner, 0);
>
>               while (REFERENCE_CLASS_P (inner)
>                      && TREE_CODE (inner) != INDIRECT_REF)
>                 inner = TREE_OPERAND (inner, 0);
>
>               if (DECL_P (inner)
>                   && !DECL_EXTERNAL (inner)
>                   && !TREE_STATIC (inner)
>                   && DECL_CONTEXT (inner) == current_function_decl)
> -               warning_at (loc,
> -                           OPT_Wreturn_local_addr, "function returns
> address "
> -                           "of local variable");
> +               {
> +                 warning_at (loc, OPT_Wreturn_local_addr,
> +                             "function returns address of local variable");
> +                 t = build_zero_cst (TREE_TYPE (res));
> +               }
>               break;
>
>             default:
>               break;
>             }
>
>           break;
>         }
>
>        retval = build2 (MODIFY_EXPR, TREE_TYPE (res), res, t);
> Index: gcc/cp/typeck.c
> ===================================================================
> --- gcc/cp/typeck.c     (revision 209157)
> +++ gcc/cp/typeck.c     (working copy)
> @@ -49,21 +49,21 @@ static tree convert_for_assignment (tree
>  static tree cp_pointer_int_sum (enum tree_code, tree, tree,
> tsubst_flags_t);
>  static tree rationalize_conditional_expr (enum tree_code, tree,
>                                           tsubst_flags_t);
>  static int comp_ptr_ttypes_real (tree, tree, int);
>  static bool comp_except_types (tree, tree, bool);
>  static bool comp_array_types (const_tree, const_tree, bool);
>  static tree pointer_diff (tree, tree, tree, tsubst_flags_t);
>  static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t);
>  static void casts_away_constness_r (tree *, tree *, tsubst_flags_t);
>  static bool casts_away_constness (tree, tree, tsubst_flags_t);
> -static void maybe_warn_about_returning_address_of_local (tree);
> +static bool maybe_warn_about_returning_address_of_local (tree);
>  static tree lookup_destructor (tree, tree, tree, tsubst_flags_t);
>  static void warn_args_num (location_t, tree, bool);
>  static int convert_arguments (tree, vec<tree, va_gc> **, tree, int,
>                                tsubst_flags_t);
>
>  /* Do `exp = require_complete_type (exp);' to make sure exp
>     does not have an incomplete type.  (That includes void types.)
>     Returns error_mark_node if the VALUE does not have
>     complete type when this function returns.  */
>
> @@ -8253,79 +8253,81 @@ convert_for_initialization (tree exp, tr
>      return rhs;
>
>    if (MAYBE_CLASS_TYPE_P (type))
>      return perform_implicit_conversion_flags (type, rhs, complain, flags);
>
>    return convert_for_assignment (type, rhs, errtype, fndecl, parmnum,
>                                  complain, flags);
>  }
>
>  /* If RETVAL is the address of, or a reference to, a local variable or
> -   temporary give an appropriate warning.  */
> +   temporary give an appropriate warning and return true.  */
>
> -static void
> +static bool
>  maybe_warn_about_returning_address_of_local (tree retval)
>  {
>    tree valtype = TREE_TYPE (DECL_RESULT (current_function_decl));
>    tree whats_returned = retval;
>
>    for (;;)
>      {
>        if (TREE_CODE (whats_returned) == COMPOUND_EXPR)
>         whats_returned = TREE_OPERAND (whats_returned, 1);
>        else if (CONVERT_EXPR_P (whats_returned)
>                || TREE_CODE (whats_returned) == NON_LVALUE_EXPR)
>         whats_returned = TREE_OPERAND (whats_returned, 0);
>        else
>         break;
>      }
>
>    if (TREE_CODE (whats_returned) != ADDR_EXPR)
> -    return;
> +    return false;
>    whats_returned = TREE_OPERAND (whats_returned, 0);
>
>    while (TREE_CODE (whats_returned) == COMPONENT_REF
>          || TREE_CODE (whats_returned) == ARRAY_REF)
>      whats_returned = TREE_OPERAND (whats_returned, 0);
>
>    if (TREE_CODE (valtype) == REFERENCE_TYPE)
>      {
>        if (TREE_CODE (whats_returned) == AGGR_INIT_EXPR
>           || TREE_CODE (whats_returned) == TARGET_EXPR)
>         {
>           warning (OPT_Wreturn_local_addr, "returning reference to
> temporary");
> -         return;
> +         return true;
>         }
>        if (VAR_P (whats_returned)
>           && DECL_NAME (whats_returned)
>           && TEMP_NAME_P (DECL_NAME (whats_returned)))
>         {
>           warning (OPT_Wreturn_local_addr, "reference to non-lvalue
> returned");
> -         return;
> +         return true;
>         }
>      }
>
>    if (DECL_P (whats_returned)
>        && DECL_NAME (whats_returned)
>        && DECL_FUNCTION_SCOPE_P (whats_returned)
>        && !is_capture_proxy (whats_returned)
>        && !(TREE_STATIC (whats_returned)
>            || TREE_PUBLIC (whats_returned)))
>      {
>        if (TREE_CODE (valtype) == REFERENCE_TYPE)
>         warning (OPT_Wreturn_local_addr, "reference to local variable %q+D
> returned",
>                  whats_returned);
>        else
>         warning (OPT_Wreturn_local_addr, "address of local variable %q+D
> returned",
>                  whats_returned);
> -      return;
> +      return true;
>      }
> +
> +  return false;
>  }
>
>  /* Check that returning RETVAL from the current function is valid.
>     Return an expression explicitly showing all conversions required to
>     change RETVAL into the function return type, and to assign it to
>     the DECL_RESULT for the function.  Set *NO_WARNING to true if
>     code reaches end of non-void function warning shouldn't be issued
>     on this RETURN_EXPR.  */
>
>  tree
> @@ -8606,22 +8608,22 @@ check_return_expr (tree retval, bool *no
>
>        /* If the conversion failed, treat this just like `return;'.  */
>        if (retval == error_mark_node)
>         return retval;
>        /* We can't initialize a register from a AGGR_INIT_EXPR.  */
>        else if (! cfun->returns_struct
>                && TREE_CODE (retval) == TARGET_EXPR
>                && TREE_CODE (TREE_OPERAND (retval, 1)) == AGGR_INIT_EXPR)
>         retval = build2 (COMPOUND_EXPR, TREE_TYPE (retval), retval,
>                          TREE_OPERAND (retval, 0));
> -      else
> -       maybe_warn_about_returning_address_of_local (retval);
> +      else if (maybe_warn_about_returning_address_of_local (retval))
> +       retval = build_zero_cst (TREE_TYPE (retval));
>      }
>
>    /* Actually copy the value returned into the appropriate location.  */
>    if (retval && retval != result)
>      retval = build2 (INIT_EXPR, TREE_TYPE (result), result, retval);
>
>    return retval;
>  }
>
>
> Index: gcc/gimple-fold.c
> ===================================================================
> --- gcc/gimple-fold.c   (revision 209157)
> +++ gcc/gimple-fold.c   (working copy)
> @@ -45,20 +45,21 @@ along with GCC; see the file COPYING3.
>  #include "tree-into-ssa.h"
>  #include "tree-dfa.h"
>  #include "tree-ssa.h"
>  #include "tree-ssa-propagate.h"
>  #include "target.h"
>  #include "ipa-utils.h"
>  #include "gimple-pretty-print.h"
>  #include "tree-ssa-address.h"
>  #include "langhooks.h"
>  #include "gimplify-me.h"
> +#include "diagnostic-core.h"
>
>  /* Return true when DECL can be referenced from current unit.
>     FROM_DECL (if non-null) specify constructor of variable DECL was taken
> from.
>     We can get declarations that are not possible to reference for various
>     reasons:
>
>       1) When analyzing C++ virtual tables.
>         C++ virtual tables do have known constructors even
>         when they are keyed to other compilation unit.
>         Those tables can contain pointers to methods and vars
> @@ -1366,20 +1367,39 @@ fold_stmt_1 (gimple_stmt_iterator *gsi,
>               if (tem)
>                 {
>                   tem = build_fold_addr_expr_with_type (tem, TREE_TYPE
> (val));
>                   gimple_debug_bind_set_value (stmt, tem);
>                   changed = true;
>                 }
>             }
>         }
>        break;
>
> +    case GIMPLE_RETURN:
> +      {
> +       tree val = gimple_return_retval (stmt);
> +       if (val && TREE_CODE (val) == ADDR_EXPR)
> +         {
> +           tree valbase = get_base_address (TREE_OPERAND (val, 0));
> +           if (TREE_CODE (valbase) == VAR_DECL
> +               && !is_global_var (valbase))
> +             {
> +               if (warning_at (gimple_location (stmt),
> OPT_Wreturn_local_addr,
> +                               "function returns address of local
> variable"))
> +                 inform (DECL_SOURCE_LOCATION(valbase), "declared here");
> +               tree zero = build_zero_cst (TREE_TYPE (val));
> +               gimple_return_set_retval (stmt, zero);
> +               changed = true;
> +             }
> +         }
> +      }
> +      break;
>      default:;
>      }
>
>    stmt = gsi_stmt (*gsi);
>
>    /* Fold *& on the lhs.  */
>    if (gimple_has_lhs (stmt))
>      {
>        tree lhs = gimple_get_lhs (stmt);
>        if (lhs && REFERENCE_CLASS_P (lhs))
> Index: gcc/testsuite/c-c++-common/addrtmp.c
> ===================================================================
> --- gcc/testsuite/c-c++-common/addrtmp.c        (revision 0)
> +++ gcc/testsuite/c-c++-common/addrtmp.c        (working copy)
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +typedef struct A { int a,b; } A;
> +int*g(int*x){return x;}
> +int*f(){
> +  A x[2]={{1,2},{3,4}};
> +  return g(&x[1].a); // { dg-warning "address of local variable" }
> +}
> +A y[2]={{1,2},{3,4}};
> +int*h(){
> +  return g(&y[1].a);
> +}
>
> Property changes on: gcc/testsuite/c-c++-common/addrtmp.c
> ___________________________________________________________________
> Added: svn:keywords
> ## -0,0 +1 ##
> +Author Date Id Revision URL
> \ No newline at end of property
> Added: svn:eol-style
> ## -0,0 +1 ##
> +native
> \ No newline at end of property
> Index: gcc/testsuite/c-c++-common/uninit-G.c
> ===================================================================
> --- gcc/testsuite/c-c++-common/uninit-G.c       (revision 209157)
> +++ gcc/testsuite/c-c++-common/uninit-G.c       (working copy)
> @@ -1,9 +1,10 @@
>  /* Test we do not warn about initializing variable with address of self in
> the initialization. */
>  /* { dg-do compile } */
>  /* { dg-options "-O -Wuninitialized" } */
>
> -void *f()
> +void g(void*);
> +void f()
>  {
>    void *i = &i;
> -  return i;
> +  g(i);
>  }
>

Reply via email to