On Mon, Jan 15, 2024 at 12:27 PM Andrew Carlotti
<[email protected]> wrote:
>
> This allows code to determine why a particular function is
> multiversioned. For now, this will primarily be used to preserve
> existing name mangling quirks when subsequent commits change all
> function multiversioning name mangling to use explicit target hooks.
> However, this can also be used in future to allow more of the
> multiversioning logic to be moved out of target hooks, and to allow
> targets to simultaneously enable multiversioning with both 'target' and
> 'target_version' attributes.
Why does module.cc need to stream the bits? target_clone runs long
after the FE finished. Instead I wonder why LTO doesn't stream the bits
(tree-streamer-{in,out}.cc)?
You have four states but only mention 'target' and 'target_version', what's the
states actually? Can you amend the function_version_source enum
comment accordingly?
This looks like stage1 material to me.
Thanks,
Richard.
> gcc/ChangeLog:
>
> * multiple_target.cc (expand_target_clones): Use new enum value.
> * tree-core.h (enum function_version_source): New enum.
> (struct tree_function_decl): Extend versioned_function to two
> bits.
>
> gcc/cp/ChangeLog:
>
> * decl.cc (maybe_mark_function_versioned): Use new enum value.
> (duplicate_decls): Preserve DECL_FUNCTION_VERSIONED enum value.
> * module.cc (trees_out::core_bools): Use two bits for
> function_decl.versioned_function.
> (trees_in::core_bools): Ditto.
>
>
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index
> b10a72a87bf0a1cabab52c1e4b657bc8a379b91e..527931cd90a0a779a508a096b2623351fd65a2e8
> 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1254,7 +1254,10 @@ maybe_mark_function_versioned (tree decl)
> {
> if (!DECL_FUNCTION_VERSIONED (decl))
> {
> - DECL_FUNCTION_VERSIONED (decl) = 1;
> + if (TARGET_HAS_FMV_TARGET_ATTRIBUTE)
> + DECL_FUNCTION_VERSIONED (decl) = FUNCTION_VERSION_TARGET;
> + else
> + DECL_FUNCTION_VERSIONED (decl) = FUNCTION_VERSION_TARGET_VERSION;
> /* If DECL_ASSEMBLER_NAME has already been set, re-mangle
> to include the version marker. */
> if (DECL_ASSEMBLER_NAME_SET_P (decl))
> @@ -3159,7 +3162,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool
> hiding, bool was_hidden)
> && DECL_FUNCTION_VERSIONED (olddecl))
> {
> /* Set the flag for newdecl so that it gets copied to olddecl. */
> - DECL_FUNCTION_VERSIONED (newdecl) = 1;
> + DECL_FUNCTION_VERSIONED (newdecl) = DECL_FUNCTION_VERSIONED (olddecl);
> /* newdecl will be purged after copying to olddecl and is no longer
> a version. */
> cgraph_node::delete_function_version_by_decl (newdecl);
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index
> aa75e2809d8fdca14443c6b911bf725f6d286d20..ba60d0753f91ef91d45fb5d62f26118be4e34840
> 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -5473,7 +5473,11 @@ trees_out::core_bools (tree t)
> WB (t->function_decl.looping_const_or_pure_flag);
>
> WB (t->function_decl.has_debug_args_flag);
> - WB (t->function_decl.versioned_function);
> +
> + /* versioned_function is a 2 bit enum. */
> + unsigned vf = t->function_decl.versioned_function;
> + WB ((vf >> 0) & 1);
> + WB ((vf >> 1) & 1);
>
> /* decl_type is a (misnamed) 2 bit discriminator. */
> unsigned kind = t->function_decl.decl_type;
> @@ -5618,7 +5622,12 @@ trees_in::core_bools (tree t)
> RB (t->function_decl.looping_const_or_pure_flag);
>
> RB (t->function_decl.has_debug_args_flag);
> - RB (t->function_decl.versioned_function);
> +
> + /* versioned_function is a 2 bit enum. */
> + unsigned vf = 0;
> + vf |= unsigned (b ()) << 0;
> + vf |= unsigned (b ()) << 1;
> + t->function_decl.versioned_function = function_version_source (vf);
>
> /* decl_type is a (misnamed) 2 bit discriminator. */
> unsigned kind = 0;
> diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc
> index
> 1fdd279da04a7acc5e8c50f528139f19cadcd5ff..56a1934fe820e91b2fa451dcf6989382c906b98c
> 100644
> --- a/gcc/multiple_target.cc
> +++ b/gcc/multiple_target.cc
> @@ -383,7 +383,7 @@ expand_target_clones (struct cgraph_node *node, bool
> definition)
> if (decl1_v == NULL)
> decl1_v = node->insert_new_function_version ();
> before = decl1_v;
> - DECL_FUNCTION_VERSIONED (node->decl) = 1;
> + DECL_FUNCTION_VERSIONED (node->decl) = FUNCTION_VERSION_TARGET_CLONES;
>
> for (i = 0; i < attrnum; i++)
> {
> @@ -421,7 +421,8 @@ expand_target_clones (struct cgraph_node *node, bool
> definition)
>
> before->next = after;
> after->prev = before;
> - DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
> + DECL_FUNCTION_VERSIONED (new_node->decl)
> + = FUNCTION_VERSION_TARGET_CLONES;
> }
>
> XDELETEVEC (attrs);
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index
> 8a89462bd7ecac52fcdc11c0b57ccf7c190572b3..e159d53f9d11ba848c49499aa963daa2fbcbc648
> 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1955,6 +1955,19 @@ enum function_decl_type
> /* 0 values left */
> };
>
> +/* Enumerate function multiversioning attributes. This is used to record
> which
> + attribute enabled multiversioning on a function, and allows targets to
> + adjust their behaviour accordingly. */
> +
> +enum function_version_source
> +{
> + FUNCTION_VERSION_NONE = 0,
> + FUNCTION_VERSION_TARGET = 1,
> + FUNCTION_VERSION_TARGET_CLONES = 2,
> + FUNCTION_VERSION_TARGET_VERSION = 3
> +};
> +
> +
> /* FUNCTION_DECL inherits from DECL_NON_COMMON because of the use of the
> arguments/result/saved_tree fields by front ends. It was either inherit
> FUNCTION_DECL from non_common, or inherit non_common from FUNCTION_DECL,
> @@ -2002,10 +2015,10 @@ struct GTY(()) tree_function_decl {
> /* Align the bitfield to boundary of a byte. */
> ENUM_BITFIELD(function_decl_type) decl_type: 2;
> unsigned has_debug_args_flag : 1;
> - unsigned versioned_function : 1;
> + ENUM_BITFIELD(function_version_source) versioned_function : 2;
> unsigned replaceable_operator : 1;
>
> - /* 11 bits left for future expansion. */
> + /* 10 bits left for future expansion. */
> /* 32 bits on 64-bit HW. */
> };
>