https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127509
Bug ID: 127509
Summary: noipa attribute is ignored for calls through an alias
of the function
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: ipa
Assignee: unassigned at gcc dot gnu.org
Reporter: kapellirohith at gmail dot com
Target Milestone: ---
Host: aarch64-unknown-linux-gnu
Target: aarch64-unknown-linux-gnu
Build: aarch64-unknown-linux-gnu
Testcase:
__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;
}
Compiled with -O2 by an unpatched trunk compiler, gcc (GCC) 17.0.0 20260917
(experimental) built from 1f54efacc155831aebc13f17f036adec2b56e2ec, this
program aborts.
Expected: the two calls through the alias stay separate, as they do when a is
called
directly. gcc/doc/extend.texi:3871-3874 at that revision 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, so its caller is one of the
callers that
sentence covers.
What happens: the body of a is analysed and the properties found there are
attached to
the alias b, so the two calls fold into one and c becomes a constant.
Measured on trunk 1f54efacc155831aebc13f17f036adec2b56e2ec. A direct call keeps
the
property and a call through a plain alias loses it, at -O1, -O2, -O3 and -Os,
with -flto,
and across translation units with -flto:
const: a call through the alias whose result is unused is removed. The kept
alias call
carries no virtual operand; the direct call carries "# .MEM_4 = VDEF
<.MEM_2(D)>".
pure: same shape, the kept alias call carries only a VUSE.
nothrow: in C++ the caller through the alias loses its landing pad and its
call to
_Unwind_Resume; the direct caller keeps both.
malloc: the caller through the alias drops the reload of the returned
pointer and
returns a constant; the direct caller reloads.
It applies equally to a static alias, to an alias of an alias, and to a
weakref.
ipa-icf is a second consumer of the same availability. With two identical noipa
functions,
each with a plain alias, and two callers that call through those aliases, the
icf dump
shows
optimized: Semantic equality hit:f1/1->f2/2
optimized: Semantic equality hit:c1/5->c2/6
Unified; Wrapper has been created.
and both calls end up relocated to the same alias. The two noipa bodies
themselves are not
merged, no_icf prevents that; what is merged is the pair of callers, using the
knowledge
that the two bodies compare equal. This cannot produce a wrong result, because
icf merges
only bodies it has proved equivalent, but it is an interprocedural decision
taken from the
contents of two noipa bodies, which is what the attribute asks the compiler not
to do.
Things that do not show the problem, each measured:
-O0; -fno-ipa-pure-const; a body with a side effect; an alias whose target
does not carry
noipa; an alias declaration that carries noipa itself; ifunc resolvers;
variable aliases,
noipa being a function attribute; a comdat member function with an alias to
its mangled
name; -fPIC with the default -fsemantic-interposition.
With -fPIC -fno-semantic-interposition the call is not removed either. The
compiler emits
the call to the target symbol rather than to the alias, which is a symbol
level choice,
not a use of the body.
Inlining, ipa-cp cloning, IPA-SRA and return value propagation do not happen
through the
alias: 0 "Inlining" lines, 0 "Creating a specialized node" lines and 0 isra
clones in the
respective dumps, on both an unpatched and a patched compiler. The target
carries noinline
and noclone, implied by noipa, and those govern the body. ipa-modref refuses
the body
outright at ipa-modref.cc:3132-3134 and does not leak through the alias
either; it is the
pass that removes such a call when the target is not noipa, shown by
-fno-ipa-modref
restoring the call in that case. ipa-reference shows no difference with or
without
-fno-ipa-reference.
Mechanism, line numbers from trunk 1f54efacc155831aebc13f17f036adec2b56e2ec:
gcc/cgraph.cc:2809-2811 cgraph_node::get_availability returns
AVAIL_INTERPOSABLE when the
decl it is asked about carries noipa. An alias does
not carry the
attribute, so the alias comes out available.
gcc/ipa-pure-const.cc:1483-1484 skip_function_for_local_pure_const analyses
an interposable
function when it has aliases, so the noipa body is
analysed.
gcc/cgraph.cc:3278-3289 cgraph_node::set_const_flag skips the interposable
node itself and
walks FOR_EACH_ALIAS, setting the flag on each
alias whose own
availability is above AVAIL_INTERPOSABLE.
gcc/cgraph.cc:3240-3246 set_const_flag_1 repeats that walk.
gcc/cgraph.cc:2999-3011 and 2977-2983 the same shape in set_nothrow_flag.
gcc/cgraph.cc:3046-3058 and 3025-3031 the same shape in set_malloc_flag.
gcc/cgraph.cc:3357-3366 set_pure_flag reaches the aliases through
call_for_symbol_thunks_and_aliases, which visits an
alias whose own
availability is above AVAIL_INTERPOSABLE.
The dump line "Declaration updated to be const: get/1" in local-pure-const1
is printed with
the name of the noipa function, but the decl that changed is the alias: the
direct call keeps
its VDEF, so the noipa decl itself did not become const.
Affected versions. Measured: trunk 1f54efacc155831aebc13f17f036adec2b56e2ec, a
trunk build
from 20260903, and gcc-13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04.1), all on
aarch64-unknown-linux-gnu. Inferred, from the two commits that create the
behaviour and not
from a build: every release from GCC 8.1 onwards. The alias walk arrived in
r235081
(69a4e898c8f, 2016-04-17), first released in GCC 7.1.0, and the noipa attribute
in r250607
(036ea39917b, 2017-07-27), first released in GCC 8.1.0. Releases 14, 15 and 16
were not
tested. Not a regression.
PR c++/119518 is the same class of problem on a different path, already fixed:
there the C++
front end set TREE_NOTHROW on a noipa function, and 94816c640ad (2025-03-31)
made
finish_function honor the attribute. The patch for that PR states that the
ipa-pure-const
passes honor noipa; this report is about the path where that does not hold, the
alias.
A patch exists. It has been bootstrapped and regression tested on
aarch64-unknown-linux-gnu with no regressions, and will be posted to
gcc-patches once this
report has a number.