A call through a plain alias of a noipa function is optimized using
properties found in that function's body, while a direct call is not.
__attribute__ ((noipa)) int a (int v) { return v; }
extern int b (int) __attribute__ ((alias ("a")));
int
main ()
{
int c = b (1) == b (1);
if (__builtin_constant_p (c))
__builtin_abort ();
return 0;
}
Built with "gcc -O2 t.c -o t" this aborts: a is found to be const, the
flag lands on b, and the two calls fold into one, where calling a
directly keeps both. doc/extend.texi says of noipa: "It disables
interprocedural optimizations between the function with this attribute
and its callers, as if the body of the function is not available when
optimizing callers and the callers are unavailable when optimizing the
body." A call through an alias of a is a call to a. This is PR 127509.
cgraph_node::get_availability looks for the attribute on the decl it is
asked about. An alias does not carry it, so the alias comes out
available, skip_function_for_local_pure_const analyses the interposable
body because it has an alias, and set_const_flag walks the aliases and
sets the flag there. The direct call keeps its VDEF, so it is the alias
decl that changed. The patch asks about the symbol an alias refers to
as well, the immediate one and the one the chain ends at; an alias with
noipa itself, or of a symbol interposable for another reason such as a
static alias of a weak definition, behaves as before. A chain whose
noipa link is neither of those two is still not covered, and behaves
there as it does on trunk today.
pure, nothrow and malloc reach a caller the same way, and ipa-icf merges
callers that reach two equal noipa bodies through their aliases.
Measured on trunk, on a 20260903 trunk build and on gcc-13.3.0, all
aarch64-unknown-linux-gnu. r235081 and r250607 create the behaviour
between them, so GCC 8.1 onwards is affected. Not a regression.
Compiling 122 libstdc++ sources at -O2 takes 0.37 percent longer with
the patch, ten runs each side with non-overlapping ranges, and the
library those runs produce is byte identical.
Bootstrapped and regtested on aarch64-unknown-linux-gnu, no regressions.
gcc/ChangeLog:
PR ipa/127509
* cgraph.cc (cgraph_node::get_availability): Return
AVAIL_INTERPOSABLE for an alias whose immediate or ultimate
target has the noipa attribute.
gcc/testsuite/ChangeLog:
PR ipa/127509
* gcc.dg/ipa/noipa-alias-1.c: New test.
* gcc.dg/ipa/noipa-alias-2.c: New test.
* gcc.dg/ipa/noipa-alias-3.c: New test.
Signed-off-by: Rohith Kapelli <[email protected]>
---
gcc/cgraph.cc | 9 ++++++-
gcc/testsuite/gcc.dg/ipa/noipa-alias-1.c | 22 +++++++++++++++
gcc/testsuite/gcc.dg/ipa/noipa-alias-2.c | 34 ++++++++++++++++++++++++
gcc/testsuite/gcc.dg/ipa/noipa-alias-3.c | 27 +++++++++++++++++++
4 files changed, 91 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.dg/ipa/noipa-alias-1.c
create mode 100644 gcc/testsuite/gcc.dg/ipa/noipa-alias-2.c
create mode 100644 gcc/testsuite/gcc.dg/ipa/noipa-alias-3.c
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index 8e34918..feb3e7a 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -2807,7 +2807,14 @@ cgraph_node::get_availability (symtab_node *ref)
else if (transparent_alias)
ultimate_alias_target (&avail, ref);
else if (ifunc_resolver
- || lookup_attribute ("noipa", DECL_ATTRIBUTES (decl)))
+ || lookup_attribute ("noipa", DECL_ATTRIBUTES (decl))
+ || (alias
+ && (lookup_attribute ("noipa",
+ DECL_ATTRIBUTES
+ (get_alias_target ()->decl))
+ || lookup_attribute ("noipa",
+ DECL_ATTRIBUTES
+ (ultimate_alias_target ()->decl)))))
avail = AVAIL_INTERPOSABLE;
else if (!externally_visible)
avail = AVAIL_AVAILABLE;
diff --git a/gcc/testsuite/gcc.dg/ipa/noipa-alias-1.c
b/gcc/testsuite/gcc.dg/ipa/noipa-alias-1.c
new file mode 100644
index 0000000..3c87f75
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/noipa-alias-1.c
@@ -0,0 +1,22 @@
+/* A call through an alias of a noipa function must not be optimized using
+ properties discovered from the body of that function. */
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((noipa)) int
+a (int v)
+{
+ return v;
+}
+
+extern int b (int) __attribute__ ((alias ("a")));
+
+int
+main ()
+{
+ int c = b (1) == b (1);
+ if (__builtin_constant_p (c))
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/noipa-alias-2.c
b/gcc/testsuite/gcc.dg/ipa/noipa-alias-2.c
new file mode 100644
index 0000000..6536ae2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/noipa-alias-2.c
@@ -0,0 +1,34 @@
+/* Identical noipa functions must not have their callers merged because the
+ bodies reached through their aliases compare equal. */
+/* { dg-do compile } */
+/* { dg-require-alias "" } */
+/* { dg-options "-O2 -fipa-icf -fdump-ipa-icf-details" } */
+
+__attribute__ ((noipa)) int
+a1 (int v)
+{
+ return v + 1;
+}
+
+__attribute__ ((noipa)) int
+a2 (int v)
+{
+ return v + 1;
+}
+
+extern int b1 (int) __attribute__ ((alias ("a1")));
+extern int b2 (int) __attribute__ ((alias ("a2")));
+
+int
+c1 (int v)
+{
+ return b1 (v);
+}
+
+int
+c2 (int v)
+{
+ return b2 (v);
+}
+
+/* { dg-final { scan-ipa-dump-not "Unified" "icf" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/noipa-alias-3.c
b/gcc/testsuite/gcc.dg/ipa/noipa-alias-3.c
new file mode 100644
index 0000000..ed290fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/noipa-alias-3.c
@@ -0,0 +1,27 @@
+/* The properties must not reach a caller one level further out either. */
+/* { dg-do run } */
+/* { dg-require-alias "" } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((noipa)) int
+a (int v)
+{
+ return v;
+}
+
+extern int b (int) __attribute__ ((alias ("a")));
+
+__attribute__ ((noinline)) int
+c (int v)
+{
+ return b (v);
+}
+
+int
+main ()
+{
+ int d = c (1) == c (1);
+ if (__builtin_constant_p (d))
+ __builtin_abort ();
+ return 0;
+}
--
2.53.0