This changes function version structures to maintain the default version as the first declaration in the linked data structures by giving priority to the set containing the default when constructing the structure.
This allows for removing logic for moving the default to the first position which was duplicated across target specific code and enables easier reasoning about function sets when checking for a default. gcc/ChangeLog: * cgraph.cc (cgraph_node::record_function_versions): Update to implicitly keep default first. * config/aarch64/aarch64.cc (aarch64_get_function_versions_dispatcher): Remove reordering. * config/i386/i386-features.cc (ix86_get_function_versions_dispatcher): Remove reordering. * config/riscv/riscv.cc (riscv_get_function_versions_dispatcher): Remove reordering. * config/rs6000/rs6000.cc (rs6000_get_function_versions_dispatcher): Remove reordering. --- gcc/cgraph.cc | 27 ++++++++++++++++----- gcc/config/aarch64/aarch64.cc | 37 +++++++--------------------- gcc/config/i386/i386-features.cc | 33 ++++--------------------- gcc/config/riscv/riscv.cc | 41 +++++++------------------------- gcc/config/rs6000/rs6000.cc | 35 +++++---------------------- 5 files changed, 49 insertions(+), 124 deletions(-)
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index d0b19ad850e..bf6b43d00db 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -247,7 +247,9 @@ cgraph_node::record_function_versions (tree decl1, tree decl2) decl1_v = decl1_node->function_version (); decl2_v = decl2_node->function_version (); - if (decl1_v != NULL && decl2_v != NULL) + /* If the nodes are already linked, skip. */ + if ((decl1_v != NULL && (decl1_v->next || decl1_v->prev)) + && (decl2_v != NULL && (decl2_v->next || decl2_v->prev))) return; if (decl1_v == NULL) @@ -256,18 +258,31 @@ cgraph_node::record_function_versions (tree decl1, tree decl2) if (decl2_v == NULL) decl2_v = decl2_node->insert_new_function_version (); - /* Chain decl2_v and decl1_v. All semantically identical versions - will be chained together. */ + gcc_assert (decl1_v); + gcc_assert (decl2_v); before = decl1_v; after = decl2_v; + /* Go to first after node. */ + while (after->prev != NULL) + after = after->prev; + + while (before->prev != NULL) + before = before->prev; + + /* Potentially swap the nodes to maintain the default always being in the + first position. */ + if (before->next + ? !is_function_default_version (before->this_node->decl) + : is_function_default_version (after->this_node->decl)) + std::swap (before, after); + + /* Go to last node of before. */ while (before->next != NULL) before = before->next; - while (after->prev != NULL) - after= after->prev; - + /* Chain decl2_v and decl1_v. */ before->next = after; after->prev = before; } diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index f5f23f6ff4b..418f3039329 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -20650,7 +20650,6 @@ aarch64_get_function_versions_dispatcher (void *decl) struct cgraph_node *node = NULL; struct cgraph_node *default_node = NULL; struct cgraph_function_version_info *node_v = NULL; - struct cgraph_function_version_info *first_v = NULL; tree dispatch_decl = NULL; @@ -20667,37 +20666,17 @@ aarch64_get_function_versions_dispatcher (void *decl) if (node_v->dispatcher_resolver != NULL) return node_v->dispatcher_resolver; - /* Find the default version and make it the first node. */ - first_v = node_v; - /* Go to the beginning of the chain. */ - while (first_v->prev != NULL) - first_v = first_v->prev; - default_version_info = first_v; - while (default_version_info != NULL) - { - if (get_feature_mask_for_version - (default_version_info->this_node->decl) == 0ULL) - break; - default_version_info = default_version_info->next; - } - - /* If there is no default node, just return NULL. */ - if (default_version_info == NULL) - return NULL; - - /* Make default info the first node. */ - if (first_v != default_version_info) - { - default_version_info->prev->next = default_version_info->next; - if (default_version_info->next) - default_version_info->next->prev = default_version_info->prev; - first_v->prev = default_version_info; - default_version_info->next = first_v; - default_version_info->prev = NULL; - } + /* The default node is always the beginning of the chain. */ + default_version_info = node_v; + while (default_version_info->prev != NULL) + default_version_info = default_version_info->prev; default_node = default_version_info->this_node; + /* If there is no default version, just return NULL. */ + if (!is_function_default_version (default_node->decl)) + return NULL; + if (targetm.has_ifunc_p ()) { struct cgraph_function_version_info *it_v = NULL; diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc index c35ac24fd8a..2eb3a21bb5d 100644 --- a/gcc/config/i386/i386-features.cc +++ b/gcc/config/i386/i386-features.cc @@ -3962,7 +3962,6 @@ ix86_get_function_versions_dispatcher (void *decl) struct cgraph_node *node = NULL; struct cgraph_node *default_node = NULL; struct cgraph_function_version_info *node_v = NULL; - struct cgraph_function_version_info *first_v = NULL; tree dispatch_decl = NULL; @@ -3979,37 +3978,15 @@ ix86_get_function_versions_dispatcher (void *decl) if (node_v->dispatcher_resolver != NULL) return node_v->dispatcher_resolver; - /* Find the default version and make it the first node. */ - first_v = node_v; - /* Go to the beginning of the chain. */ - while (first_v->prev != NULL) - first_v = first_v->prev; - default_version_info = first_v; - while (default_version_info != NULL) - { - if (is_function_default_version - (default_version_info->this_node->decl)) - break; - default_version_info = default_version_info->next; - } + /* The default node is always the beginning of the chain. */ + default_version_info = node_v; + while (default_version_info->prev != NULL) + default_version_info = default_version_info->prev; /* If there is no default node, just return NULL. */ - if (default_version_info == NULL) + if (!is_function_default_version (default_node->decl)) return NULL; - /* Make default info the first node. */ - if (first_v != default_version_info) - { - default_version_info->prev->next = default_version_info->next; - if (default_version_info->next) - default_version_info->next->prev = default_version_info->prev; - first_v->prev = default_version_info; - default_version_info->next = first_v; - default_version_info->prev = NULL; - } - - default_node = default_version_info->this_node; - #if defined (ASM_OUTPUT_TYPE_DIRECTIVE) if (targetm.has_ifunc_p ()) { diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 9bf7713139f..e5aa99a4965 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -13726,7 +13726,6 @@ riscv_get_function_versions_dispatcher (void *decl) struct cgraph_node *node = NULL; struct cgraph_node *default_node = NULL; struct cgraph_function_version_info *node_v = NULL; - struct cgraph_function_version_info *first_v = NULL; tree dispatch_decl = NULL; @@ -13743,41 +13742,19 @@ riscv_get_function_versions_dispatcher (void *decl) if (node_v->dispatcher_resolver != NULL) return node_v->dispatcher_resolver; - /* Find the default version and make it the first node. */ - first_v = node_v; - /* Go to the beginning of the chain. */ - while (first_v->prev != NULL) - first_v = first_v->prev; - default_version_info = first_v; - - while (default_version_info != NULL) - { - struct riscv_feature_bits res; - int priority; /* Unused. */ - parse_features_for_version (default_version_info->this_node->decl, - res, priority); - if (res.length == 0) - break; - default_version_info = default_version_info->next; - } + /* The default node is always the beginning of the chain. */ + default_version_info = node_v; + while (default_version_info->prev) + default_version_info = default_version_info->prev; + default_node = default_version_info->this_node; /* If there is no default node, just return NULL. */ - if (default_version_info == NULL) + struct riscv_feature_bits res; + int priority; /* Unused. */ + parse_features_for_version (default_node->decl, res, priority); + if (res.length != 0) return NULL; - /* Make default info the first node. */ - if (first_v != default_version_info) - { - default_version_info->prev->next = default_version_info->next; - if (default_version_info->next) - default_version_info->next->prev = default_version_info->prev; - first_v->prev = default_version_info; - default_version_info->next = first_v; - default_version_info->prev = NULL; - } - - default_node = default_version_info->this_node; - if (targetm.has_ifunc_p ()) { struct cgraph_function_version_info *it_v = NULL; diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc index 675b039c2b6..c59bd43c76d 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -25295,7 +25295,6 @@ rs6000_get_function_versions_dispatcher (void *decl) struct cgraph_node *node = NULL; struct cgraph_node *default_node = NULL; struct cgraph_function_version_info *node_v = NULL; - struct cgraph_function_version_info *first_v = NULL; tree dispatch_decl = NULL; @@ -25315,38 +25314,16 @@ rs6000_get_function_versions_dispatcher (void *decl) if (node_v->dispatcher_resolver != NULL) return node_v->dispatcher_resolver; - /* Find the default version and make it the first node. */ - first_v = node_v; - /* Go to the beginning of the chain. */ - while (first_v->prev != NULL) - first_v = first_v->prev; - - default_version_info = first_v; - while (default_version_info != NULL) - { - const tree decl2 = default_version_info->this_node->decl; - if (is_function_default_version (decl2)) - break; - default_version_info = default_version_info->next; - } + /* The default node is always the beginning of the chain. */ + default_version_info = node_v; + while (default_version_info->prev) + default_version_info = default_version_info->prev; + default_node = default_version_info->this_node; /* If there is no default node, just return NULL. */ - if (default_version_info == NULL) + if (!is_function_default_version (default_node->decl)) return NULL; - /* Make default info the first node. */ - if (first_v != default_version_info) - { - default_version_info->prev->next = default_version_info->next; - if (default_version_info->next) - default_version_info->next->prev = default_version_info->prev; - first_v->prev = default_version_info; - default_version_info->next = first_v; - default_version_info->prev = NULL; - } - - default_node = default_version_info->this_node; - #ifndef TARGET_LIBC_PROVIDES_HWCAP_IN_TCB error_at (DECL_SOURCE_LOCATION (default_node->decl), "%<target_clones%> attribute needs GLIBC (2.23 and newer) that "