diff mbox

PR rtl-optimization/32219: optimizer causees wrong code in pic/hidden/weak symbol checking

Message ID 54DD3FA9.3010609@redhat.com
State New
Headers show

Commit Message

Richard Henderson Feb. 13, 2015, 12:04 a.m. UTC
On 02/12/2015 03:05 PM, H.J. Lu wrote:
> @@ -6830,9 +6830,15 @@ default_binds_local_p_2 (const_tree exp, bool shlib, bool weak_dominate)
>    bool resolved_locally = false;
>    if (symtab_node *node = symtab_node::get (exp))
>      {
> -      /* When not building shared library and weak_dominate is true:
> -         weak, common or initialized symbols are resolved locally.  */
> -      if ((weak_dominate && !shlib && node->definition)
> +      /* When weak_dominate is true and not building shared library or
> +	 non-default visibility is specified by user: weak, common or
> +	 initialized symbols are resolved locally.
> +	 */
> +      if (((!shlib
> +	    || (DECL_VISIBILITY_SPECIFIED (exp)
> +		&& DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT))
> +	   && weak_dominate
> +	   && node->definition)
>  	  || node->in_other_partition
>  	  || resolution_local_p (node->resolution))
>  	resolved_locally = true;

Hum.  I don't find that particularly easy to reason with either.

How about this?  I'm about half-way through regression testing on it.

I re-instated the use of resolution_to_local_definition_p, and attempt to infer
a proper value for that when lto isn't in use.  I use this to eliminate only
undef-weak early, rather than non-dominate weak.

I re-instated the use of the existence of the local definition in the
DECL_VISIBILITY test.  But unlike before, I reason that this allows us to
eliminate the second visibility check.  We either have an assertion from the
user (SPECIFIED), or we know we have a definition.  We no longer rely on the
DECL_EXTERNAL test in the middle eliminating symbols without a definition.

I shuffled some of the "return false" tests around among themselves, attempting
to put the simplest test first.  No change in behavior there.

(First patch is delta from the 5-patch bundle; second patch is the
composite from trunk, to avoid confusion.)


r~

diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index f2c40d4..942826d 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -442,8 +442,10 @@ cgraph_node::finalize_function (tree decl, bool no_collect)
       node->local.redefined_extern_inline = true;
     }
 
-  notice_global_symbol (decl);
+  /* Set definition first before calling notice_global_symbol so that
+     it is available to notice_global_symbol.  */
   node->definition = true;
+  notice_global_symbol (decl);
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
 
   /* With -fkeep-inline-functions we are keeping all inline functions except
@@ -803,8 +805,10 @@ varpool_node::finalize_decl (tree decl)
 
   if (node->definition)
     return;
-  notice_global_symbol (decl);
+  /* Set definition first before calling notice_global_symbol so that
+     it is available to notice_global_symbol.  */
   node->definition = true;
+  notice_global_symbol (decl);
   if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)
       /* Traditionally we do not eliminate static variables when not
 	 optimizing and when not doing toplevel reoder.  */
