Fix crash with constant initializer
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
Hi,
the attached Ada testcase compiled with -O2 -gnatn makes the compiler crash in
vect_can_force_dr_alignment_p during SLP vectorization:
if (decl_in_symtab_p (decl)
&& !symtab_node::get (decl)->can_increase_alignment_p ())
return false;
because symtab_node::get (decl) returns a null node. The phenomenon occurs
for a pair of twin symbols listed like so in .cgraph:
Opt7_Pkg.T12b/17 (Opt7_Pkg.T12b)
Type: variable definition analyzed
Visibility: semantic_interposition external public artificial
Aux: @0x44d45e0
References:
Referring: opt7_pkg__enum_name_table/13 (addr) opt7_pkg__enum_name_table/13
(addr)
Availability: not-ready
Varpool flags: initialized read-only const-value-known
Opt7_Pkg.T8b/16 (Opt7_Pkg.T8b)
Type: variable definition analyzed
Visibility: semantic_interposition external public artificial
Aux: @0x7f9fda3fff00
References:
Referring: opt7_pkg__enum_name_table/13 (addr) opt7_pkg__enum_name_table/13
(addr)
Availability: not-ready
Varpool flags: initialized read-only const-value-known
with:
opt7_pkg__enum_name_table/13 (Opt7_Pkg.Enum_Name_Table)
Type: variable definition analyzed
Visibility: semantic_interposition external public
Aux: @0x44d45e0
References: Opt7_Pkg.T8b/16 (addr) Opt7_Pkg.T8b/16 (addr) Opt7_Pkg.T12b/17
(addr) Opt7_Pkg.T12b/17 (addr)
Referring: opt7_pkg__image/2 (read) opt7_pkg__image/2 (read)
opt7_pkg__image/2 (read) opt7_pkg__image/2 (read) opt7_pkg__image/2 (read)
opt7_pkg__image/2 (read) opt7_pkg__image/2 (read) opt7_pkg__image/2 (read)
Availability: not-ready
Varpool flags: initialized read-only const-value-known
being the crux of the matter.
What happens is that symtab_remove_unreachable_nodes leaves the last symbol in
kind of a limbo state: in .remove_symbols, we have:
opt7_pkg__enum_name_table/13 (Opt7_Pkg.Enum_Name_Table)
Type: variable
Body removed by symtab_remove_unreachable_nodes
Visibility: externally_visible semantic_interposition external public
References:
Referring: opt7_pkg__image/2 (read) opt7_pkg__image/2 (read)
Availability: not_available
Varpool flags: initialized read-only const-value-known
This means that the "body" (DECL_INITIAL) of the symbol has been disregarded
during reachability analysis, causing the first two symbols to be discarded:
Reclaiming variables: Opt7_Pkg.T12b/17 Opt7_Pkg.T8b/16
but the DECL_INITIAL is explicitly preserved for later constant folding, which
makes it possible to retrofit the DECLs corresponding to the first two symbols
in the GIMPLE IR and ultimately leads vect_can_force_dr_alignment_p to crash.
The decision to disregard the "body" (DECL_INITIAL) of the symbol is made in
the first process_references present in ipa.cc:
if (node->definition && !node->in_other_partition
&& ((!DECL_EXTERNAL (node->decl) || node->alias)
|| (possible_inline_candidate_p (node)
/* We use variable constructors during late compilation for
constant folding. Keep references alive so partitioning
knows about potential references. */
|| (VAR_P (node->decl)
&& (flag_wpa
|| flag_incremental_link
== INCREMENTAL_LINK_LTO)
&& dyn_cast <varpool_node *> (node)
->ctor_useable_for_folding_p ()))))
because neither flag_wpa nor flag_incremental_link = INCREMENTAL_LINK_LTO is
true, while the decision to ultimately preserve the DECL_INITIAL is made later
in remove_unreachable_nodes:
/* Keep body if it may be useful for constant folding. */
if ((flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO)
|| ((init = ctor_for_folding (vnode->decl)) == error_mark_node))
vnode->remove_initializer ();
else
DECL_INITIAL (vnode->decl) = init;
I think that the testcase shows that the "body" of ctor_useable_for_folding_p
symbols must always be considered for reachability analysis (which could make
the above test on ctor_for_folding useless). But implementing that introduces
a regression for g++.dg/ipa/devirt-39.C, because the vtable is preserved and
in turn forces the method to be preserved, hence the special case for vtables.
The test also renames the first process_references function in ipa.cc to clear
the confusion with the second function in the same file.
Bootstrapped/regtested on x86-64/Linux, OK for the mainline?
2024-09-30 Eric Botcazou <ebotcazou@adacore.com>
* ipa.cc (process_references): Rename into...
(mark_references): ...this. Always mark referenced external
variables as reachable if they are usable for folding, except
for vtables.
(symbol_table::remove_unreachable_nodes): Adjust to renaming.
2024-09-30 Eric Botcazou <ebotcazou@adacore.com>
* gnat.dg/specs/opt7.ads: New test.
* gnat.dg/specs/opt7_pkg.ads: New helper.
* gnat.dg/specs/opt7_pkg.adb: Likewise.
@@ -124,41 +124,39 @@ possible_inline_candidate_p (symtab_node *node)
return lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl));
}
-/* Process references. */
+/* Mark references from SNODE as (members of) REACHABLE and add them to the
+ queue starting at FIRST. */
static void
-process_references (symtab_node *snode,
- symtab_node **first,
- hash_set<symtab_node *> *reachable)
+mark_references (symtab_node *snode, symtab_node **first,
+ hash_set<symtab_node *> *reachable)
{
- int i;
struct ipa_ref *ref = NULL;
- for (i = 0; snode->iterate_reference (i, ref); i++)
+ for (int i = 0; snode->iterate_reference (i, ref); i++)
{
symtab_node *node = ref->referred;
symtab_node *body = node->ultimate_alias_target ();
- if (node->definition && !node->in_other_partition
- && ((!DECL_EXTERNAL (node->decl) || node->alias)
- || (possible_inline_candidate_p (node)
- /* We use variable constructors during late compilation for
- constant folding. Keep references alive so partitioning
- knows about potential references. */
- || (VAR_P (node->decl)
- && (flag_wpa
- || flag_incremental_link
- == INCREMENTAL_LINK_LTO)
- && dyn_cast <varpool_node *> (node)
- ->ctor_useable_for_folding_p ()))))
+ if (node->definition
+ && !node->in_other_partition
+ && (!DECL_EXTERNAL (node->decl)
+ || node->alias
+ || possible_inline_candidate_p (node)
+ /* We use variable constructors during late compilation for
+ constant folding, but vtables are handled separately. */
+ || (VAR_P (node->decl)
+ && !DECL_VIRTUAL_P (node->decl)
+ && dyn_cast <varpool_node *> (node)
+ ->ctor_useable_for_folding_p ())))
{
- /* Be sure that we will not optimize out alias target
- body. */
+ /* Be sure that we will not optimize out alias target body. */
if (DECL_EXTERNAL (node->decl)
&& node->alias
&& symtab->state < IPA_SSA_AFTER_INLINING)
reachable->add (body);
reachable->add (node);
}
+
enqueue_node (node, first, reachable);
}
}
@@ -409,8 +407,9 @@ symbol_table::remove_unreachable_nodes (FILE *file)
&& !reachable.add (next))
enqueue_node (next, &first, &reachable);
}
+
/* Mark references as reachable. */
- process_references (node, &first, &reachable);
+ mark_references (node, &first, &reachable);
}
if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))