2011-01-13  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/47278
	* tree.h (DECL_REPLACEABLE_P): Remove.
	(decl_replaceable_p): Declare.
	(decl_binds_to_current_def_p): Likewise.
	* varasm.c (decl_replaceable_p): New function.
	(decl_binds_to_current_def_p): Likewise.
	* cgraph.c (cgraph_function_body_availability): Use decl_replaceable_p.
	* except.c (set_nothrow_function_flags): Likewise.
	* tree-inline.c (inlinable_function_p): Likewise.

	cp/
	* decl.c (finish_function): Likewise.

Index: gcc/tree.h
===================================================================
*** gcc/tree.h	(revision 168743)
--- gcc/tree.h	(working copy)
*************** extern void decl_restrict_base_insert (t
*** 3023,3048 ****
     something which is DECL_COMDAT.  */
  #define DECL_COMDAT(NODE) (DECL_WITH_VIS_CHECK (NODE)->decl_with_vis.comdat_flag)
  
- /* A replaceable function is one which may be replaced at link-time
-    with an entirely different definition, provided that the
-    replacement has the same type.  For example, functions declared
-    with __attribute__((weak)) on most systems are replaceable.  
- 
-    COMDAT functions are not replaceable, since all definitions of the
-    function must be equivalent.  It is important that COMDAT functions
-    not be treated as replaceable so that use of C++ template
-    instantiations is not penalized.  
- 
-    For example, DECL_REPLACEABLE is used to determine whether or not a
-    function (including a template instantiation) which is not
-    explicitly declared "inline" can be inlined.  If the function is
-    DECL_REPLACEABLE then it is not safe to do the inlining, since the
-    implementation chosen at link-time may be different.  However, a
-    function that is not DECL_REPLACEABLE can be inlined, since all
-    versions of the function will be functionally identical.  */
- #define DECL_REPLACEABLE_P(NODE) \
-   (!DECL_COMDAT (NODE) && !targetm.binds_local_p (NODE))
- 
  /* The name of the object as the assembler will see it (but before any
     translations made by ASM_OUTPUT_LABELREF).  Often this is the same
     as DECL_NAME.  It is an IDENTIFIER_NODE.  */
--- 3023,3028 ----
*************** extern void process_pending_assemble_ext
*** 5067,5072 ****
--- 5047,5054 ----
  extern void finish_aliases_1 (void);
  extern void finish_aliases_2 (void);
  extern tree emutls_decl (tree);
+ extern bool decl_replaceable_p (tree);
+ extern bool decl_binds_to_current_def_p (tree);
  
  /* In stmt.c */
  extern void expand_computed_goto (tree);
Index: gcc/varasm.c
===================================================================
*** gcc/varasm.c	(revision 168743)
--- gcc/varasm.c	(working copy)
*************** default_valid_pointer_mode (enum machine
*** 6257,6262 ****
--- 6257,6307 ----
    return (mode == ptr_mode || mode == Pmode);
  }
  
+ /* Return true when references to DECL must bind to current definition in
+    final executable.
+ 
+    The condition is usually equivalent to whether the function binds to the
+    current module (shared library or executable), that is to binds_local_p.
+    We use this fact to avoid need for another target hook and implement
+    the logic using binds_local_p and just special cases where
+    decl_binds_to_current_def_p is stronger than binds local_p.  In particular
+    the weak definitions (that can be overwritten at linktime by other
+    definition from different object file) and when resolution info is available
+    we simply use the knowledge passed to us by linker plugin.  */
+ bool
+ decl_binds_to_current_def_p (tree decl)
+ {
+   gcc_assert (DECL_P (decl));
+   if (!TREE_PUBLIC (decl))
+     return true;
+   if (!targetm.binds_local_p (decl))
+     return false;
+   /* Otherwise we have to assume the worst for DECL_WEAK (hidden weaks
+      binds localy but still can be overwritten).
+      This rely on fact that binds_local_p behave as decl_replaceable_p
+      for all other declaration types.  */
+   return !DECL_WEAK (decl);
+ }
+ 
+ /* A replaceable function or variable is one which may be replaced
+    at link-time with an entirely different definition, provided that the
+    replacement has the same type.  For example, functions declared
+    with __attribute__((weak)) on most systems are replaceable.
+ 
+    COMDAT functions are not replaceable, since all definitions of the
+    function must be equivalent.  It is important that COMDAT functions
+    not be treated as replaceable so that use of C++ template
+    instantiations is not penalized.  */
+ 
+ bool
+ decl_replaceable_p (tree decl)
+ {
+   gcc_assert (DECL_P (decl));
+   if (!TREE_PUBLIC (decl) || DECL_COMDAT (decl))
+     return false;
+   return !decl_binds_to_current_def_p (decl);
+ }
+ 
  /* Default function to output code that will globalize a label.  A
     target must define GLOBAL_ASM_OP or provide its own function to
     globalize a label.  */
Index: gcc/cgraph.c
===================================================================
*** gcc/cgraph.c	(revision 168743)
--- gcc/cgraph.c	(working copy)
*************** cgraph_function_body_availability (struc
*** 1007,1021 ****
  
       ??? Does the C++ one definition rule allow us to always return
       AVAIL_AVAILABLE here?  That would be good reason to preserve this
!      hook Similarly deal with extern inline functions - this is again
!      necessary to get C++ shared functions having keyed templates
!      right and in the C extension documentation we probably should
!      document the requirement of both versions of function (extern
!      inline and offline) having same side effect characteristics as
!      good optimization is what this optimization is about.  */
  
!   else if (!(*targetm.binds_local_p) (node->decl)
! 	   && !DECL_COMDAT (node->decl) && !DECL_EXTERNAL (node->decl))
      avail = AVAIL_OVERWRITABLE;
    else avail = AVAIL_AVAILABLE;
  
--- 1007,1015 ----
  
       ??? Does the C++ one definition rule allow us to always return
       AVAIL_AVAILABLE here?  That would be good reason to preserve this
!      bit.  */
  
!   else if (decl_replaceable_p (node->decl) && !DECL_EXTERNAL (node->decl))
      avail = AVAIL_OVERWRITABLE;
    else avail = AVAIL_AVAILABLE;
  
Index: gcc/except.c
===================================================================
*** gcc/except.c	(revision 168743)
--- gcc/except.c	(working copy)
*************** set_nothrow_function_flags (void)
*** 2807,2813 ****
    /* If we don't know that this implementation of the function will
       actually be used, then we must not set TREE_NOTHROW, since
       callers must not assume that this function does not throw.  */
!   if (DECL_REPLACEABLE_P (current_function_decl))
      return 0;
  
    TREE_NOTHROW (current_function_decl) = 1;
--- 2807,2813 ----
    /* If we don't know that this implementation of the function will
       actually be used, then we must not set TREE_NOTHROW, since
       callers must not assume that this function does not throw.  */
!   if (decl_replaceable_p (current_function_decl))
      return 0;
  
    TREE_NOTHROW (current_function_decl) = 1;
Index: gcc/tree-inline.c
===================================================================
*** gcc/tree-inline.c	(revision 168743)
--- gcc/tree-inline.c	(working copy)
*************** inlinable_function_p (tree fn)
*** 2103,2109 ****
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    else if (!DECL_DECLARED_INLINE_P (fn)
! 	   && DECL_REPLACEABLE_P (fn))
      inlinable = false;
  
    else if (!function_attribute_inlinable_p (fn))
--- 2103,2109 ----
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    else if (!DECL_DECLARED_INLINE_P (fn)
! 	   && decl_replaceable_p (fn))
      inlinable = false;
  
    else if (!function_attribute_inlinable_p (fn))
Index: gcc/cp/decl.c
===================================================================
*** gcc/cp/decl.c	(revision 168743)
--- gcc/cp/decl.c	(working copy)
*************** finish_function (int flags)
*** 11885,11891 ****
    if (!processing_template_decl
        && !cp_function_chain->can_throw
        && !flag_non_call_exceptions
!       && !DECL_REPLACEABLE_P (fndecl))
      TREE_NOTHROW (fndecl) = 1;
  
    /* This must come after expand_function_end because cleanups might
--- 11885,11891 ----
    if (!processing_template_decl
        && !cp_function_chain->can_throw
        && !flag_non_call_exceptions
!       && !decl_replaceable_p (fndecl))
      TREE_NOTHROW (fndecl) = 1;
  
    /* This must come after expand_function_end because cleanups might
Index: gcc/testsuite/gcc.dg/torture/pr47278-1.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr47278-1.c	(revision 0)
--- gcc/testsuite/gcc.dg/torture/pr47278-1.c	(revision 0)
***************
*** 0 ****
--- 1,4 ----
+ /* { dg-do run } */
+ /* { dg-additional-sources "pr47278-2.c" } */
+ 
+ int foo (void) { return 1; }
Index: gcc/testsuite/gcc.dg/torture/pr47278-2.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr47278-2.c	(revision 0)
--- gcc/testsuite/gcc.dg/torture/pr47278-2.c	(revision 0)
***************
*** 0 ****
--- 1,13 ----
+ extern void abort (void);
+ 
+ int __attribute__((weak,visibility("hidden"))) foo (void)
+ {
+   return 0;
+ }
+ 
+ int main()
+ {
+   if (foo() != 1)
+     abort ();
+   return 0;
+ }