diff --git a/gcc/testsuite/gcc.dg/visibility-22.c b/gcc/testsuite/gcc.dg/visibility-22.c
new file mode 100644
index 0000000..52f59be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/visibility-22.c
@@ -0,0 +1,17 @@
+/* PR target/32219 */
+/* { dg-do run } */
+/* { dg-require-visibility "" } */
+/* { dg-options "-O2 -fPIC" { target fpic } } */
+/* This test requires support for undefined weak symbols.  This support
+   is not available on hppa*-*-hpux*.  The test is skipped rather than
+   xfailed to suppress the warning that would otherwise arise.  */
+/* { dg-skip-if "" { "hppa*-*-hpux*" "*-*-aix*" "*-*-darwin*" } "*" { "" } }  */
+
+extern void foo () __attribute__((weak,visibility("hidden")));
+int
+main()
+{
+  if (foo)
+    foo ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/visibility-23.c b/gcc/testsuite/gcc.dg/visibility-23.c
new file mode 100644
index 0000000..0fa9ef4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/visibility-23.c
@@ -0,0 +1,15 @@
+/* PR target/32219 */
+/* { dg-do compile } */
+/* { dg-require-visibility "" } */
+/* { dg-final { scan-hidden "foo" } } */
+/* { dg-options "-O2 -fPIC" { target fpic } } */
+/* { dg-skip-if "" { "hppa*-*-hpux*" "*-*-aix*" "*-*-darwin*" } "*" { "" } }  */
+
+extern void foo () __attribute__((weak,visibility("hidden")));
+int
+main()
+{
+  if (foo)
+    foo ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-1.c b/gcc/testsuite/gcc.target/i386/pr32219-1.c
new file mode 100644
index 0000000..5bd80a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie" } */
+
+/* Common symbol with -fpie.  */
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-2.c b/gcc/testsuite/gcc.target/i386/pr32219-2.c
new file mode 100644
index 0000000..0cf2eb5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-2.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic" } */
+
+/* Common symbol with -fpic.  */
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-3.c b/gcc/testsuite/gcc.target/i386/pr32219-3.c
new file mode 100644
index 0000000..911f2a5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie" } */
+
+/* Weak common symbol with -fpie.  */
+__attribute__((weak))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-4.c b/gcc/testsuite/gcc.target/i386/pr32219-4.c
new file mode 100644
index 0000000..3d43439
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-4.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic" } */
+
+/* Weak common symbol with -fpic.  */
+__attribute__((weak))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-5.c b/gcc/testsuite/gcc.target/i386/pr32219-5.c
new file mode 100644
index 0000000..ee7442e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-5.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie" } */
+
+/* Initialized symbol with -fpie.  */
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-6.c b/gcc/testsuite/gcc.target/i386/pr32219-6.c
new file mode 100644
index 0000000..f261433
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-6.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic" } */
+
+/* Initialized symbol with -fpic.  */
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-7.c b/gcc/testsuite/gcc.target/i386/pr32219-7.c
new file mode 100644
index 0000000..12aaf72
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-7.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie" } */
+
+/* Weak initialized symbol with -fpie.  */
+__attribute__((weak))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-8.c b/gcc/testsuite/gcc.target/i386/pr32219-8.c
new file mode 100644
index 0000000..2e4fba0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-8.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic" } */
+
+/* Weak initialized symbol with -fpic.  */
+__attribute__((weak))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx\\(%rip\\), %eax" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]xxx@GOTOFF\\(%\[^,\]*\\), %eax" { target ia32 } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]xxx@GOT\\(%\[^,\]*\\), %eax" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr64317.c b/gcc/testsuite/gcc.target/i386/pr64317.c
index 33f5b5d..32969fc 100644
--- a/gcc/testsuite/gcc.target/i386/pr64317.c
+++ b/gcc/testsuite/gcc.target/i386/pr64317.c
@@ -1,7 +1,7 @@
 /* { dg-do compile { target { *-*-linux* && ia32 } } } */
 /* { dg-options "-O2 -fpie" } */
 /* { dg-final { scan-assembler "addl\[ \\t\]+\[$\]_GLOBAL_OFFSET_TABLE_, %ebx" } } */
-/* { dg-final { scan-assembler "movl\[ \\t\]+c@GOT\[(\]%ebx\[)\]" } } */
+/* { dg-final { scan-assembler "movl\[ \\t\]+c@GOTOFF\[(\]%ebx\[)\]" } } */
 /* { dg-final { scan-assembler-not "movl\[ \\t\]+\[0-9]+\[(\]%esp\[)\], %ebx" } } */
 long c;
 
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 3f62fca..0211306 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -6802,97 +6802,96 @@ resolution_local_p (enum ld_plugin_symbol_resolution resolution)
 	  || resolution == LDPR_RESOLVED_EXEC);
 }
 
