diff mbox

Warn on and fold NULL checks against inline functions

Message ID 1401130917-4121-1-git-send-email-patrick@parcs.ath.cx
State New
Headers show

Commit Message

Patrick Palka May 26, 2014, 7:01 p.m. UTC
Hi,

This patch teaches the C++ frontend to warn on NULL checks against
inline functions and it teaches the middle-end to fold NULL checks
against inline functions.  These two things are currently done for
non-inline C++ functions, but inline functions are exceptional in that
the C++ frontend marks them as weak, and NULL checks against weak
symbols cannot be folded in general because the symbol may be mapped to
NULL at link-time.

But in the case of an inline function, the fact that it's a weak symbol
is an implementation detail.  And because it is not permitted to
explicitly give an inline function the "weak" attribute (see
handle_weak_attribute), in order to acheive $SUBJECT it suffices to
assume that all inline functions are non-null, which is what this patch
does.

Bootstrap and regtest against x86_64-unknown-linux-gnu in progress.  Is
this patch OK assuming no regressions?

2014-05-26  Patrick Palka  <patrick@parcs.ath.cx>

	* c-family/c-common.c (decl_with_nonnull_addr_p): Assume inline
	functions are non-null.
	* fold-const.c (tree_single_nonzero_warnv_p): Likewise.
---
 gcc/c-family/c-common.c         |  4 +++-
 gcc/fold-const.c                |  5 ++++-
 gcc/testsuite/g++.dg/inline-1.C | 14 ++++++++++++++
 3 files changed, 21 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/inline-1.C

Comments

Marc Glisse May 26, 2014, 8:26 p.m. UTC | #1
On Mon, 26 May 2014, Patrick Palka wrote:

> This patch teaches the C++ frontend to warn on NULL checks against
> inline functions and it teaches the middle-end to fold NULL checks
> against inline functions.  These two things are currently done for
> non-inline C++ functions, but inline functions are exceptional in that
> the C++ frontend marks them as weak, and NULL checks against weak
> symbols cannot be folded in general because the symbol may be mapped to
> NULL at link-time.
>
> But in the case of an inline function, the fact that it's a weak symbol
> is an implementation detail.  And because it is not permitted to
> explicitly give an inline function the "weak" attribute (see
> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
> assume that all inline functions are non-null, which is what this patch
> does.

Thanks for working on this. You may want to mention PR 59948 in the 
ChangeLog. In the comments of that PR, Honza seems to have a different 
idea of what the fold-const test should look like.
Patrick Palka May 26, 2014, 8:56 p.m. UTC | #2
On Mon, May 26, 2014 at 4:26 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Mon, 26 May 2014, Patrick Palka wrote:
>
>> This patch teaches the C++ frontend to warn on NULL checks against
>> inline functions and it teaches the middle-end to fold NULL checks
>> against inline functions.  These two things are currently done for
>> non-inline C++ functions, but inline functions are exceptional in that
>> the C++ frontend marks them as weak, and NULL checks against weak
>> symbols cannot be folded in general because the symbol may be mapped to
>> NULL at link-time.
>>
>> But in the case of an inline function, the fact that it's a weak symbol
>> is an implementation detail.  And because it is not permitted to
>> explicitly give an inline function the "weak" attribute (see
>> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
>> assume that all inline functions are non-null, which is what this patch
>> does.
>
>
> Thanks for working on this. You may want to mention PR 59948 in the
> ChangeLog. In the comments of that PR, Honza seems to have a different idea
> of what the fold-const test should look like.

I wonder, then, if the test in c-common.c is also broken...  Thanks
for the heads up about the PR.
Marc Glisse May 26, 2014, 9:14 p.m. UTC | #3
On Mon, 26 May 2014, Patrick Palka wrote:

> On Mon, May 26, 2014 at 4:26 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
>> On Mon, 26 May 2014, Patrick Palka wrote:
>>
>>> This patch teaches the C++ frontend to warn on NULL checks against
>>> inline functions and it teaches the middle-end to fold NULL checks
>>> against inline functions.  These two things are currently done for
>>> non-inline C++ functions, but inline functions are exceptional in that
>>> the C++ frontend marks them as weak, and NULL checks against weak
>>> symbols cannot be folded in general because the symbol may be mapped to
>>> NULL at link-time.
>>>
>>> But in the case of an inline function, the fact that it's a weak symbol
>>> is an implementation detail.  And because it is not permitted to
>>> explicitly give an inline function the "weak" attribute (see
>>> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
>>> assume that all inline functions are non-null, which is what this patch
>>> does.
>>
>>
>> Thanks for working on this. You may want to mention PR 59948 in the
>> ChangeLog. In the comments of that PR, Honza seems to have a different idea
>> of what the fold-const test should look like.
>
> I wonder, then, if the test in c-common.c is also broken...  Thanks
> for the heads up about the PR.

I think I should also mention PR 61144 which says the test can be wrong in 
the other direction (I didn't look at it closely).

But even if the tests are broken, your patch may still be an improvement 
while waiting for the rewrite...
Richard Biener May 27, 2014, 7:33 a.m. UTC | #4
On Mon, May 26, 2014 at 9:01 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> Hi,
>
> This patch teaches the C++ frontend to warn on NULL checks against
> inline functions and it teaches the middle-end to fold NULL checks
> against inline functions.  These two things are currently done for
> non-inline C++ functions, but inline functions are exceptional in that
> the C++ frontend marks them as weak, and NULL checks against weak
> symbols cannot be folded in general because the symbol may be mapped to
> NULL at link-time.
>
> But in the case of an inline function, the fact that it's a weak symbol
> is an implementation detail.  And because it is not permitted to
> explicitly give an inline function the "weak" attribute (see
> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
> assume that all inline functions are non-null, which is what this patch
> does.
>
> Bootstrap and regtest against x86_64-unknown-linux-gnu in progress.  Is
> this patch OK assuming no regressions?

What about always_inline function wrappers as used in FORTIFY_SOURCE?
IIRC the actual referenced function may be declared weak and thus the
address can compare to NULL?  Sth like

extern void __foo (void) __attribute__((weak,asm("foo")));
extern inline __attribute__((always_inline,gnu_inline)) void foo (void)
{
  __foo ();
}

int main()
{
  if (foo == 0)
    return 0;
  abort ();
}

The issue is that the inline and alias may be hidden to the user and thus
he'll get unwanted and confusing warnings.

Richard.

> 2014-05-26  Patrick Palka  <patrick@parcs.ath.cx>
>
>         * c-family/c-common.c (decl_with_nonnull_addr_p): Assume inline
>         functions are non-null.
>         * fold-const.c (tree_single_nonzero_warnv_p): Likewise.
> ---
>  gcc/c-family/c-common.c         |  4 +++-
>  gcc/fold-const.c                |  5 ++++-
>  gcc/testsuite/g++.dg/inline-1.C | 14 ++++++++++++++
>  3 files changed, 21 insertions(+), 2 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/inline-1.C
>
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index 6ec14fc..d4747a0 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -4508,7 +4508,9 @@ decl_with_nonnull_addr_p (const_tree expr)
>    return (DECL_P (expr)
>           && (TREE_CODE (expr) == PARM_DECL
>               || TREE_CODE (expr) == LABEL_DECL
> -             || !DECL_WEAK (expr)));
> +             || !DECL_WEAK (expr)
> +             || (TREE_CODE (expr) == FUNCTION_DECL
> +                 && DECL_DECLARED_INLINE_P (expr))));
>  }
>
>  /* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
> diff --git a/gcc/fold-const.c b/gcc/fold-const.c
> index 188b179..2796079 100644
> --- a/gcc/fold-const.c
> +++ b/gcc/fold-const.c
> @@ -16052,7 +16052,10 @@ tree_single_nonzero_warnv_p (tree t, bool *strict_overflow_p)
>                 || (DECL_CONTEXT (base)
>                     && TREE_CODE (DECL_CONTEXT (base)) == FUNCTION_DECL
>                     && auto_var_in_fn_p (base, DECL_CONTEXT (base)))))
> -         return !VAR_OR_FUNCTION_DECL_P (base) || !DECL_WEAK (base);
> +         return !VAR_OR_FUNCTION_DECL_P (base)
> +                || !DECL_WEAK (base)
> +                || (TREE_CODE (base) == FUNCTION_DECL
> +                    && DECL_DECLARED_INLINE_P (base));
>
>         /* Constants are never weak.  */
>         if (CONSTANT_CLASS_P (base))
> diff --git a/gcc/testsuite/g++.dg/inline-1.C b/gcc/testsuite/g++.dg/inline-1.C
> new file mode 100644
> index 0000000..65beff1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/inline-1.C
> @@ -0,0 +1,14 @@
> +// { dg-options "-Waddress" }
> +
> +inline void
> +foo (void)
> +{
> +}
> +
> +int
> +bar (void)
> +{
> +  return foo == 0; // { dg-warning "never be NULL" }
> +}
> +
> +// { dg-final { scan-assembler-not "foo" } }
> --
> 2.0.0.rc2
>
Patrick Palka May 27, 2014, 12:32 p.m. UTC | #5
On Tue, May 27, 2014 at 3:33 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Mon, May 26, 2014 at 9:01 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
>> Hi,
>>
>> This patch teaches the C++ frontend to warn on NULL checks against
>> inline functions and it teaches the middle-end to fold NULL checks
>> against inline functions.  These two things are currently done for
>> non-inline C++ functions, but inline functions are exceptional in that
>> the C++ frontend marks them as weak, and NULL checks against weak
>> symbols cannot be folded in general because the symbol may be mapped to
>> NULL at link-time.
>>
>> But in the case of an inline function, the fact that it's a weak symbol
>> is an implementation detail.  And because it is not permitted to
>> explicitly give an inline function the "weak" attribute (see
>> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
>> assume that all inline functions are non-null, which is what this patch
>> does.
>>
>> Bootstrap and regtest against x86_64-unknown-linux-gnu in progress.  Is
>> this patch OK assuming no regressions?
>
> What about always_inline function wrappers as used in FORTIFY_SOURCE?
> IIRC the actual referenced function may be declared weak and thus the
> address can compare to NULL?  Sth like
>
> extern void __foo (void) __attribute__((weak,asm("foo")));
> extern inline __attribute__((always_inline,gnu_inline)) void foo (void)
> {
>   __foo ();
> }
>
> int main()
> {
>   if (foo == 0)
>     return 0;
>   abort ();
> }
>
> The issue is that the inline and alias may be hidden to the user and thus
> he'll get unwanted and confusing warnings.
>
> Richard.

So as it stands the foo == 0 check in the above example gets folded
with or without my patch.  The issue here seems to be related to the
use of gnu_inline.  The address of an inline function marked
gnu_inline shouldn't be considered non-NULL because a standalone
function does not actually get emitted.  Of course, one could inspect
aliases but in general it is not correct to assume such functions are
non-NULL.  Does this sound right?

This issue has slight overlap with the issue that my patch tries to
fix.  I could try to handle it as well.
Patrick Palka May 27, 2014, 2:06 p.m. UTC | #6
On Tue, May 27, 2014 at 8:32 AM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> On Tue, May 27, 2014 at 3:33 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Mon, May 26, 2014 at 9:01 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
>>> Hi,
>>>
>>> This patch teaches the C++ frontend to warn on NULL checks against
>>> inline functions and it teaches the middle-end to fold NULL checks
>>> against inline functions.  These two things are currently done for
>>> non-inline C++ functions, but inline functions are exceptional in that
>>> the C++ frontend marks them as weak, and NULL checks against weak
>>> symbols cannot be folded in general because the symbol may be mapped to
>>> NULL at link-time.
>>>
>>> But in the case of an inline function, the fact that it's a weak symbol
>>> is an implementation detail.  And because it is not permitted to
>>> explicitly give an inline function the "weak" attribute (see
>>> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
>>> assume that all inline functions are non-null, which is what this patch
>>> does.
>>>
>>> Bootstrap and regtest against x86_64-unknown-linux-gnu in progress.  Is
>>> this patch OK assuming no regressions?
>>
>> What about always_inline function wrappers as used in FORTIFY_SOURCE?
>> IIRC the actual referenced function may be declared weak and thus the
>> address can compare to NULL?  Sth like
>>
>> extern void __foo (void) __attribute__((weak,asm("foo")));
>> extern inline __attribute__((always_inline,gnu_inline)) void foo (void)
>> {
>>   __foo ();
>> }
>>
>> int main()
>> {
>>   if (foo == 0)
>>     return 0;
>>   abort ();
>> }
>>
>> The issue is that the inline and alias may be hidden to the user and thus
>> he'll get unwanted and confusing warnings.
>>
>> Richard.
>
> So as it stands the foo == 0 check in the above example gets folded
> with or without my patch.  The issue here seems to be related to the
> use of gnu_inline.  The address of an inline function marked
> gnu_inline shouldn't be considered non-NULL because a standalone
> function does not actually get emitted.  Of course, one could inspect
> aliases but in general it is not correct to assume such functions are
> non-NULL.  Does this sound right?
>
> This issue has slight overlap with the issue that my patch tries to
> fix.  I could try to handle it as well.

Oh, disregard the above.  I think I see what you mean now, Richard.
Because __foo is an alias for foo, and __foo is weak, is it safe to
assume that foo is non-NULL?  That is a tricky question...
Richard Biener May 27, 2014, 2:54 p.m. UTC | #7
On Tue, May 27, 2014 at 4:06 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> On Tue, May 27, 2014 at 8:32 AM, Patrick Palka <patrick@parcs.ath.cx> wrote:
>> On Tue, May 27, 2014 at 3:33 AM, Richard Biener
>> <richard.guenther@gmail.com> wrote:
>>> On Mon, May 26, 2014 at 9:01 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
>>>> Hi,
>>>>
>>>> This patch teaches the C++ frontend to warn on NULL checks against
>>>> inline functions and it teaches the middle-end to fold NULL checks
>>>> against inline functions.  These two things are currently done for
>>>> non-inline C++ functions, but inline functions are exceptional in that
>>>> the C++ frontend marks them as weak, and NULL checks against weak
>>>> symbols cannot be folded in general because the symbol may be mapped to
>>>> NULL at link-time.
>>>>
>>>> But in the case of an inline function, the fact that it's a weak symbol
>>>> is an implementation detail.  And because it is not permitted to
>>>> explicitly give an inline function the "weak" attribute (see
>>>> handle_weak_attribute), in order to acheive $SUBJECT it suffices to
>>>> assume that all inline functions are non-null, which is what this patch
>>>> does.
>>>>
>>>> Bootstrap and regtest against x86_64-unknown-linux-gnu in progress.  Is
>>>> this patch OK assuming no regressions?
>>>
>>> What about always_inline function wrappers as used in FORTIFY_SOURCE?
>>> IIRC the actual referenced function may be declared weak and thus the
>>> address can compare to NULL?  Sth like
>>>
>>> extern void __foo (void) __attribute__((weak,asm("foo")));
>>> extern inline __attribute__((always_inline,gnu_inline)) void foo (void)
>>> {
>>>   __foo ();
>>> }
>>>
>>> int main()
>>> {
>>>   if (foo == 0)
>>>     return 0;
>>>   abort ();
>>> }
>>>
>>> The issue is that the inline and alias may be hidden to the user and thus
>>> he'll get unwanted and confusing warnings.
>>>
>>> Richard.
>>
>> So as it stands the foo == 0 check in the above example gets folded
>> with or without my patch.  The issue here seems to be related to the
>> use of gnu_inline.  The address of an inline function marked
>> gnu_inline shouldn't be considered non-NULL because a standalone
>> function does not actually get emitted.  Of course, one could inspect
>> aliases but in general it is not correct to assume such functions are
>> non-NULL.  Does this sound right?
>>
>> This issue has slight overlap with the issue that my patch tries to
>> fix.  I could try to handle it as well.
>
> Oh, disregard the above.  I think I see what you mean now, Richard.
> Because __foo is an alias for foo, and __foo is weak, is it safe to
> assume that foo is non-NULL?  That is a tricky question...

No (I wasn't making a folding validity remark).  I want to make sure
we won't see diagnostics on code like

#include <string.h>

int main()
{
   if (malloc != 0)
     ...
}

just because it is built with -D_FORTIFY_SOURCE.  Whether we
may fold that check at all is another question, of course (and again
that should _not_ differ between -D_FORTIFY_SOURCE or -U_FORTIFY_SOURCE,
but it may well also be a glibc issue).

And yes, aliases are tricky beasts ...

Richard.
diff mbox

Patch

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 6ec14fc..d4747a0 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -4508,7 +4508,9 @@  decl_with_nonnull_addr_p (const_tree expr)
   return (DECL_P (expr)
 	  && (TREE_CODE (expr) == PARM_DECL
 	      || TREE_CODE (expr) == LABEL_DECL
-	      || !DECL_WEAK (expr)));
+	      || !DECL_WEAK (expr)
+	      || (TREE_CODE (expr) == FUNCTION_DECL
+		  && DECL_DECLARED_INLINE_P (expr))));
 }
 
 /* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 188b179..2796079 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -16052,7 +16052,10 @@  tree_single_nonzero_warnv_p (tree t, bool *strict_overflow_p)
 		|| (DECL_CONTEXT (base)
 		    && TREE_CODE (DECL_CONTEXT (base)) == FUNCTION_DECL
 		    && auto_var_in_fn_p (base, DECL_CONTEXT (base)))))
-	  return !VAR_OR_FUNCTION_DECL_P (base) || !DECL_WEAK (base);
+	  return !VAR_OR_FUNCTION_DECL_P (base)
+		 || !DECL_WEAK (base)
+		 || (TREE_CODE (base) == FUNCTION_DECL
+		     && DECL_DECLARED_INLINE_P (base));
 
 	/* Constants are never weak.  */
 	if (CONSTANT_CLASS_P (base))
diff --git a/gcc/testsuite/g++.dg/inline-1.C b/gcc/testsuite/g++.dg/inline-1.C
new file mode 100644
index 0000000..65beff1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/inline-1.C
@@ -0,0 +1,14 @@ 
+// { dg-options "-Waddress" }
+
+inline void
+foo (void)
+{
+}
+
+int
+bar (void)
+{
+  return foo == 0; // { dg-warning "never be NULL" }
+}
+
+// { dg-final { scan-assembler-not "foo" } }