-/* Assume ELF-ish defaults, since that's pretty much the most liberal
-   wrt cross-module name binding.  */
-
-bool
-default_binds_local_p (const_tree exp)
-{
-  return default_binds_local_p_1 (exp, flag_shlib);
-}
-
-bool
-default_binds_local_p_1 (const_tree exp, int shlib)
+static bool
+default_binds_local_p_2 (const_tree exp, bool shlib, bool weak_dominate)
 {
-  bool local_p;
-  bool resolved_locally = false;
-  bool resolved_to_local_def = false;
-
-  /* With resolution file in hands, take look into resolutions.
-     We can't just return true for resolved_locally symbols,
-     because dynamic linking might overwrite symbols
-     in shared libraries.  */
-  if (TREE_CODE (exp) == VAR_DECL && TREE_PUBLIC (exp)
-      && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
-    {
-      varpool_node *vnode = varpool_node::get (exp);
-      if (vnode && (resolution_local_p (vnode->resolution) || vnode->in_other_partition))
-	resolved_locally = true;
-      if (vnode
-	  && resolution_to_local_definition_p (vnode->resolution))
-	resolved_to_local_def = true;
-    }
-  else if (TREE_CODE (exp) == FUNCTION_DECL && TREE_PUBLIC (exp))
-    {
-      struct cgraph_node *node = cgraph_node::get (exp);
-      if (node
-	  && (resolution_local_p (node->resolution) || node->in_other_partition))
-	resolved_locally = true;
-      if (node
-	  && resolution_to_local_definition_p (node->resolution))
-	resolved_to_local_def = true;
-    }
-
   /* A non-decl is an entry in the constant pool.  */
   if (!DECL_P (exp))
-    local_p = true;
+    return true;
+
   /* Weakrefs may not bind locally, even though the weakref itself is always
      static and therefore local.  Similarly, the resolver for ifunc functions
      might resolve to a non-local function.
      FIXME: We can resolve the weakref case more curefuly by looking at the
      weakref alias.  */
-  else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp))
+  if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp))
 	   || (TREE_CODE (exp) == FUNCTION_DECL
 	       && lookup_attribute ("ifunc", DECL_ATTRIBUTES (exp))))
-    local_p = false;
+    return false;
+
   /* Static variables are always local.  */
-  else if (! TREE_PUBLIC (exp))
-    local_p = true;
-  /* A variable is local if the user has said explicitly that it will
-     be.  */
-  else if ((DECL_VISIBILITY_SPECIFIED (exp)
-	    || resolved_to_local_def)
-	   && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
-    local_p = true;
-  /* Variables defined outside this object might not be local.  */
-  else if (DECL_EXTERNAL (exp) && !resolved_locally)
-    local_p = false;
-  /* If defined in this object and visibility is not default, must be
-     local.  */
-  else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
-    local_p = true;
-  /* Default visibility weak data can be overridden by a strong symbol
-     in another module and so are not local.  */
-  else if (DECL_WEAK (exp)
-	   && !resolved_locally)
-    local_p = false;
+  if (! TREE_PUBLIC (exp))
+    return true;
+
+  /* With resolution file in hand, take look into resolutions.
+     We can't just return true for resolved_locally symbols,
+     because dynamic linking might overwrite symbols
+     in shared libraries.  */
+  bool resolved_locally = false;
+  bool defined_locally = false;
+  if (symtab_node *node = symtab_node::get (exp))
+    {
+      if (node->definition || node->in_other_partition)
+	{
+	  defined_locally = true;
+	  resolved_locally = (weak_dominate && !shlib);
+	}
+      if (resolution_to_local_definition_p (node->resolution))
+	defined_locally = resolved_locally = true;
+      else if (resolution_local_p (node->resolution))
+	resolved_locally = true;
+    }
+
+  /* Undefined weak symbols are never defined locally.  */
+  if (DECL_WEAK (exp) && !defined_locally)
+    return false;
+
+  /* A symbol is local if the user has said explicitly that it will be,
+     or if we have a definition for the symbol.  We cannot infer visibility
+     for undefined symbols.  */
+  if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT
+      && (DECL_VISIBILITY_SPECIFIED (exp) || defined_locally))
+    return true;
+
   /* If PIC, then assume that any global name can be overridden by
      symbols resolved from other modules.  */
-  else if (shlib)
-    local_p = false;
+  if (shlib)
+    return false;
+
+  /* Variables defined outside this object might not be local.  */
+  if (DECL_EXTERNAL (exp) && !resolved_locally)
+    return false;
+
+  /* Non-dominant weak symbols are not defined locally.  */
+  if (DECL_WEAK (exp) && !resolved_locally)
+    return false;
+
   /* Uninitialized COMMON variable may be unified with symbols
      resolved from other modules.  */
-  else if (DECL_COMMON (exp)
-	   && !resolved_locally
-	   && (DECL_INITIAL (exp) == NULL
-	       || (!in_lto_p && DECL_INITIAL (exp) == error_mark_node)))
-    local_p = false;
+  if (DECL_COMMON (exp)
+      && !resolved_locally
+      && (DECL_INITIAL (exp) == NULL
+	  || (!in_lto_p && DECL_INITIAL (exp) == error_mark_node)))
+    return false;
+
   /* Otherwise we're left with initialized (or non-common) global data
      which is of necessity defined locally.  */
-  else
-    local_p = true;
+  return true;
+}
+
+/* Assume ELF-ish defaults, since that's pretty much the most liberal
+   wrt cross-module name binding.  */
+
+bool
+default_binds_local_p (const_tree exp)
+{
+  return default_binds_local_p_2 (exp, flag_shlib != 0, true);
+}
 
-  return local_p;
+bool
+default_binds_local_p_1 (const_tree exp, int shlib)
+{
+  return default_binds_local_p_2 (exp, shlib != 0, false);
 }
 
 /* Return true when references to DECL must bind to current definition in
@@ -6914,22 +6913,14 @@ decl_binds_to_current_def_p (const_tree decl)
     return false;
   if (!TREE_PUBLIC (decl))
     return true;
+
   /* When resolution is available, just use it.  */
-  if (TREE_CODE (decl) == VAR_DECL
-      && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+  if (symtab_node *node = symtab_node::get (decl))
     {
-      varpool_node *vnode = varpool_node::get (decl);
-      if (vnode
-	  && vnode->resolution != LDPR_UNKNOWN)
-	return resolution_to_local_definition_p (vnode->resolution);
-    }
-  else if (TREE_CODE (decl) == FUNCTION_DECL)
-    {
-      struct cgraph_node *node = cgraph_node::get (decl);
-      if (node
-	  && node->resolution != LDPR_UNKNOWN)
+      if (node->resolution != LDPR_UNKNOWN)
 	return resolution_to_local_definition_p (node->resolution);
     }
+
   /* Otherwise we have to assume the worst for DECL_WEAK (hidden weaks
      binds locally but still can be overwritten), DECL_COMMON (can be merged
      with a non-common definition somewhere in the same module) or
@@ -7449,9 +7440,10 @@ default_elf_asm_output_external (FILE *file ATTRIBUTE_UNUSED,
 {
   /* We output the name if and only if TREE_SYMBOL_REFERENCED is
      set in order to avoid putting out names that are never really
-     used. */
+     used.  Always output visibility specified in the source.  */
   if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
-      && targetm.binds_local_p (decl))
+      && (DECL_VISIBILITY_SPECIFIED (decl)
+	  || targetm.binds_local_p (decl)))
     maybe_assemble_visibility (decl);
 }

Comments

H.J. Lu Feb. 13, 2015, 4:14 a.m. UTC | #1
On Thu, Feb 12, 2015 at 4:04 PM, Richard Henderson <rth@redhat.com> wrote:
> On 02/12/2015 03:05 PM, H.J. Lu wrote:
>> @@ -6830,9 +6830,15 @@ default_binds_local_p_2 (const_tree exp, bool shlib, bool weak_dominate)
>>    bool resolved_locally = false;
>>    if (symtab_node *node = symtab_node::get (exp))
>>      {
>> -      /* When not building shared library and weak_dominate is true:
>> -         weak, common or initialized symbols are resolved locally.  */
>> -      if ((weak_dominate && !shlib && node->definition)
>> +      /* When weak_dominate is true and not building shared library or
>> +      non-default visibility is specified by user: weak, common or
>> +      initialized symbols are resolved locally.
>> +      */
>> +      if (((!shlib
>> +         || (DECL_VISIBILITY_SPECIFIED (exp)
>> +             && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT))
>> +        && weak_dominate
>> +        && node->definition)
>>         || node->in_other_partition
>>         || resolution_local_p (node->resolution))
>>       resolved_locally = true;
>
> Hum.  I don't find that particularly easy to reason with either.
>
> How about this?  I'm about half-way through regression testing on it.
>
> I re-instated the use of resolution_to_local_definition_p, and attempt to infer
> a proper value for that when lto isn't in use.  I use this to eliminate only
> undef-weak early, rather than non-dominate weak.
>
> I re-instated the use of the existence of the local definition in the
> DECL_VISIBILITY test.  But unlike before, I reason that this allows us to
> eliminate the second visibility check.  We either have an assertion from the
> user (SPECIFIED), or we know we have a definition.  We no longer rely on the
> DECL_EXTERNAL test in the middle eliminating symbols without a definition.
>
> I shuffled some of the "return false" tests around among themselves, attempting
> to put the simplest test first.  No change in behavior there.
>
> (First patch is delta from the 5-patch bundle; second patch is the
> composite from trunk, to avoid confusion.)
>
>

I tried the second patch.  Results look good on Linux/x86-64.

Thanks.
Richard Henderson Feb. 13, 2015, 5:11 a.m. UTC | #2
On 02/12/2015 08:14 PM, H.J. Lu wrote:
> I tried the second patch.  Results look good on Linux/x86-64.

Thanks.  My results concurr.  I went ahead and installed the patch as posted.


r~


2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>
            Richard Henderson  <rth@redhat.com>

        PR rtl/32219
        * cgraphunit.c (cgraph_node::finalize_function): Set definition
        before notice_global_symbol.
        (varpool_node::finalize_decl): Likewise.
        * varasm.c (default_binds_local_p_2): Rename from
        default_binds_local_p_1, add weak_dominate argument.  Use direct
        returns instead of assigning to local variable.  Unify varpool and
        cgraph paths via symtab_node.  Reject undef weak variables before
        testing visibility.  Reorder tests for simplicity.
        (default_binds_local_p): Use default_binds_local_p_2.
        (default_binds_local_p_1): Likewise.
        (decl_binds_to_current_def_p): Unify varpool and cgraph paths
        via symtab_node.
        (default_elf_asm_output_external): Emit visibility when specified.

2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>

        PR rtl/32219
        * gcc.dg/visibility-22.c: New test.
        * gcc.dg/visibility-23.c: New test.
        * gcc.target/i386/pr32219-1.c: New test.
        * gcc.target/i386/pr32219-2.c: New test.
        * gcc.target/i386/pr32219-3.c: New test.
        * gcc.target/i386/pr32219-4.c: New test.
        * gcc.target/i386/pr32219-5.c: New test.
        * gcc.target/i386/pr32219-6.c: New test.
        * gcc.target/i386/pr32219-7.c: New test.
        * gcc.target/i386/pr32219-8.c: New test.
        * gcc.target/i386/pr64317.c: Expect GOTOFF, not GOT.
Alex Velenko Feb. 18, 2015, 2:17 p.m. UTC | #3
On 13/02/15 05:11, Richard Henderson wrote:
> On 02/12/2015 08:14 PM, H.J. Lu wrote:
>> I tried the second patch.  Results look good on Linux/x86-64.
>
> Thanks.  My results concurr.  I went ahead and installed the patch as posted.
>
>
> r~
>
>
> 2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>
>              Richard Henderson  <rth@redhat.com>
>
>          PR rtl/32219
>          * cgraphunit.c (cgraph_node::finalize_function): Set definition
>          before notice_global_symbol.
>          (varpool_node::finalize_decl): Likewise.
>          * varasm.c (default_binds_local_p_2): Rename from
>          default_binds_local_p_1, add weak_dominate argument.  Use direct
>          returns instead of assigning to local variable.  Unify varpool and
>          cgraph paths via symtab_node.  Reject undef weak variables before
>          testing visibility.  Reorder tests for simplicity.
>          (default_binds_local_p): Use default_binds_local_p_2.
>          (default_binds_local_p_1): Likewise.
>          (decl_binds_to_current_def_p): Unify varpool and cgraph paths
>          via symtab_node.
>          (default_elf_asm_output_external): Emit visibility when specified.
>
> 2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>
>
>          PR rtl/32219
>          * gcc.dg/visibility-22.c: New test.
>          * gcc.dg/visibility-23.c: New test.
>          * gcc.target/i386/pr32219-1.c: New test.
>          * gcc.target/i386/pr32219-2.c: New test.
>          * gcc.target/i386/pr32219-3.c: New test.
>          * gcc.target/i386/pr32219-4.c: New test.
>          * gcc.target/i386/pr32219-5.c: New test.
>          * gcc.target/i386/pr32219-6.c: New test.
>          * gcc.target/i386/pr32219-7.c: New test.
>          * gcc.target/i386/pr32219-8.c: New test.
>          * gcc.target/i386/pr64317.c: Expect GOTOFF, not GOT.
>

Hi all,
By changing behaviour of varasm.c:default_binds_local_p, this patch 
changes behaviour of gcc/config/arm/arm.c:arm_function_in_section_p and 
through it breaks gcc/config/arm/arm.c:arm_is_long_call_p for weak symbols.

As a result, I get regression for gcc.target/arm/long-calls-1.c on
arm-none-eabi:
FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l1\n
FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l3\n

In https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html there
is a description for -mlong-calls.

This has to be fixed.

Kind regards,
Alex
H.J. Lu Feb. 19, 2015, 1:10 p.m. UTC | #4
On Wed, Feb 18, 2015 at 6:17 AM, Alex Velenko <Alex.Velenko@arm.com> wrote:
> On 13/02/15 05:11, Richard Henderson wrote:
>>
>> On 02/12/2015 08:14 PM, H.J. Lu wrote:
>>>
>>> I tried the second patch.  Results look good on Linux/x86-64.
>>
>>
>> Thanks.  My results concurr.  I went ahead and installed the patch as
>> posted.
>>
>>
>> r~
>>
>>
>> 2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>
>>              Richard Henderson  <rth@redhat.com>
>>
>>          PR rtl/32219
>>          * cgraphunit.c (cgraph_node::finalize_function): Set definition
>>          before notice_global_symbol.
>>          (varpool_node::finalize_decl): Likewise.
>>          * varasm.c (default_binds_local_p_2): Rename from
>>          default_binds_local_p_1, add weak_dominate argument.  Use direct
>>          returns instead of assigning to local variable.  Unify varpool
>> and
>>          cgraph paths via symtab_node.  Reject undef weak variables before
>>          testing visibility.  Reorder tests for simplicity.
>>          (default_binds_local_p): Use default_binds_local_p_2.
>>          (default_binds_local_p_1): Likewise.
>>          (decl_binds_to_current_def_p): Unify varpool and cgraph paths
>>          via symtab_node.
>>          (default_elf_asm_output_external): Emit visibility when
>> specified.
>>
>> 2015-02-12  H.J. Lu  <hongjiu.lu@intel.com>
>>
>>          PR rtl/32219
>>          * gcc.dg/visibility-22.c: New test.
>>          * gcc.dg/visibility-23.c: New test.
>>          * gcc.target/i386/pr32219-1.c: New test.
>>          * gcc.target/i386/pr32219-2.c: New test.
>>          * gcc.target/i386/pr32219-3.c: New test.
>>          * gcc.target/i386/pr32219-4.c: New test.
>>          * gcc.target/i386/pr32219-5.c: New test.
>>          * gcc.target/i386/pr32219-6.c: New test.
>>          * gcc.target/i386/pr32219-7.c: New test.
>>          * gcc.target/i386/pr32219-8.c: New test.
>>          * gcc.target/i386/pr64317.c: Expect GOTOFF, not GOT.
>>
>
> Hi all,
> By changing behaviour of varasm.c:default_binds_local_p, this patch changes
> behaviour of gcc/config/arm/arm.c:arm_function_in_section_p and through it
> breaks gcc/config/arm/arm.c:arm_is_long_call_p for weak symbols.
>
> As a result, I get regression for gcc.target/arm/long-calls-1.c on
> arm-none-eabi:
> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l1\n
> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l3\n
>
> In https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html there
> is a description for -mlong-calls.

I know nothing about arm.  I built a cross compiler and got:

[hjl@gnu-tools-1 gcc]$ cat /tmp/z.i
const char *
__attribute__((long_call)) __attribute__((noinline))
strong_l1 (void) { return "strong_l1"; }
const char * call_strong_l1 (void) { return strong_l1 () + 1; }
const char * sibcall_strong_l1 (void) { return strong_l1 (); }
const char *
__attribute__((weak)) __attribute__((long_call)) __attribute__((noinline))
weak_l1 (void) { return "weak_l1"; }
const char * call_weak_l1 (void) { return weak_l1 () + 1; }
const char * sibcall_weak_l1 (void) { return weak_l1 (); }
[hjl@gnu-tools-1 gcc]$ ./xgcc -B./ -S -O2 /tmp/z.i
[hjl@gnu-tools-1 gcc]$ grep "b.*        strong_l1" z.s
.global strong_l1
bl strong_l1
b strong_l1
[hjl@gnu-tools-1 gcc]$ grep "b.*        weak_l1" z.s
bl weak_l1
bl weak_l1
[hjl@gnu-tools-1 gcc]$

Can someone tell me what is wrong with the output and why?
Richard Henderson Feb. 19, 2015, 2:16 p.m. UTC | #5
On 02/18/2015 06:17 AM, Alex Velenko wrote:
> By changing behaviour of varasm.c:default_binds_local_p, this patch changes
> behaviour of gcc/config/arm/arm.c:arm_function_in_section_p and through it
> breaks gcc/config/arm/arm.c:arm_is_long_call_p for weak symbols.
> 
> As a result, I get regression for gcc.target/arm/long-calls-1.c on
> arm-none-eabi:
> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l1\n
> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l3\n
> 
> In https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html there
> is a description for -mlong-calls.
> 
> This has to be fixed.

Please file a bug, for tracking purposes.

That said, it looks as if arm_function_in_section_p should be using
decl_binds_to_current_def_p instead of targetm.binds_local_p.

That will properly reject weak symbols within a given module until we receive
extra information from LTO indicating when a weak definition turns out to be
the prevailing definition.


r~
Alex Velenko Feb. 19, 2015, 5:08 p.m. UTC | #6
On 19/02/15 14:16, Richard Henderson wrote:
> On 02/18/2015 06:17 AM, Alex Velenko wrote:
>> By changing behaviour of varasm.c:default_binds_local_p, this patch changes
>> behaviour of gcc/config/arm/arm.c:arm_function_in_section_p and through it
>> breaks gcc/config/arm/arm.c:arm_is_long_call_p for weak symbols.
>>
>> As a result, I get regression for gcc.target/arm/long-calls-1.c on
>> arm-none-eabi:
>> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l1\n
>> FAIL: gcc.target/arm/long-calls-1.c scan-assembler-not \tbl?\tweak_l3\n
>>
>> In https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html there
>> is a description for -mlong-calls.
>>
>> This has to be fixed.
>
> Please file a bug, for tracking purposes.
>
> That said, it looks as if arm_function_in_section_p should be using
> decl_binds_to_current_def_p instead of targetm.binds_local_p.
>
> That will properly reject weak symbols within a given module until we receive
> extra information from LTO indicating when a weak definition turns out to be
> the prevailing definition.
>
>
> r~
>

Hi Richard,
Thank you for your reply.
Here is the bug report.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65121

Your suggestion seem to fix gcc.target/arm/long-calls-1.c, but has to be 
thoroughly tested.

Kind regards,
Alex
diff mbox

Patch

diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 057eedb..942826d 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -442,8 +442,10 @@  cgraph_node::finalize_function (tree decl, bool no_collect)
       node->local.redefined_extern_inline = true;
     }
 
-  notice_global_symbol (decl);
+  /* Set definition first before calling notice_global_symbol so that
+     it is available to notice_global_symbol.  */
   node->definition = true;
+  notice_global_symbol (decl);
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
 
   /* With -fkeep-inline-functions we are keeping all inline functions except
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 9f79416..0211306 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -6828,37 +6828,42 @@  default_binds_local_p_2 (const_tree exp, bool shlib, bool weak_dominate)
      because dynamic linking might overwrite symbols
      in shared libraries.  */
   bool resolved_locally = false;
+  bool defined_locally = false;
   if (symtab_node *node = symtab_node::get (exp))
     {
-      /* When not building shared library and weak_dominate is true:
-         weak, common or initialized symbols are resolved locally.  */
-      if ((weak_dominate && !shlib && node->definition)
-	  || node->in_other_partition
-	  || resolution_local_p (node->resolution))
+      if (node->definition || node->in_other_partition)
+	{
+	  defined_locally = true;
+	  resolved_locally = (weak_dominate && !shlib);
+	}
+      if (resolution_to_local_definition_p (node->resolution))
+	defined_locally = resolved_locally = true;
+      else if (resolution_local_p (node->resolution))
 	resolved_locally = true;
     }
 
-  /* Undefined (or non-dominant) weak symbols are not defined locally.  */
-  if (DECL_WEAK (exp) && !resolved_locally)
+  /* Undefined weak symbols are never defined locally.  */
+  if (DECL_WEAK (exp) && !defined_locally)
     return false;
 
-  /* A variable is local if the user has said explicitly that it will be.  */
-  if (DECL_VISIBILITY_SPECIFIED (exp)
-      && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
+  /* A symbol is local if the user has said explicitly that it will be,
+     or if we have a definition for the symbol.  We cannot infer visibility
+     for undefined symbols.  */
+  if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT
+      && (DECL_VISIBILITY_SPECIFIED (exp) || defined_locally))
     return true;
 
+  /* If PIC, then assume that any global name can be overridden by
+     symbols resolved from other modules.  */
+  if (shlib)
+    return false;
+
   /* Variables defined outside this object might not be local.  */
   if (DECL_EXTERNAL (exp) && !resolved_locally)
     return false;
 
-  /* If defined in this object and visibility is not default,
-     must be local.  */
-  if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
-    return true;
-
-  /* If PIC, then assume that any global name can be overridden by
-     symbols resolved from other modules.  */
-  if (shlib)
+  /* Non-dominant weak symbols are not defined locally.  */
+  if (DECL_WEAK (exp) && !resolved_locally)
     return false;
 
   /* Uninitialized COMMON variable may be unified with symbols