From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 411 invoked by alias); 8 Mar 2012 21:37:27 -0000 Received: (qmail 400 invoked by uid 22791); 8 Mar 2012 21:37:19 -0000 X-SWARE-Spam-Status: No, hits=-3.0 required=5.0 tests=AWL,BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_LOW,TW_CF,TW_CP,TW_FN,TW_TM,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mail-lpp01m010-f47.google.com (HELO mail-lpp01m010-f47.google.com) (209.85.215.47) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 08 Mar 2012 21:36:56 +0000 Received: by lagw12 with SMTP id w12so1080300lag.20 for ; Thu, 08 Mar 2012 13:36:54 -0800 (PST) Received: by 10.112.37.73 with SMTP id w9mr2799860lbj.22.1331242613965; Thu, 08 Mar 2012 13:36:53 -0800 (PST) MIME-Version: 1.0 Received: by 10.112.37.73 with SMTP id w9mr2799846lbj.22.1331242613661; Thu, 08 Mar 2012 13:36:53 -0800 (PST) Received: by 10.152.132.138 with HTTP; Thu, 8 Mar 2012 13:36:53 -0800 (PST) In-Reply-To: References: <20120307004630.A503DB21B6@azwildcat.mtv.corp.google.com> Date: Thu, 08 Mar 2012 21:37:00 -0000 Message-ID: Subject: Re: User directed Function Multiversioning via Function Overloading (issue5752064) From: Xinliang David Li To: Sriraman Tallam Cc: Richard Guenther , reply@codereview.appspotmail.com, gcc-patches@gcc.gnu.org Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable X-System-Of-Record: true X-Gm-Message-State: ALoCoQnMuhl5HM8Ir/quQN9QFUSzMBH0chaq69LIfbU14Khr7gcvezX1hG10lhIWnRhxhzHCaRFTO5bKUPdH9laQQtT/S7koY2WfTwYO1vsrWfGb0FrMFQHdANORgGjD6mV8PLKcouOB+YOaDrJALzCerbVqD6rcDg== X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2012-03/txt/msg00592.txt.bz2 On Wed, Mar 7, 2012 at 11:08 AM, Sriraman Tallam wrot= e: > On Wed, Mar 7, 2012 at 6:05 AM, Richard Guenther > wrote: >> On Wed, Mar 7, 2012 at 1:46 AM, Sriraman Tallam wr= ote: >>> User directed Function Multiversioning (MV) via Function Overloading >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> >>> This patch adds support for user directed function MV via function over= loading. >>> For more detailed description: >>> http://gcc.gnu.org/ml/gcc/2012-03/msg00074.html >>> >>> >>> Here is an example program with function versions: >>> >>> int foo (); =A0/* Default version */ >>> int foo () __attribute__ ((targetv("arch=3Dcorei7")));/*Specialized for= corei7 */ >>> int foo () __attribute__ ((targetv("arch=3Dcore2")));/*Specialized for = core2 */ >>> >>> int main () >>> { >>> =A0int (*p)() =3D &foo; >>> =A0return foo () + (*p)(); >>> } >>> >>> int foo () >>> { >>> =A0return 0; >>> } >>> >>> int __attribute__ ((targetv("arch=3Dcorei7"))) >>> foo () >>> { >>> =A0return 0; >>> } >>> >>> int __attribute__ ((targetv("arch=3Dcore2"))) >>> foo () >>> { >>> =A0return 0; >>> } >>> >>> The above example has foo defined 3 times, but all 3 definitions of foo= are >>> different versions of the same function. The call to foo in main, direc= tly and >>> via a pointer, are calls to the multi-versioned function foo which is d= ispatched >>> to the right foo at run-time. >>> >>> Function versions must have the same signature but must differ in the s= pecifier >>> string provided to a new attribute called "targetv", which is nothing b= ut the >>> target attribute with an extra specification to indicate a version. Any= number >>> of versions can be created using the targetv attribute but it is mandat= ory to >>> have one function without the attribute, which is treated as the default >>> version. >>> >>> The dispatching is done using the IFUNC mechanism to keep the dispatch = overhead >>> low. The compiler creates a dispatcher function which checks the CPU ty= pe and >>> calls the right version of foo. The dispatching code checks for the pla= tform >>> type and calls the first version that matches. The default function is = called if >>> no specialized version is appropriate for execution. >>> >>> The pointer to foo is made to be the address of the dispatcher function= , so that >>> it is unique and calls made via the pointer also work correctly. The as= sembler >>> names of the various versions of foo is made different, by tagging >>> the specifier strings, to keep them unique. =A0A specific version can b= e called >>> directly by creating an alias to its assembler name. For instance, to c= all the >>> corei7 version directly, make an alias : >>> int foo_corei7 () __attribute__((alias ("_Z3foov.arch_corei7"))); >>> and then call foo_corei7. >>> >>> Note that using IFUNC =A0blocks inlining of versioned functions. I had = implemented >>> an optimization earlier to do hot path cloning to allow versioned funct= ions to >>> be inlined. Please see : http://gcc.gnu.org/ml/gcc-patches/2011-04/msg0= 2285.html >>> In the next iteration, I plan to merge these two. With that, hot code p= aths with >>> versioned functions will be cloned so that versioned functions can be i= nlined. >> >> Note that inlining of functions with the target attribute is limited as = well, >> but your issue is that of the indirect dispatch as ... >> >> You don't give an overview of the frontend implementation. =A0Thus I have >> extracted the following >> >> =A0- the FE does not really know about the "overloading", nor can it dir= ectly >> =A0 resolve calls from a "sse" function to another "sse" function withou= t going >> =A0 through the 2nd IFUNC > > This is a good point but I can change function joust, where the > overload candidate is selected, to return the decl of the versioned > function with matching target attributes as that of the callee. That > will solve this problem. I have to treat the target attributes as an > additional criterion for a match in overload resolution. The front end > *does know* about the overloading, it is a question of doing the > overload resolution correctly right? =A0This is easy when there is no > cloning involved. Should this be covered by a new IFUNC folding rule? FE just needs to generate dummy code. > > When cloning of a version is required, it gets complicated since the > FE must clone and produce the bodies. Once, all the bodies are > available the overload resolution can do the right thing. > How can you safely clone a function without knowing if the versioned body is available in another module? David >> >> =A0- cgraph also does not know about the "overloading", so it cannot do = such >> =A0 "devirtualization" either >> >> you seem to have implemented something inbetween a pure frontend >> solution and a proper middle-end solution. > > The only thing I delayed is the code generation of the dispatcher. I > thought it is better to have this come later, after cfg and cgraph is > generated, so that multiple dispatching mechanisms could be > implemented. > > For optimization and eventually >> automatically selecting functions for cloning (like, callees of a manual= "sse" >> versioned function should be cloned?) it would be nice if the cgraph wou= ld >> know about the different versions and their relationships (and the dispa= tcher). >> Especially the cgraph code should know the functions are semantically >> equivalent (I suppose we should require that). =A0The IFUNC should be >> generated by cgraph / target code, similar to how we generate C++ thunks. >> >> Honza, any suggestions on how the FE side of such cgraph infrastructure >> should look like and how we should encode the target bits? >> >> Thanks, >> Richard. >> >>> =A0 =A0 =A0 =A0* doc/tm.texi.in: Add description for TARGET_DISPATCH_VE= RSION. >>> =A0 =A0 =A0 =A0* doc/tm.texi: Regenerate. >>> =A0 =A0 =A0 =A0* c-family/c-common.c (handle_targetv_attribute): New fu= nction. >>> =A0 =A0 =A0 =A0* target.def (dispatch_version): New target hook. >>> =A0 =A0 =A0 =A0* tree.h (DECL_FUNCTION_VERSIONED): New macro. >>> =A0 =A0 =A0 =A0(tree_function_decl): New bit-field versioned_function. >>> =A0 =A0 =A0 =A0* tree-pass.h (pass_dispatch_versions): New pass. >>> =A0 =A0 =A0 =A0* multiversion.c: New file. >>> =A0 =A0 =A0 =A0* multiversion.h: New file. >>> =A0 =A0 =A0 =A0* cgraphunit.c: Include multiversion.h >>> =A0 =A0 =A0 =A0(cgraph_finalize_function): Change assembler names of ve= rsioned >>> =A0 =A0 =A0 =A0functions. >>> =A0 =A0 =A0 =A0* cp/class.c: Include multiversion.h >>> =A0 =A0 =A0 =A0(add_method): aggregate function versions. Change assemb= ler names of >>> =A0 =A0 =A0 =A0versioned functions. >>> =A0 =A0 =A0 =A0(resolve_address_of_overloaded_function): Match address = of function >>> =A0 =A0 =A0 =A0version with default function. =A0Return address of ifun= c dispatcher >>> =A0 =A0 =A0 =A0for address of versioned functions. >>> =A0 =A0 =A0 =A0* cp/decl.c (decls_match): Make decls unmatched for vers= ioned >>> =A0 =A0 =A0 =A0functions. >>> =A0 =A0 =A0 =A0(duplicate_decls): Remove ambiguity for versioned functi= ons. Notify >>> =A0 =A0 =A0 =A0of deleted function version decls. >>> =A0 =A0 =A0 =A0(start_decl): Change assembler name of versioned functio= ns. >>> =A0 =A0 =A0 =A0(start_function): Change assembler name of versioned fun= ctions. >>> =A0 =A0 =A0 =A0(cxx_comdat_group): Make comdat group of versioned funct= ions be the >>> =A0 =A0 =A0 =A0same. >>> =A0 =A0 =A0 =A0* cp/semantics.c (expand_or_defer_fn_1): Mark as needed = versioned >>> =A0 =A0 =A0 =A0functions that are also marked inline. >>> =A0 =A0 =A0 =A0* cp/decl2.c: Include multiversion.h >>> =A0 =A0 =A0 =A0(check_classfn): Check attributes of versioned functions= for match. >>> =A0 =A0 =A0 =A0* cp/call.c: Include multiversion.h >>> =A0 =A0 =A0 =A0(build_over_call): Make calls to multiversioned function= s to call the >>> =A0 =A0 =A0 =A0dispatcher. >>> =A0 =A0 =A0 =A0(joust): For calls to multi-versioned functions, make th= e default >>> =A0 =A0 =A0 =A0function win. >>> =A0 =A0 =A0 =A0* timevar.def (TV_MULTIVERSION_DISPATCH): New time var. >>> =A0 =A0 =A0 =A0* varasm.c (finish_aliases_1): Check if the alias points= to a function >>> =A0 =A0 =A0 =A0with a body before giving an error. >>> =A0 =A0 =A0 =A0* Makefile.in: Add multiversion.o >>> =A0 =A0 =A0 =A0* passes.c: Add pass_dispatch_versions to the pass list. >>> =A0 =A0 =A0 =A0* config/i386/i386.c (add_condition_to_bb): New function. >>> =A0 =A0 =A0 =A0(get_builtin_code_for_version): New function. >>> =A0 =A0 =A0 =A0(ix86_dispatch_version): New function. >>> =A0 =A0 =A0 =A0(TARGET_DISPATCH_VERSION): New macro. >>> =A0 =A0 =A0 =A0* testsuite/g++.dg/mv1.C: New test. >>> >>> Index: doc/tm.texi >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- doc/tm.texi (revision 184971) >>> +++ doc/tm.texi (working copy) >>> @@ -10995,6 +10995,14 @@ The result is another tree containing a simpli= fied >>> =A0call's result. =A0If @var{ignore} is true the value will be ignored. >>> =A0@end deftypefn >>> >>> +@deftypefn {Target Hook} int TARGET_DISPATCH_VERSION (tree @var{dispat= ch_decl}, void *@var{fndecls}, basic_block *@var{empty_bb}) >>> +For multi-versioned function, this hook sets up the dispatcher. >>> +@var{dispatch_decl} is the function that will be used to dispatch the >>> +version. @var{fndecls} are the function choices for dispatch. >>> +@var{empty_bb} is an basic block in @var{dispatch_decl} where the >>> +code to do the dispatch will be added. >>> +@end deftypefn >>> + >>> =A0@deftypefn {Target Hook} {const char *} TARGET_INVALID_WITHIN_DOLOOP= (const_rtx @var{insn}) >>> >>> =A0Take an instruction in @var{insn} and return NULL if it is valid wit= hin a >>> Index: doc/tm.texi.in >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- doc/tm.texi.in =A0 =A0 =A0(revision 184971) >>> +++ doc/tm.texi.in =A0 =A0 =A0(working copy) >>> @@ -10873,6 +10873,14 @@ The result is another tree containing a simpli= fied >>> =A0call's result. =A0If @var{ignore} is true the value will be ignored. >>> =A0@end deftypefn >>> >>> +@hook TARGET_DISPATCH_VERSION >>> +For multi-versioned function, this hook sets up the dispatcher. >>> +@var{dispatch_decl} is the function that will be used to dispatch the >>> +version. @var{fndecls} are the function choices for dispatch. >>> +@var{empty_bb} is an basic block in @var{dispatch_decl} where the >>> +code to do the dispatch will be added. >>> +@end deftypefn >>> + >>> =A0@hook TARGET_INVALID_WITHIN_DOLOOP >>> >>> =A0Take an instruction in @var{insn} and return NULL if it is valid wit= hin a >>> Index: c-family/c-common.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- c-family/c-common.c (revision 184971) >>> +++ c-family/c-common.c (working copy) >>> @@ -315,6 +315,7 @@ static tree check_case_value (tree); >>> =A0static bool check_case_bounds (tree, tree, tree *, tree *); >>> >>> =A0static tree handle_packed_attribute (tree *, tree, tree, int, bool *= ); >>> +static tree handle_targetv_attribute (tree *, tree, tree, int, bool *); >>> =A0static tree handle_nocommon_attribute (tree *, tree, tree, int, bool= *); >>> =A0static tree handle_common_attribute (tree *, tree, tree, int, bool *= ); >>> =A0static tree handle_noreturn_attribute (tree *, tree, tree, int, bool= *); >>> @@ -604,6 +605,8 @@ const struct attribute_spec c_common_attribute_tab >>> =A0{ >>> =A0 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handl= er, >>> =A0 =A0 =A0 =A0affects_type_identity } */ >>> + =A0{ "targetv", =A0 =A0 =A0 =A0 =A0 =A0 =A0 1, -1, true, false, false, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_target= v_attribute, false }, >>> =A0 { "packed", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 0, false, false, fal= se, >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0handle_packe= d_attribute , false}, >>> =A0 { "nocommon", =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 0, true, =A0false, fal= se, >>> @@ -5869,6 +5872,54 @@ handle_packed_attribute (tree *node, tree name, = tr >>> =A0 return NULL_TREE; >>> =A0} >>> >>> +/* The targetv attribue is used to specify a function version >>> + =A0 targeted to specific platform types. =A0The "targetv" attributes >>> + =A0 have to be valid "target" attributes. =A0NODE should always point >>> + =A0 to a FUNCTION_DECL. =A0ARGS contain the arguments to "targetv" >>> + =A0 which should be valid arguments to attribute "target" too. >>> + =A0 Check handle_target_attribute for FLAGS and NO_ADD_ATTRS. =A0*/ >>> + >>> +static tree >>> +handle_targetv_attribute (tree *node, tree name, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tree args, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int flags, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bool *no_add_attrs) >>> +{ >>> + =A0const char *attr_str =3D NULL; >>> + =A0gcc_assert (TREE_CODE (*node) =3D=3D FUNCTION_DECL); >>> + =A0gcc_assert (args !=3D NULL); >>> + >>> + =A0/* This is a function version. =A0*/ >>> + =A0DECL_FUNCTION_VERSIONED (*node) =3D 1; >>> + >>> + =A0attr_str =3D TREE_STRING_POINTER (TREE_VALUE (args)); >>> + >>> + =A0/* Check if multiple sets of target attributes are there. =A0This >>> + =A0 =A0 is not supported now. =A0 In future, this will be supported by >>> + =A0 =A0 cloning this function for each set. =A0*/ >>> + =A0if (TREE_CHAIN (args) !=3D NULL) >>> + =A0 =A0warning (OPT_Wattributes, "%qE attribute has multiple sets whi= ch " >>> + =A0 =A0 =A0 =A0 =A0 =A0"is not supported", name); >>> + >>> + =A0if (attr_str =3D=3D NULL >>> + =A0 =A0 =A0|| strstr (attr_str, "arch=3D") =3D=3D NULL) >>> + =A0 =A0error_at (DECL_SOURCE_LOCATION (*node), >>> + =A0 =A0 =A0 =A0 =A0 =A0 "Versioning supported only on \"arch=3D\" for= now"); >>> + >>> + =A0/* targetv attributes must translate into target attributes. =A0*/ >>> + =A0handle_target_attribute (node, get_identifier ("target"), args, fl= ags, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0no_add_attrs); >>> + >>> + =A0if (*no_add_attrs) >>> + =A0 =A0warning (OPT_Wattributes, "%qE attribute has no effect", name); >>> + >>> + =A0/* This is necessary to keep the attribute tagged to the decl >>> + =A0 =A0 all the time. =A0*/ >>> + =A0*no_add_attrs =3D false; >>> + >>> + =A0return NULL_TREE; >>> +} >>> + >>> =A0/* Handle a "nocommon" attribute; arguments as in >>> =A0 =A0struct attribute_spec.handler. =A0*/ >>> >>> Index: target.def >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- target.def =A0(revision 184971) >>> +++ target.def =A0(working copy) >>> @@ -1249,6 +1249,15 @@ DEFHOOK >>> =A0tree, (tree fndecl, int n_args, tree *argp, bool ignore), >>> =A0hook_tree_tree_int_treep_bool_null) >>> >>> +/* Target hook to generate the dispatching code for calls to multi-ver= sioned >>> + =A0 functions. =A0DISPATCH_DECL is the function that will have the di= spatching >>> + =A0 logic. =A0FNDECLS are the list of choices for dispatch and EMPTY_= BB is the >>> + =A0 basic bloc in DISPATCH_DECL which will contain the code. =A0*/ >>> +DEFHOOK >>> +(dispatch_version, >>> + "", >>> + int, (tree dispatch_decl, void *fndecls, basic_block *empty_bb), NULL) >>> + >>> =A0/* Returns a code for a target-specific builtin that implements >>> =A0 =A0reciprocal of the function, or NULL_TREE if not available. =A0*/ >>> =A0DEFHOOK >>> Index: tree.h >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- tree.h =A0 =A0 =A0(revision 184971) >>> +++ tree.h =A0 =A0 =A0(working copy) >>> @@ -3532,6 +3532,12 @@ extern VEC(tree, gc) **decl_debug_args_insert (t= re >>> =A0#define DECL_FUNCTION_SPECIFIC_OPTIMIZATION(NODE) \ >>> =A0 =A0(FUNCTION_DECL_CHECK (NODE)->function_decl.function_specific_opt= imization) >>> >>> +/* In FUNCTION_DECL, this is set if this function has other versions g= enerated >>> + =A0 using "targetv" attributes. =A0The default version is the one whi= ch does not >>> + =A0 have any "targetv" attribute set. */ >>> +#define DECL_FUNCTION_VERSIONED(NODE)\ >>> + =A0 (FUNCTION_DECL_CHECK (NODE)->function_decl.versioned_function) >>> + >>> =A0/* FUNCTION_DECL inherits from DECL_NON_COMMON because of the use of= the >>> =A0 =A0arguments/result/saved_tree fields by front ends. =A0 It was eit= her inherit >>> =A0 =A0FUNCTION_DECL from non_common, or inherit non_common from FUNCTI= ON_DECL, >>> @@ -3576,8 +3582,8 @@ struct GTY(()) tree_function_decl { >>> =A0 unsigned looping_const_or_pure_flag : 1; >>> =A0 unsigned has_debug_args_flag : 1; >>> =A0 unsigned tm_clone_flag : 1; >>> - >>> - =A0/* 1 bit left */ >>> + =A0unsigned versioned_function : 1; >>> + =A0/* No bits left. =A0*/ >>> =A0}; >>> >>> =A0/* The source language of the translation-unit. =A0*/ >>> Index: tree-pass.h >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- tree-pass.h (revision 184971) >>> +++ tree-pass.h (working copy) >>> @@ -455,6 +455,7 @@ extern struct gimple_opt_pass pass_tm_memopt; >>> =A0extern struct gimple_opt_pass pass_tm_edges; >>> =A0extern struct gimple_opt_pass pass_split_functions; >>> =A0extern struct gimple_opt_pass pass_feedback_split_functions; >>> +extern struct gimple_opt_pass pass_dispatch_versions; >>> >>> =A0/* IPA Passes */ >>> =A0extern struct simple_ipa_opt_pass pass_ipa_lower_emutls; >>> Index: multiversion.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- multiversion.c =A0 =A0 =A0(revision 0) >>> +++ multiversion.c =A0 =A0 =A0(revision 0) >>> @@ -0,0 +1,798 @@ >>> +/* Function Multiversioning. >>> + =A0 Copyright (C) 2012 Free Software Foundation, Inc. >>> + =A0 Contributed by Sriraman Tallam (tmsriram@google.com) >>> + >>> +This file is part of GCC. >>> + >>> +GCC is free software; you can redistribute it and/or modify it under >>> +the terms of the GNU General Public License as published by the Free >>> +Software Foundation; either version 3, or (at your option) any later >>> +version. >>> + >>> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY >>> +WARRANTY; without even the implied warranty of MERCHANTABILITY or >>> +FITNESS FOR A PARTICULAR PURPOSE. =A0See the GNU General Public License >>> +for more details. >>> + >>> +You should have received a copy of the GNU General Public License >>> +along with GCC; see the file COPYING3. =A0If not see >>> +. */ >>> + >>> +/* Holds the state for multi-versioned functions here. The front-end >>> + =A0 updates the state as and when function versions are encountered. >>> + =A0 This is then used to generate the dispatch code. =A0Also, the >>> + =A0 optimization passes to clone hot paths involving versioned functi= ons >>> + =A0 will be done here. >>> + >>> + =A0 Function versions are created by using the same function signatur= e but >>> + =A0 also tagging attribute "targetv" to specify the platform type for= which >>> + =A0 the version must be executed. =A0Here is an example: >>> + >>> + =A0 int foo () >>> + =A0 { >>> + =A0 =A0 printf ("Execute as default"); >>> + =A0 =A0 return 0; >>> + =A0 } >>> + >>> + =A0 int =A0__attribute__ ((targetv ("arch=3Dcorei7"))) >>> + =A0 foo () >>> + =A0 { >>> + =A0 =A0 printf ("Execute for corei7"); >>> + =A0 =A0 return 0; >>> + =A0 } >>> + >>> + =A0 int main () >>> + =A0 { >>> + =A0 =A0 return foo (); >>> + =A0 } >>> + >>> + =A0 The call to foo in main is replaced with a call to an IFUNC funct= ion that >>> + =A0 contains the dispatch code to call the correct function version at >>> + =A0 run-time. =A0*/ >>> + >>> + >>> +#include "config.h" >>> +#include "system.h" >>> +#include "coretypes.h" >>> +#include "tm.h" >>> +#include "tree.h" >>> +#include "tree-inline.h" >>> +#include "langhooks.h" >>> +#include "flags.h" >>> +#include "cgraph.h" >>> +#include "diagnostic.h" >>> +#include "toplev.h" >>> +#include "timevar.h" >>> +#include "params.h" >>> +#include "fibheap.h" >>> +#include "intl.h" >>> +#include "tree-pass.h" >>> +#include "hashtab.h" >>> +#include "coverage.h" >>> +#include "ggc.h" >>> +#include "tree-flow.h" >>> +#include "rtl.h" >>> +#include "ipa-prop.h" >>> +#include "basic-block.h" >>> +#include "toplev.h" >>> +#include "dbgcnt.h" >>> +#include "tree-dump.h" >>> +#include "output.h" >>> +#include "vecprim.h" >>> +#include "gimple-pretty-print.h" >>> +#include "ipa-inline.h" >>> +#include "target.h" >>> +#include "multiversion.h" >>> + >>> +typedef void * void_p; >>> + >>> +DEF_VEC_P (void_p); >>> +DEF_VEC_ALLOC_P (void_p, heap); >>> + >>> +/* Each function decl that is a function version gets an instance of t= his >>> + =A0 structure. =A0 Since this is called by the front-end, decl mergin= g can >>> + =A0 happen, where a decl created for a new declaration is merged with >>> + =A0 the old. In this case, the new decl is deleted and the IS_DELETED >>> + =A0 field is set for the struct instance corresponding to the new dec= l. >>> + =A0 IFUNC_DECL is the decl of the ifunc function for default decls. >>> + =A0 IFUNC_RESOLVER_DECL is the decl of the dispatch function. =A0VERS= IONS >>> + =A0 is a vector containing the list of function versions =A0that are >>> + =A0 the candidates for dispatch. =A0*/ >>> + >>> +typedef struct version_function_d { >>> + =A0tree decl; >>> + =A0tree ifunc_decl; >>> + =A0tree ifunc_resolver_decl; >>> + =A0VEC (void_p, heap) *versions; >>> + =A0bool is_deleted; >>> +} version_function; >>> + >>> +/* Hashmap has an entry for every function decl that has other function >>> + =A0 versions. =A0For function decls that are the default, it also sto= res the >>> + =A0 list of all the other function versions. =A0Each entry is a struc= ture >>> + =A0 of type version_function_d. =A0*/ >>> +static htab_t decl_version_htab =3D NULL; >>> + >>> +/* Hashtable helpers for decl_version_htab. */ >>> + >>> +static hashval_t >>> +decl_version_htab_hash_descriptor (const void *p) >>> +{ >>> + =A0const version_function *t =3D (const version_function *) p; >>> + =A0return htab_hash_pointer (t->decl); >>> +} >>> + >>> +/* Hashtable helper for decl_version_htab. */ >>> + >>> +static int >>> +decl_version_htab_eq_descriptor (const void *p1, const void *p2) >>> +{ >>> + =A0const version_function *t1 =3D (const version_function *) p1; >>> + =A0return htab_eq_pointer ((const void_p) t1->decl, p2); >>> +} >>> + >>> +/* Create the decl_version_htab. =A0*/ >>> +static void >>> +create_decl_version_htab (void) >>> +{ >>> + =A0if (decl_version_htab =3D=3D NULL) >>> + =A0 =A0decl_version_htab =3D htab_create (10, decl_version_htab_hash_= descriptor, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0decl_version_htab_eq_descriptor, NULL); >>> +} >>> + >>> +/* Creates an instance of version_function for decl DECL. =A0*/ >>> + >>> +static version_function* >>> +new_version_function (const tree decl) >>> +{ >>> + =A0version_function *v; >>> + =A0v =3D (version_function *)xmalloc(sizeof (version_function)); >>> + =A0v->decl =3D decl; >>> + =A0v->ifunc_decl =3D NULL; >>> + =A0v->ifunc_resolver_decl =3D NULL; >>> + =A0v->versions =3D NULL; >>> + =A0v->is_deleted =3D false; >>> + =A0return v; >>> +} >>> + >>> +/* Comparator function to be used in qsort routine to sort attribute >>> + =A0 specification strings to "targetv". =A0*/ >>> + >>> +static int >>> +attr_strcmp (const void *v1, const void *v2) >>> +{ >>> + =A0const char *c1 =3D *(char *const*)v1; >>> + =A0const char *c2 =3D *(char *const*)v2; >>> + =A0return strcmp (c1, c2); >>> +} >>> + >>> +/* STR is the argument to targetv attribute. =A0This function tokenizes >>> + =A0 the comma separated arguments, sorts them and returns a string wh= ich >>> + =A0 is a unique identifier for the comma separated arguments. =A0*/ >>> + >>> +static char * >>> +sorted_attr_string (const char *str) >>> +{ >>> + =A0char **args =3D NULL; >>> + =A0char *attr_str, *ret_str; >>> + =A0char *attr =3D NULL; >>> + =A0unsigned int argnum =3D 1; >>> + =A0unsigned int i; >>> + >>> + =A0for (i =3D 0; i < strlen (str); i++) >>> + =A0 =A0if (str[i] =3D=3D ',') >>> + =A0 =A0 =A0argnum++; >>> + >>> + =A0attr_str =3D (char *)xmalloc (strlen (str) + 1); >>> + =A0strcpy (attr_str, str); >>> + >>> + =A0for (i =3D 0; i < strlen (attr_str); i++) >>> + =A0 =A0if (attr_str[i] =3D=3D '=3D') >>> + =A0 =A0 =A0attr_str[i] =3D '_'; >>> + >>> + =A0if (argnum =3D=3D 1) >>> + =A0 =A0return attr_str; >>> + >>> + =A0args =3D (char **)xmalloc (argnum * sizeof (char *)); >>> + >>> + =A0i =3D 0; >>> + =A0attr =3D strtok (attr_str, ","); >>> + =A0while (attr !=3D NULL) >>> + =A0 =A0{ >>> + =A0 =A0 =A0args[i] =3D attr; >>> + =A0 =A0 =A0i++; >>> + =A0 =A0 =A0attr =3D strtok (NULL, ","); >>> + =A0 =A0} >>> + >>> + =A0qsort (args, argnum, sizeof (char*), attr_strcmp); >>> + >>> + =A0ret_str =3D (char *)xmalloc (strlen (str) + 1); >>> + =A0strcpy (ret_str, args[0]); >>> + =A0for (i =3D 1; i < argnum; i++) >>> + =A0 =A0{ >>> + =A0 =A0 =A0strcat (ret_str, "_"); >>> + =A0 =A0 =A0strcat (ret_str, args[i]); >>> + =A0 =A0} >>> + >>> + =A0free (args); >>> + =A0free (attr_str); >>> + =A0return ret_str; >>> +} >>> + >>> +/* Returns true when only one of DECL1 and DECL2 is marked with "targe= tv" >>> + =A0 or if the "targetv" attribute strings of DECL1 and DECL2 dont mat= ch. =A0*/ >>> + >>> +bool >>> +has_different_version_attributes (const tree decl1, const tree decl2) >>> +{ >>> + =A0tree attr1, attr2; >>> + =A0char *c1, *c2; >>> + =A0bool ret =3D false; >>> + >>> + =A0if (TREE_CODE (decl1) !=3D FUNCTION_DECL >>> + =A0 =A0 =A0|| TREE_CODE (decl2) !=3D FUNCTION_DECL) >>> + =A0 =A0return false; >>> + >>> + =A0attr1 =3D lookup_attribute ("targetv", DECL_ATTRIBUTES (decl1)); >>> + =A0attr2 =3D lookup_attribute ("targetv", DECL_ATTRIBUTES (decl2)); >>> + >>> + =A0if (attr1 =3D=3D NULL_TREE && attr2 =3D=3D NULL_TREE) >>> + =A0 =A0return false; >>> + >>> + =A0if ((attr1 =3D=3D NULL_TREE && attr2 !=3D NULL_TREE) >>> + =A0 =A0 =A0|| (attr1 !=3D NULL_TREE && attr2 =3D=3D NULL_TREE)) >>> + =A0 =A0return true; >>> + >>> + =A0c1 =3D sorted_attr_string ( >>> + =A0 =A0 =A0 TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr1)))); >>> + =A0c2 =3D sorted_attr_string ( >>> + =A0 =A0 =A0 TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr2)))); >>> + >>> + =A0if (strcmp (c1, c2) !=3D 0) >>> + =A0 =A0 ret =3D true; >>> + >>> + =A0free (c1); >>> + =A0free (c2); >>> + >>> + =A0return ret; >>> +} >>> + >>> +/* If this decl corresponds to a function and has "targetv" attribute, >>> + =A0 append the attribute string to its assembler name. =A0*/ >>> + >>> +void >>> +version_assembler_name (const tree decl) >>> +{ >>> + =A0tree version_attr; >>> + =A0const char *orig_name, *version_string, *attr_str; >>> + =A0char *assembler_name; >>> + =A0tree assembler_name_tree; >>> + >>> + =A0if (TREE_CODE (decl) !=3D FUNCTION_DECL >>> + =A0 =A0 =A0|| DECL_ASSEMBLER_NAME_SET_P (decl) >>> + =A0 =A0 =A0|| !DECL_FUNCTION_VERSIONED (decl)) >>> + =A0 =A0return; >>> + >>> + =A0if (DECL_DECLARED_INLINE_P (decl) >>> + =A0 =A0 =A0&&lookup_attribute ("gnu_inline", >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 DECL_ATTRIBUTES (decl= ))) >>> + =A0 =A0error_at (DECL_SOURCE_LOCATION (decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 "Function versions cannot be marked as gnu_in= line," >>> + =A0 =A0 =A0 =A0 =A0 =A0 " bodies have to be generated\n"); >>> + >>> + =A0if (DECL_VIRTUAL_P (decl) >>> + =A0 =A0 =A0|| DECL_VINDEX (decl)) >>> + =A0 =A0error_at (DECL_SOURCE_LOCATION (decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 "Virtual function versioning not supported\n"= ); >>> + >>> + =A0version_attr =3D lookup_attribute ("targetv", DECL_ATTRIBUTES (dec= l)); >>> + =A0/* targetv attribute string is NULL for default functions. =A0*/ >>> + =A0if (version_attr =3D=3D NULL_TREE) >>> + =A0 =A0return; >>> + >>> + =A0orig_name =3D IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); >>> + =A0version_string >>> + =A0 =A0=3D TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr)= )); >>> + >>> + =A0attr_str =3D sorted_attr_string (version_string); >>> + =A0assembler_name =3D (char *) xmalloc (strlen (orig_name) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0+ strlen (attr_str) + 2); >>> + >>> + =A0sprintf (assembler_name, "%s.%s", orig_name, attr_str); >>> + =A0if (dump_file) >>> + =A0 =A0fprintf (dump_file, "Assembler name set to %s for function ver= sion %s\n", >>> + =A0 =A0 =A0 =A0 =A0 =A0assembler_name, IDENTIFIER_POINTER (DECL_NAME = (decl))); >>> + =A0assembler_name_tree =3D get_identifier (assembler_name); >>> + =A0SET_DECL_ASSEMBLER_NAME (decl, assembler_name_tree); >>> +} >>> + >>> +/* Returns true if decl is multi-versioned and DECL is the default fun= ction, >>> + =A0 that is it is not tagged with "targetv" attribute. =A0*/ >>> + >>> +bool >>> +is_default_function (const tree decl) >>> +{ >>> + =A0return (TREE_CODE (decl) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 =A0 && DECL_FUNCTION_VERSIONED (decl) >>> + =A0 =A0 =A0 =A0 && (lookup_attribute ("targetv", DECL_ATTRIBUTES (dec= l)) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =3D=3D NULL_TREE)); >>> +} >>> + >>> +/* For function decl DECL, find the version_function struct in the >>> + =A0 decl_version_htab. =A0*/ >>> + >>> +static version_function * >>> +find_function_version (const tree decl) >>> +{ >>> + =A0void *slot; >>> + >>> + =A0if (!DECL_FUNCTION_VERSIONED (decl)) >>> + =A0 =A0return NULL; >>> + >>> + =A0if (!decl_version_htab) >>> + =A0 =A0return NULL; >>> + >>> + =A0slot =3D htab_find_with_hash (decl_version_htab, decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0htab_hash_= pointer (decl)); >>> + >>> + =A0if (slot !=3D NULL) >>> + =A0 =A0return (version_function *)slot; >>> + >>> + =A0return NULL; >>> +} >>> + >>> +/* Record DECL as a function version by creating a version_function st= ruct >>> + =A0 for it and storing it in the hashtable. =A0*/ >>> + >>> +static version_function * >>> +add_function_version (const tree decl) >>> +{ >>> + =A0void **slot; >>> + =A0version_function *v; >>> + >>> + =A0if (!DECL_FUNCTION_VERSIONED (decl)) >>> + =A0 =A0return NULL; >>> + >>> + =A0create_decl_version_htab (); >>> + >>> + =A0slot =3D htab_find_slot_with_hash (decl_version_htab, (const void_= p)decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 h= tab_hash_pointer ((const void_p)decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0IN= SERT); >>> + >>> + =A0if (*slot !=3D NULL) >>> + =A0 =A0return (version_function *)*slot; >>> + >>> + =A0v =3D new_version_function (decl); >>> + =A0*slot =3D v; >>> + >>> + =A0return v; >>> +} >>> + >>> +/* Push V into VEC only if it is not already present. =A0*/ >>> + >>> +static void >>> +push_function_version (version_function *v, VEC (void_p, heap) *vec) >>> +{ >>> + =A0int ix; >>> + =A0void_p ele; >>> + =A0for (ix =3D 0; VEC_iterate (void_p, vec, ix, ele); ++ix) >>> + =A0 =A0{ >>> + =A0 =A0 =A0if (ele =3D=3D (void_p)v) >>> + =A0 =A0 =A0 =A0return; >>> + =A0 =A0} >>> + >>> + =A0VEC_safe_push (void_p, heap, vec, (void*)v); >>> +} >>> + >>> +/* Mark DECL as deleted. =A0This is called by the front-end when a dup= licate >>> + =A0 decl is merged with the original decl and the duplicate decl is d= eleted. >>> + =A0 This function marks the duplicate_decl as invalid. =A0Called by >>> + =A0 duplicate_decls in cp/decl.c. =A0*/ >>> + >>> +void >>> +mark_delete_decl_version (const tree decl) >>> +{ >>> + =A0version_function *decl_v; >>> + >>> + =A0decl_v =3D find_function_version (decl); >>> + >>> + =A0if (decl_v =3D=3D NULL) >>> + =A0 =A0return; >>> + >>> + =A0decl_v->is_deleted =3D true; >>> + >>> + =A0if (is_default_function (decl) >>> + =A0 =A0 =A0&& decl_v->versions !=3D NULL) >>> + =A0 =A0{ >>> + =A0 =A0 =A0VEC_truncate (void_p, decl_v->versions, 0); >>> + =A0 =A0 =A0VEC_free (void_p, heap, decl_v->versions); >>> + =A0 =A0} >>> +} >>> + >>> +/* Mark DECL1 and DECL2 to be function versions in the same group. =A0= One >>> + =A0 of DECL1 and DECL2 must be the default, otherwise this function d= oes >>> + =A0 nothing. =A0This function aggregates the versions. =A0*/ >>> + >>> +int >>> +group_function_versions (const tree decl1, const tree decl2) >>> +{ >>> + =A0tree default_decl, version_decl; >>> + =A0version_function *default_v, *version_v; >>> + >>> + =A0gcc_assert (DECL_FUNCTION_VERSIONED (decl1) >>> + =A0 =A0 =A0 =A0 =A0 =A0 && DECL_FUNCTION_VERSIONED (decl2)); >>> + >>> + =A0/* The version decls are added only to the default decl. =A0*/ >>> + =A0if (!is_default_function (decl1) >>> + =A0 =A0 =A0&& !is_default_function (decl2)) >>> + =A0 =A0return 0; >>> + >>> + =A0/* This can happen with duplicate declarations. =A0Just ignore. = =A0*/ >>> + =A0if (is_default_function (decl1) >>> + =A0 =A0 =A0&& is_default_function (decl2)) >>> + =A0 =A0return 0; >>> + >>> + =A0default_decl =3D (is_default_function (decl1)) ? decl1 : decl2; >>> + =A0version_decl =3D (default_decl =3D=3D decl1) ? decl2 : decl1; >>> + >>> + =A0gcc_assert (default_decl !=3D version_decl); >>> + =A0create_decl_version_htab (); >>> + >>> + =A0/* If the version function is found, it has been added. =A0*/ >>> + =A0if (find_function_version (version_decl)) >>> + =A0 =A0return 0; >>> + >>> + =A0default_v =3D add_function_version (default_decl); >>> + =A0version_v =3D add_function_version (version_decl); >>> + >>> + =A0if (default_v->versions =3D=3D NULL) >>> + =A0 =A0default_v->versions =3D VEC_alloc (void_p, heap, 1); >>> + >>> + =A0push_function_version (version_v, default_v->versions); >>> + =A0return 0; >>> +} >>> + >>> +/* Makes a function attribute of the form NAME(ARG_NAME) and chains >>> + =A0 it to CHAIN. =A0*/ >>> + >>> +static tree >>> +make_attribute (const char *name, const char *arg_name, tree chain) >>> +{ >>> + =A0tree attr_name; >>> + =A0tree attr_arg_name; >>> + =A0tree attr_args; >>> + =A0tree attr; >>> + >>> + =A0attr_name =3D get_identifier (name); >>> + =A0attr_arg_name =3D build_string (strlen (arg_name), arg_name); >>> + =A0attr_args =3D tree_cons (NULL_TREE, attr_arg_name, NULL_TREE); >>> + =A0attr =3D tree_cons (attr_name, attr_args, chain); >>> + =A0return attr; >>> +} >>> + >>> +/* Return a new name by appending SUFFIX to the DECL name. =A0If >>> + =A0 make_unique is true, append the full path name. =A0*/ >>> + >>> +static char * >>> +make_name (tree decl, const char *suffix, bool make_unique) >>> +{ >>> + =A0char *global_var_name; >>> + =A0int name_len; >>> + =A0const char *name; >>> + =A0const char *unique_name =3D NULL; >>> + >>> + =A0name =3D IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); >>> + >>> + =A0/* Get a unique name that can be used globally without any chances >>> + =A0 =A0 of collision at link time. =A0*/ >>> + =A0if (make_unique) >>> + =A0 =A0unique_name =3D IDENTIFIER_POINTER (get_file_function_name ("\= 0")); >>> + >>> + =A0name_len =3D strlen (name) + strlen (suffix) + 2; >>> + >>> + =A0if (make_unique) >>> + =A0 =A0name_len +=3D strlen (unique_name) + 1; >>> + =A0global_var_name =3D (char *) xmalloc (name_len); >>> + >>> + =A0/* Use '.' to concatenate names as it is demangler friendly. =A0*/ >>> + =A0if (make_unique) >>> + =A0 =A0 =A0snprintf (global_var_name, name_len, "%s.%s.%s", name, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 unique_name, suffix); >>> + =A0else >>> + =A0 =A0 =A0snprintf (global_var_name, name_len, "%s.%s", name, suffix= ); >>> + >>> + =A0return global_var_name; >>> +} >>> + >>> +/* Make the resolver function decl for ifunc (IFUNC_DECL) to dispatch >>> + =A0 the versions of multi-versioned function DEFAULT_DECL. =A0Create = and >>> + =A0 empty basic block in the resolver and store the pointer in >>> + =A0 EMPTY_BB. =A0Return the decl of the resolver function. =A0*/ >>> + >>> +static tree >>> +make_ifunc_resolver_func (const tree default_decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 const tree ifunc_decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 basic_block *empty_bb) >>> +{ >>> + =A0char *resolver_name; >>> + =A0tree decl, type, decl_name, t; >>> + =A0basic_block new_bb; >>> + =A0tree old_current_function_decl; >>> + =A0bool make_unique =3D false; >>> + >>> + =A0/* IFUNC's have to be globally visible. =A0So, if the default_decl= is >>> + =A0 =A0 not, then the name of the IFUNC should be made unique. =A0*/ >>> + =A0if (TREE_PUBLIC (default_decl) =3D=3D 0) >>> + =A0 =A0make_unique =3D true; >>> + >>> + =A0/* Append the filename to the resolver function if the versions are >>> + =A0 =A0 not externally visible. =A0This is because the resolver funct= ion has >>> + =A0 =A0 to be externally visible for the loader to find it. =A0So, ap= pending >>> + =A0 =A0 the filename will prevent conflicts with a resolver function = from >>> + =A0 =A0 another module which is based on the same version name. =A0*/ >>> + =A0resolver_name =3D make_name (default_decl, "resolver", make_unique= ); >>> + >>> + =A0/* The resolver function should return a (void *). */ >>> + =A0type =3D build_function_type_list (ptr_type_node, NULL_TREE); >>> + >>> + =A0decl =3D build_fn_decl (resolver_name, type); >>> + =A0decl_name =3D get_identifier (resolver_name); >>> + =A0SET_DECL_ASSEMBLER_NAME (decl, decl_name); >>> + >>> + =A0DECL_NAME (decl) =3D decl_name; >>> + =A0TREE_USED (decl) =3D TREE_USED (default_decl); >>> + =A0DECL_ARTIFICIAL (decl) =3D 1; >>> + =A0DECL_IGNORED_P (decl) =3D 0; >>> + =A0/* IFUNC resolvers have to be externally visible. =A0*/ >>> + =A0TREE_PUBLIC (decl) =3D 1; >>> + =A0DECL_UNINLINABLE (decl) =3D 1; >>> + >>> + =A0DECL_EXTERNAL (decl) =3D DECL_EXTERNAL (default_decl); >>> + =A0DECL_EXTERNAL (ifunc_decl) =3D 0; >>> + >>> + =A0DECL_CONTEXT (decl) =3D NULL_TREE; >>> + =A0DECL_INITIAL (decl) =3D make_node (BLOCK); >>> + =A0DECL_STATIC_CONSTRUCTOR (decl) =3D 0; >>> + =A0TREE_READONLY (decl) =3D 0; >>> + =A0DECL_PURE_P (decl) =3D 0; >>> + =A0DECL_COMDAT (decl) =3D DECL_COMDAT (default_decl); >>> + =A0if (DECL_COMDAT_GROUP (default_decl)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0make_decl_one_only (decl, DECL_COMDAT_GROUP (default_decl)= ); >>> + =A0 =A0} >>> + =A0/* Build result decl and add to function_decl. */ >>> + =A0t =3D build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, ptr_ty= pe_node); >>> + =A0DECL_ARTIFICIAL (t) =3D 1; >>> + =A0DECL_IGNORED_P (t) =3D 1; >>> + =A0DECL_RESULT (decl) =3D t; >>> + >>> + =A0gimplify_function_tree (decl); >>> + =A0old_current_function_decl =3D current_function_decl; >>> + =A0push_cfun (DECL_STRUCT_FUNCTION (decl)); >>> + =A0current_function_decl =3D decl; >>> + =A0init_empty_tree_cfg_for_function (DECL_STRUCT_FUNCTION (decl)); >>> + =A0cfun->curr_properties |=3D >>> + =A0 =A0(PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_reference= d_vars | >>> + =A0 =A0 PROP_ssa); >>> + =A0new_bb =3D create_empty_bb (ENTRY_BLOCK_PTR); >>> + =A0make_edge (ENTRY_BLOCK_PTR, new_bb, EDGE_FALLTHRU); >>> + =A0make_edge (new_bb, EXIT_BLOCK_PTR, 0); >>> + =A0*empty_bb =3D new_bb; >>> + >>> + =A0cgraph_add_new_function (decl, true); >>> + =A0cgraph_call_function_insertion_hooks (cgraph_get_create_node (decl= )); >>> + =A0cgraph_analyze_function (cgraph_get_create_node (decl)); >>> + =A0cgraph_mark_needed_node (cgraph_get_create_node (decl)); >>> + >>> + =A0if (DECL_COMDAT_GROUP (default_decl)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0gcc_assert (cgraph_get_node (default_decl)); >>> + =A0 =A0 =A0cgraph_add_to_same_comdat_group (cgraph_get_node (decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0cgraph_get_node (default_decl)); >>> + =A0 =A0} >>> + >>> + =A0pop_cfun (); >>> + =A0current_function_decl =3D old_current_function_decl; >>> + >>> + =A0gcc_assert (ifunc_decl !=3D NULL); >>> + =A0DECL_ATTRIBUTES (ifunc_decl) >>> + =A0 =A0=3D make_attribute ("ifunc", resolver_name, DECL_ATTRIBUTES (i= func_decl)); >>> + =A0assemble_alias (ifunc_decl, get_identifier (resolver_name)); >>> + =A0return decl; >>> +} >>> + >>> +/* Make and ifunc declaration for the multi-versioned function DECL. = =A0Calls to >>> + =A0 DECL function will be replaced with calls to the ifunc. =A0 Retur= n the decl >>> + =A0 of the ifunc created. =A0*/ >>> + >>> +static tree >>> +make_ifunc_func (const tree decl) >>> +{ >>> + =A0tree ifunc_decl; >>> + =A0char *ifunc_name, *resolver_name; >>> + =A0tree fn_type, ifunc_type; >>> + =A0bool make_unique =3D false; >>> + >>> + =A0if (TREE_PUBLIC (decl) =3D=3D 0) >>> + =A0 =A0make_unique =3D true; >>> + >>> + =A0ifunc_name =3D make_name (decl, "ifunc", make_unique); >>> + =A0resolver_name =3D make_name (decl, "resolver", make_unique); >>> + =A0gcc_assert (resolver_name); >>> + >>> + =A0fn_type =3D TREE_TYPE (decl); >>> + =A0ifunc_type =3D build_function_type (TREE_TYPE (fn_type), >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 T= YPE_ARG_TYPES (fn_type)); >>> + >>> + =A0ifunc_decl =3D build_fn_decl (ifunc_name, ifunc_type); >>> + =A0TREE_USED (ifunc_decl) =3D 1; >>> + =A0DECL_CONTEXT (ifunc_decl) =3D NULL_TREE; >>> + =A0DECL_INITIAL (ifunc_decl) =3D error_mark_node; >>> + =A0DECL_ARTIFICIAL (ifunc_decl) =3D 1; >>> + =A0/* Mark this ifunc as external, the resolver will flip it again if >>> + =A0 =A0 it gets generated. =A0*/ >>> + =A0DECL_EXTERNAL (ifunc_decl) =3D 1; >>> + =A0/* IFUNCs have to be externally visible. =A0*/ >>> + =A0TREE_PUBLIC (ifunc_decl) =3D 1; >>> + >>> + =A0return ifunc_decl; >>> +} >>> + >>> +/* For multi-versioned function decl, which should also be the default, >>> + =A0 return the decl of the ifunc resolver, create it if it does not >>> + =A0 exist. =A0*/ >>> + >>> +tree >>> +get_ifunc_for_version (const tree decl) >>> +{ >>> + =A0version_function *decl_v; >>> + =A0int ix; >>> + =A0void_p ele; >>> + >>> + =A0/* DECL has to be the default version, otherwise it is missing and >>> + =A0 =A0 that is not allowed. =A0*/ >>> + =A0if (!is_default_function (decl)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0error_at (DECL_SOURCE_LOCATION (decl), "Default version no= t found"); >>> + =A0 =A0 =A0return decl; >>> + =A0 =A0} >>> + >>> + =A0decl_v =3D find_function_version (decl); >>> + =A0gcc_assert (decl_v !=3D NULL); >>> + =A0if (decl_v->ifunc_decl =3D=3D NULL) >>> + =A0 =A0{ >>> + =A0 =A0 =A0tree ifunc_decl; >>> + =A0 =A0 =A0ifunc_decl =3D make_ifunc_func (decl); >>> + =A0 =A0 =A0decl_v->ifunc_decl =3D ifunc_decl; >>> + =A0 =A0} >>> + >>> + =A0if (cgraph_get_node (decl)) >>> + =A0 =A0cgraph_mark_needed_node (cgraph_get_node (decl)); >>> + >>> + =A0for (ix =3D 0; VEC_iterate (void_p, decl_v->versions, ix, ele); ++= ix) >>> + =A0 =A0{ >>> + =A0 =A0 =A0version_function *v =3D (version_function *) ele; >>> + =A0 =A0 =A0gcc_assert (v->decl !=3D NULL); >>> + =A0 =A0 =A0if (cgraph_get_node (v->decl)) >>> + =A0 =A0 =A0 cgraph_mark_needed_node (cgraph_get_node (v->decl)); >>> + =A0 =A0} >>> + >>> + =A0return decl_v->ifunc_decl; >>> +} >>> + >>> +/* Generate the dispatching code to dispatch multi-versioned function >>> + =A0 DECL. =A0Make a new function decl for dispatching and call the ta= rget >>> + =A0 hook to process the "targetv" attributes and provide the code to >>> + =A0 dispatch the right function at run-time. =A0*/ >>> + >>> +static tree >>> +make_ifunc_resolver_for_version (const tree decl) >>> +{ >>> + =A0version_function *decl_v; >>> + =A0tree ifunc_resolver_decl, ifunc_decl; >>> + =A0basic_block empty_bb; >>> + =A0int ix; >>> + =A0void_p ele; >>> + =A0VEC (tree, heap) *fn_ver_vec =3D NULL; >>> + >>> + =A0gcc_assert (is_default_function (decl)); >>> + >>> + =A0decl_v =3D find_function_version (decl); >>> + =A0gcc_assert (decl_v !=3D NULL); >>> + >>> + =A0if (decl_v->ifunc_resolver_decl !=3D NULL) >>> + =A0 =A0return decl_v->ifunc_resolver_decl; >>> + >>> + =A0ifunc_decl =3D decl_v->ifunc_decl; >>> + >>> + =A0if (ifunc_decl =3D=3D NULL) >>> + =A0 =A0ifunc_decl =3D decl_v->ifunc_decl =3D make_ifunc_func (decl); >>> + >>> + =A0ifunc_resolver_decl =3D make_ifunc_resolver_func (decl, ifunc_decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 &empty_bb); >>> + >>> + =A0fn_ver_vec =3D VEC_alloc (tree, heap, 2); >>> + =A0VEC_safe_push (tree, heap, fn_ver_vec, decl); >>> + >>> + =A0for (ix =3D 0; VEC_iterate (void_p, decl_v->versions, ix, ele); ++= ix) >>> + =A0 =A0{ >>> + =A0 =A0 =A0version_function *v =3D (version_function *) ele; >>> + =A0 =A0 =A0gcc_assert (v->decl !=3D NULL); >>> + =A0 =A0 =A0/* Check for virtual functions here again, as by this time= it should >>> + =A0 =A0 =A0 =A0have been determined if this function needs a vtable i= ndex or >>> + =A0 =A0 =A0 =A0not. =A0This happens for methods in derived classes th= at override >>> + =A0 =A0 =A0 =A0virtual methods in base classes but are not explicitly= marked as >>> + =A0 =A0 =A0 =A0virtual. =A0*/ >>> + =A0 =A0 =A0if (DECL_VINDEX (v->decl)) >>> + =A0 =A0 =A0 =A0error_at (DECL_SOURCE_LOCATION (v->decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Virtual function versioning not supp= orted\n"); >>> + =A0 =A0 =A0if (!v->is_deleted) >>> + =A0 =A0 =A0 VEC_safe_push (tree, heap, fn_ver_vec, v->decl); >>> + =A0 =A0} >>> + >>> + =A0gcc_assert (targetm.dispatch_version); >>> + =A0targetm.dispatch_version (ifunc_resolver_decl, fn_ver_vec, &empty_= bb); >>> + =A0decl_v->ifunc_resolver_decl =3D ifunc_resolver_decl; >>> + >>> + =A0return ifunc_resolver_decl; >>> +} >>> + >>> +/* Main entry point to pass_dispatch_versions. For multi-versioned fun= ctions, >>> + =A0 generate the dispatching code. =A0*/ >>> + >>> +static unsigned int >>> +do_dispatch_versions (void) >>> +{ >>> + =A0/* A new pass for generating dispatch code for multi-versioned fun= ctions. >>> + =A0 =A0 Other forms of dispatch can be added when ifunc support is no= t available >>> + =A0 =A0 like just calling the function directly after checking for ta= rget type. >>> + =A0 =A0 Currently, dispatching is done through IFUNC. =A0This pass wi= ll become >>> + =A0 =A0 more meaningful when other dispatch mechanisms are added. =A0= */ >>> + >>> + =A0/* Cloning a function to produce more versions will happen here wh= en the >>> + =A0 =A0 user requests that via the targetv attribute. For example, >>> + =A0 =A0 int foo () __attribute__ ((targetv(("arch=3Dcore2"), ("arch= =3Dcorei7")))); >>> + =A0 =A0 means that the user wants the same body of foo to be versione= d for core2 >>> + =A0 =A0 and corei7. =A0In that case, this function will be cloned dur= ing this >>> + =A0 =A0 pass. =A0*/ >>> + >>> + =A0if (DECL_FUNCTION_VERSIONED (current_function_decl) >>> + =A0 =A0 =A0&& is_default_function (current_function_decl)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0tree decl =3D make_ifunc_resolver_for_version (current_fun= ction_decl); >>> + =A0 =A0 =A0if (dump_file && decl) >>> + =A0 =A0 =A0 dump_function_to_file (decl, dump_file, TDF_BLOCKS); >>> + =A0 =A0} >>> + =A0return 0; >>> +} >>> + >>> +static =A0bool >>> +gate_dispatch_versions (void) >>> +{ >>> + =A0return true; >>> +} >>> + >>> +/* A pass to generate the dispatch code to execute the appropriate ver= sion >>> + =A0 of a multi-versioned function at run-time. =A0*/ >>> + >>> +struct gimple_opt_pass pass_dispatch_versions =3D >>> +{ >>> + { >>> + =A0GIMPLE_PASS, >>> + =A0"dispatch_multiversion_functions", =A0 =A0/* name */ >>> + =A0gate_dispatch_versions, =A0 =A0 =A0 =A0 =A0 =A0 =A0/* gate */ >>> + =A0do_dispatch_versions, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0/* execute */ >>> + =A0NULL, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0/* sub */ >>> + =A0NULL, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0/* next */ >>> + =A00, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 /* static_pass_number */ >>> + =A0TV_MULTIVERSION_DISPATCH, =A0 =A0 =A0 =A0 =A0 =A0/* tv_id */ >>> + =A0PROP_cfg, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/= * properties_required */ >>> + =A0PROP_cfg, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/= * properties_provided */ >>> + =A00, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 /* properties_destroyed */ >>> + =A00, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 /* todo_flags_start */ >>> + =A0TODO_dump_func | =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* todo_f= lags_finish */ >>> + =A0TODO_cleanup_cfg | TODO_dump_cgraph >>> + } >>> +}; >>> Index: cgraphunit.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cgraphunit.c =A0 =A0 =A0 =A0(revision 184971) >>> +++ cgraphunit.c =A0 =A0 =A0 =A0(working copy) >>> @@ -141,6 +141,7 @@ along with GCC; see the file COPYING3. =A0If not see >>> =A0#include "ipa-inline.h" >>> =A0#include "ipa-utils.h" >>> =A0#include "lto-streamer.h" >>> +#include "multiversion.h" >>> >>> =A0static void cgraph_expand_all_functions (void); >>> =A0static void cgraph_mark_functions_to_output (void); >>> @@ -343,6 +344,13 @@ cgraph_finalize_function (tree decl, bool nested) >>> =A0 =A0 =A0 node->local.redefined_extern_inline =3D true; >>> =A0 =A0 } >>> >>> + =A0/* If this is a function version and not the default, change the >>> + =A0 =A0 assembler name of this function. =A0The DECL names of function >>> + =A0 =A0 versions are the same, only the assembler names are made uniq= ue. >>> + =A0 =A0 The assembler name is changed by appending the string from >>> + =A0 =A0 the "targetv" attribute. =A0*/ >>> + =A0version_assembler_name (decl); >>> + >>> =A0 notice_global_symbol (decl); >>> =A0 node->local.finalized =3D true; >>> =A0 node->lowered =3D DECL_STRUCT_FUNCTION (decl)->cfg !=3D NULL; >>> Index: multiversion.h >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- multiversion.h =A0 =A0 =A0(revision 0) >>> +++ multiversion.h =A0 =A0 =A0(revision 0) >>> @@ -0,0 +1,52 @@ >>> +/* Function Multiversioning. >>> + =A0 Copyright (C) 2012 Free Software Foundation, Inc. >>> + =A0 Contributed by Sriraman Tallam (tmsriram@google.com) >>> + >>> +This file is part of GCC. >>> + >>> +GCC is free software; you can redistribute it and/or modify it under >>> +the terms of the GNU General Public License as published by the Free >>> +Software Foundation; either version 3, or (at your option) any later >>> +version. >>> + >>> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY >>> +WARRANTY; without even the implied warranty of MERCHANTABILITY or >>> +FITNESS FOR A PARTICULAR PURPOSE. =A0See the GNU General Public License >>> +for more details. >>> + >>> +You should have received a copy of the GNU General Public License >>> +along with GCC; see the file COPYING3. =A0If not see >>> +. */ >>> + >>> +/* This is the header file which provides the functions to keep track >>> + =A0 of functions that are multi-versioned and to generate the dispatch >>> + =A0 code to call the right version at run-time. =A0*/ >>> + >>> +#ifndef GCC_MULTIVERSION_H >>> +#define GCC_MULTIVERION_H >>> + >>> +#include "tree.h" >>> + >>> +/* Mark DECL1 and DECL2 as function versions. =A0*/ >>> +int group_function_versions (const tree decl1, const tree decl2); >>> + >>> +/* Mark DECL as deleted and no longer a version. =A0*/ >>> +void mark_delete_decl_version (const tree decl); >>> + >>> +/* Returns true if DECL is the default version to be executed if all >>> + =A0 other versions are inappropriate at run-time. =A0*/ >>> +bool is_default_function (const tree decl); >>> + >>> +/* Gets the IFUNC dispatcher for this multi-versioned function DECL. D= ECL >>> + =A0 must be the default function in the multi-versioned group. =A0*/ >>> +tree get_ifunc_for_version (const tree decl); >>> + >>> +/* Returns true when only one of DECL1 and DECL2 is marked with "targe= tv" >>> + =A0 or if the "targetv" attribute strings of =A0DECL1 and DECL2 dont = match. =A0*/ >>> +bool has_different_version_attributes (const tree decl1, const tree de= cl2); >>> + >>> +/* If DECL is a function version and not the default version, the asse= mbler >>> + =A0 name of DECL is changed to include the attribute string to keep t= he >>> + =A0 name unambiguous. =A0*/ >>> +void version_assembler_name (const tree decl); >>> +#endif >>> Index: cp/class.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cp/class.c =A0(revision 184971) >>> +++ cp/class.c =A0(working copy) >>> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. =A0If not see >>> =A0#include "tree-dump.h" >>> =A0#include "splay-tree.h" >>> =A0#include "pointer-set.h" >>> +#include "multiversion.h" >>> >>> =A0/* The number of nested classes being processed. =A0If we are not in= the >>> =A0 =A0scope of any class, this is zero. =A0*/ >>> @@ -1092,7 +1093,20 @@ add_method (tree type, tree method, tree using_d= ec >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0|| same_type_p (TREE_TYPE (fn_type), >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0TREE_TYPE (m= ethod_type)))) >>> =A0 =A0 =A0 =A0{ >>> - =A0 =A0 =A0 =A0 if (using_decl) >>> + =A0 =A0 =A0 =A0 /* For function versions, their parms and types match >>> + =A0 =A0 =A0 =A0 =A0 =A0but they are not duplicates. =A0Record functio= n versions >>> + =A0 =A0 =A0 =A0 =A0 =A0as and when they are found. =A0*/ >>> + =A0 =A0 =A0 =A0 if (TREE_CODE (fn) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 =A0 =A0 =A0 && TREE_CODE (method) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 =A0 =A0 =A0 && (DECL_FUNCTION_VERSIONED (fn) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 || DECL_FUNCTION_VERSIONED (method))) >>> + =A0 =A0 =A0 =A0 =A0 { >>> + =A0 =A0 =A0 =A0 =A0 =A0 DECL_FUNCTION_VERSIONED (fn) =3D 1; >>> + =A0 =A0 =A0 =A0 =A0 =A0 DECL_FUNCTION_VERSIONED (method) =3D 1; >>> + =A0 =A0 =A0 =A0 =A0 =A0 group_function_versions (fn, method); >>> + =A0 =A0 =A0 =A0 =A0 =A0 continue; >>> + =A0 =A0 =A0 =A0 =A0 } >>> + =A0 =A0 =A0 =A0 else if (using_decl) >>> =A0 =A0 =A0 =A0 =A0 =A0{ >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0if (DECL_CONTEXT (fn) =3D=3D type) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Defer to the local function. =A0*/ >>> @@ -1150,6 +1164,13 @@ add_method (tree type, tree method, tree using_d= ec >>> =A0 else >>> =A0 =A0 /* Replace the current slot. =A0*/ >>> =A0 =A0 VEC_replace (tree, method_vec, slot, overload); >>> + >>> + =A0/* Change the assembler name of method here if it has "targetv" >>> + =A0 =A0 attributes. =A0Since all versions have the same mangled name, >>> + =A0 =A0 their assembler name is changed by appending the string from >>> + =A0 =A0 the "targetv" attribute. */ >>> + =A0version_assembler_name (method); >>> + >>> =A0 return true; >>> =A0} >>> >>> @@ -6890,8 +6911,11 @@ resolve_address_of_overloaded_function (tree tar= ge >>> =A0 =A0 =A0 =A0 =A0if (DECL_ANTICIPATED (fn)) >>> =A0 =A0 =A0 =A0 =A0 =A0continue; >>> >>> - =A0 =A0 =A0 =A0 /* See if there's a match. =A0*/ >>> - =A0 =A0 =A0 =A0 if (same_type_p (target_fn_type, static_fn_type (fn))) >>> + =A0 =A0 =A0 =A0 /* See if there's a match. =A0 For functions that are= multi-versioned >>> + =A0 =A0 =A0 =A0 =A0 =A0match it to the default function. =A0*/ >>> + =A0 =A0 =A0 =A0 if (same_type_p (target_fn_type, static_fn_type (fn)) >>> + =A0 =A0 =A0 =A0 =A0 =A0 && (!DECL_FUNCTION_VERSIONED (fn) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 || is_default_function (fn))) >>> =A0 =A0 =A0 =A0 =A0 =A0matches =3D tree_cons (fn, NULL_TREE, matches); >>> =A0 =A0 =A0 =A0} >>> =A0 =A0 } >>> @@ -7053,6 +7077,21 @@ resolve_address_of_overloaded_function (tree tar= ge >>> =A0 =A0 =A0 perform_or_defer_access_check (access_path, fn, fn); >>> =A0 =A0 } >>> >>> + =A0/* If a pointer to a function that is multi-versioned is requested= , the >>> + =A0 =A0 pointer to the dispatcher function is returned instead. =A0Th= is works >>> + =A0 =A0 well because indirectly calling the function will dispatch th= e right >>> + =A0 =A0 function version at run-time. Also, the function address is k= ept >>> + =A0 =A0 unique. =A0*/ >>> + =A0if (DECL_FUNCTION_VERSIONED (fn) >>> + =A0 =A0 =A0&& is_default_function (fn)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0tree ifunc_decl; >>> + =A0 =A0 =A0ifunc_decl =3D get_ifunc_for_version (fn); >>> + =A0 =A0 =A0gcc_assert (ifunc_decl !=3D NULL); >>> + =A0 =A0 =A0mark_used (fn); >>> + =A0 =A0 =A0return build_fold_addr_expr (ifunc_decl); >>> + =A0 =A0} >>> + >>> =A0 if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type)) >>> =A0 =A0 return cp_build_addr_expr (fn, flags); >>> =A0 else >>> Index: cp/decl.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cp/decl.c =A0 (revision 184971) >>> +++ cp/decl.c =A0 (working copy) >>> @@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. =A0If not see >>> =A0#include "pointer-set.h" >>> =A0#include "splay-tree.h" >>> =A0#include "plugin.h" >>> +#include "multiversion.h" >>> >>> =A0/* Possible cases of bad specifiers type used by bad_specifiers. */ >>> =A0enum bad_spec_place { >>> @@ -972,6 +973,23 @@ decls_match (tree newdecl, tree olddecl) >>> =A0 =A0 =A0 if (t1 !=3D t2) >>> =A0 =A0 =A0 =A0return 0; >>> >>> + =A0 =A0 =A0/* The decls dont match if they correspond to two differen= t versions >>> + =A0 =A0 =A0 =A0of the same function. =A0*/ >>> + =A0 =A0 =A0if (compparms (p1, p2) >>> + =A0 =A0 =A0 =A0 && same_type_p (TREE_TYPE (f1), TREE_TYPE (f2)) >>> + =A0 =A0 =A0 =A0 && (DECL_FUNCTION_VERSIONED (newdecl) >>> + =A0 =A0 =A0 =A0 =A0 =A0 || DECL_FUNCTION_VERSIONED (olddecl)) >>> + =A0 =A0 =A0 =A0 && has_different_version_attributes (newdecl, olddecl= )) >>> + =A0 =A0 =A0 { >>> + =A0 =A0 =A0 =A0 /* One of the decls could be the default without the = "targetv" >>> + =A0 =A0 =A0 =A0 =A0 =A0attribute. Set it to be a versioned function h= ere. =A0*/ >>> + =A0 =A0 =A0 =A0 DECL_FUNCTION_VERSIONED (newdecl) =3D 1; >>> + =A0 =A0 =A0 =A0 DECL_FUNCTION_VERSIONED (olddecl) =3D 1; >>> + =A0 =A0 =A0 =A0 /* Accumulate all the versions of a function. =A0*/ >>> + =A0 =A0 =A0 =A0 group_function_versions (olddecl, newdecl); >>> + =A0 =A0 =A0 =A0 return 0; >>> + =A0 =A0 =A0 } >>> + >>> =A0 =A0 =A0 if (CP_DECL_CONTEXT (newdecl) !=3D CP_DECL_CONTEXT (olddecl) >>> =A0 =A0 =A0 =A0 =A0&& ! (DECL_EXTERN_C_P (newdecl) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&& DECL_EXTERN_C_P (olddecl))) >>> @@ -1482,7 +1500,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0error ("previous declaration %q+#D here", ol= ddecl); >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0return NULL_TREE; >>> =A0 =A0 =A0 =A0 =A0 =A0} >>> - =A0 =A0 =A0 =A0 else if (compparms (TYPE_ARG_TYPES (TREE_TYPE (newdec= l)), >>> + =A0 =A0 =A0 =A0 /* For function versions, params and types match, but= they >>> + =A0 =A0 =A0 =A0 =A0 =A0are not ambiguous. =A0*/ >>> + =A0 =A0 =A0 =A0 else if ((!DECL_FUNCTION_VERSIONED (newdecl) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 && !DECL_FUNCTION_VERSIONED (oldd= ecl)) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&& compparms (TYPE_ARG_TYPES (TREE= _TYPE (newdecl)), >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0TYPE_ARG_TYP= ES (TREE_TYPE (olddecl)))) >>> =A0 =A0 =A0 =A0 =A0 =A0{ >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0error ("new declaration %q#D", newdecl); >>> @@ -2250,6 +2272,16 @@ duplicate_decls (tree newdecl, tree olddecl, bool >>> =A0 else if (DECL_PRESERVE_P (newdecl)) >>> =A0 =A0 DECL_PRESERVE_P (olddecl) =3D 1; >>> >>> + =A0/* If the olddecl is a version, so is the newdecl. =A0*/ >>> + =A0if (TREE_CODE (newdecl) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0&& DECL_FUNCTION_VERSIONED (olddecl)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0DECL_FUNCTION_VERSIONED (newdecl) =3D 1; >>> + =A0 =A0 =A0/* Record that newdecl is not a valid version and has >>> + =A0 =A0 =A0 =A0been deleted. =A0*/ >>> + =A0 =A0 =A0mark_delete_decl_version (newdecl); >>> + =A0 =A0} >>> + >>> =A0 if (TREE_CODE (newdecl) =3D=3D FUNCTION_DECL) >>> =A0 =A0 { >>> =A0 =A0 =A0 int function_size; >>> @@ -4512,6 +4544,10 @@ start_decl (const cp_declarator *declarator, >>> =A0 /* Enter this declaration into the symbol table. =A0*/ >>> =A0 decl =3D maybe_push_decl (decl); >>> >>> + =A0/* If this decl is a function version and not the default, its ass= embler >>> + =A0 =A0 name has to be changed. =A0*/ >>> + =A0version_assembler_name (decl); >>> + >>> =A0 if (processing_template_decl) >>> =A0 =A0 decl =3D push_template_decl (decl); >>> =A0 if (decl =3D=3D error_mark_node) >>> @@ -13019,6 +13055,10 @@ start_function (cp_decl_specifier_seq *declspe= cs, >>> =A0 =A0 gcc_assert (same_type_p (TREE_TYPE (TREE_TYPE (decl1)), >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 integer_type_no= de)); >>> >>> + =A0/* If this decl is a function version and not the default, its ass= embler >>> + =A0 =A0 name has to be changed. =A0*/ >>> + =A0version_assembler_name (decl1); >>> + >>> =A0 start_preparsed_function (decl1, attrs, /*flags=3D*/SF_DEFAULT); >>> >>> =A0 return 1; >>> @@ -13960,6 +14000,11 @@ cxx_comdat_group (tree decl) >>> =A0 =A0 =A0 =A0 =A0 =A0break; >>> =A0 =A0 =A0 =A0} >>> =A0 =A0 =A0 name =3D DECL_ASSEMBLER_NAME (decl); >>> + =A0 =A0 =A0if (TREE_CODE (decl) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 =A0 && DECL_FUNCTION_VERSIONED (decl)) >>> + =A0 =A0 =A0 name =3D DECL_NAME (decl); >>> + =A0 =A0 =A0else >>> + =A0 =A0 =A0 =A0name =3D DECL_ASSEMBLER_NAME (decl); >>> =A0 =A0 } >>> >>> =A0 return name; >>> Index: cp/semantics.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cp/semantics.c =A0 =A0 =A0(revision 184971) >>> +++ cp/semantics.c =A0 =A0 =A0(working copy) >>> @@ -3783,8 +3783,11 @@ expand_or_defer_fn_1 (tree fn) >>> =A0 =A0 =A0 /* If the user wants us to keep all inline functions, then = mark >>> =A0 =A0 =A0 =A0 this function as needed so that finish_file will make s= ure to >>> =A0 =A0 =A0 =A0 output it later. =A0Similarly, all dllexport'd function= s must >>> - =A0 =A0 =A0 =A0be emitted; there may be callers in other DLLs. =A0*/ >>> - =A0 =A0 =A0if ((flag_keep_inline_functions >>> + =A0 =A0 =A0 =A0be emitted; there may be callers in other DLLs. >>> + =A0 =A0 =A0 =A0Also, mark this function as needed if it is marked inl= ine but >>> + =A0 =A0 =A0 =A0is a multi-versioned function. =A0*/ >>> + =A0 =A0 =A0if (((flag_keep_inline_functions >>> + =A0 =A0 =A0 =A0 =A0 || DECL_FUNCTION_VERSIONED (fn)) >>> =A0 =A0 =A0 =A0 =A0 && DECL_DECLARED_INLINE_P (fn) >>> =A0 =A0 =A0 =A0 =A0 && !DECL_REALLY_EXTERN (fn)) >>> =A0 =A0 =A0 =A0 =A0|| (flag_keep_inline_dllexport >>> Index: cp/decl2.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cp/decl2.c =A0(revision 184971) >>> +++ cp/decl2.c =A0(working copy) >>> @@ -53,6 +53,7 @@ along with GCC; see the file COPYING3. =A0If not see >>> =A0#include "splay-tree.h" >>> =A0#include "langhooks.h" >>> =A0#include "c-family/c-ada-spec.h" >>> +#include "multiversion.h" >>> >>> =A0extern cpp_reader *parse_in; >>> >>> @@ -674,9 +675,13 @@ check_classfn (tree ctype, tree function, tree tem >>> =A0 =A0 =A0 =A0 =A0if (is_template !=3D (TREE_CODE (fndecl) =3D=3D TEMP= LATE_DECL)) >>> =A0 =A0 =A0 =A0 =A0 =A0continue; >>> >>> + =A0 =A0 =A0 =A0 /* While finding a match, same types and params are n= ot enough >>> + =A0 =A0 =A0 =A0 =A0 =A0if the function is versioned. =A0Also check ve= rsion ("targetv") >>> + =A0 =A0 =A0 =A0 =A0 =A0attributes. =A0*/ >>> =A0 =A0 =A0 =A0 =A0if (same_type_p (TREE_TYPE (TREE_TYPE (function)), >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 TREE_TYPE (TREE_TYP= E (fndecl))) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0&& compparms (p1, p2) >>> + =A0 =A0 =A0 =A0 =A0 =A0 && !has_different_version_attributes (functio= n, fndecl) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0&& (!is_template >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|| comp_template_parms (template_par= ms, >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0DECL_TEMPLATE_PARMS (fndecl))) >>> Index: cp/call.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- cp/call.c =A0 (revision 184971) >>> +++ cp/call.c =A0 (working copy) >>> @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. =A0If not see >>> =A0#include "langhooks.h" >>> =A0#include "c-family/c-objc.h" >>> =A0#include "timevar.h" >>> +#include "multiversion.h" >>> >>> =A0/* The various kinds of conversion. =A0*/ >>> >>> @@ -6730,6 +6731,17 @@ build_over_call (struct z_candidate *cand, int f= la >>> =A0 if (!already_used) >>> =A0 =A0 mark_used (fn); >>> >>> + =A0/* For a call to a multi-versioned function, the call should actua= lly be to >>> + =A0 =A0 the dispatcher. =A0*/ >>> + =A0if (DECL_FUNCTION_VERSIONED (fn)) >>> + =A0 =A0{ >>> + =A0 =A0 =A0tree ifunc_decl; >>> + =A0 =A0 =A0ifunc_decl =3D get_ifunc_for_version (fn); >>> + =A0 =A0 =A0gcc_assert (ifunc_decl !=3D NULL); >>> + =A0 =A0 =A0return build_call_expr_loc_array (UNKNOWN_LOCATION, ifunc_= decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 nargs, argarray); >>> + =A0 =A0} >>> + >>> =A0 if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) =3D=3D 0) >>> =A0 =A0 { >>> =A0 =A0 =A0 tree t; >>> @@ -7980,6 +7992,30 @@ joust (struct z_candidate *cand1, struct z_candi= da >>> =A0 size_t i; >>> =A0 size_t len; >>> >>> + =A0/* For Candidates of a multi-versioned function, the one marked de= fault >>> + =A0 =A0 wins. =A0This is because the default decl is used as key to a= ggregate >>> + =A0 =A0 all the other versions provided for it in multiversion.c. =A0= When >>> + =A0 =A0 generating the actual call, the appropriate dispatcher is cre= ated >>> + =A0 =A0 to call the right function version at run-time. =A0*/ >>> + >>> + =A0if ((TREE_CODE (cand1->fn) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 && DECL_FUNCTION_VERSIONED (cand1->fn)) >>> + =A0 =A0 =A0||(TREE_CODE (cand2->fn) =3D=3D FUNCTION_DECL >>> + =A0 =A0 =A0 =A0&& DECL_FUNCTION_VERSIONED (cand2->fn))) >>> + =A0 =A0{ >>> + =A0 =A0 =A0if (is_default_function (cand1->fn)) >>> + =A0 =A0 =A0 { >>> + =A0 =A0 =A0 =A0 =A0mark_used (cand2->fn); >>> + =A0 =A0 =A0 =A0 return 1; >>> + =A0 =A0 =A0 } >>> + =A0 =A0 =A0if (is_default_function (cand2->fn)) >>> + =A0 =A0 =A0 { >>> + =A0 =A0 =A0 =A0 =A0mark_used (cand1->fn); >>> + =A0 =A0 =A0 =A0 return -1; >>> + =A0 =A0 =A0 } >>> + =A0 =A0 =A0return 0; >>> + =A0 =A0} >>> + >>> =A0 /* Candidates that involve bad conversions are always worse than th= ose >>> =A0 =A0 =A0that don't. =A0*/ >>> =A0 if (cand1->viable > cand2->viable) >>> Index: timevar.def >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- timevar.def (revision 184971) >>> +++ timevar.def (working copy) >>> @@ -253,6 +253,7 @@ DEFTIMEVAR (TV_TREE_IFCOMBINE =A0 =A0 =A0 =A0, "tre= e if-co >>> =A0DEFTIMEVAR (TV_TREE_UNINIT =A0 =A0 =A0 =A0 =A0 , "uninit var analysi= s") >>> =A0DEFTIMEVAR (TV_PLUGIN_INIT =A0 =A0 =A0 =A0 =A0 , "plugin initializat= ion") >>> =A0DEFTIMEVAR (TV_PLUGIN_RUN =A0 =A0 =A0 =A0 =A0 =A0, "plugin execution= ") >>> +DEFTIMEVAR (TV_MULTIVERSION_DISPATCH , "multiversion dispatch") >>> >>> =A0/* Everything else in rest_of_compilation not included above. =A0*/ >>> =A0DEFTIMEVAR (TV_EARLY_LOCAL =A0 =A0 =A0 =A0 =A0, "early local passes") >>> Index: varasm.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- varasm.c =A0 =A0(revision 184971) >>> +++ varasm.c =A0 =A0(working copy) >>> @@ -5755,6 +5755,8 @@ finish_aliases_1 (void) >>> =A0 =A0 =A0 =A0} >>> =A0 =A0 =A0 else if (! (p->emitted_diags & ALIAS_DIAG_TO_EXTERN) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 && DECL_EXTERNAL (target_decl) >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0&& (!TREE_CODE (target_decl) =3D=3D FUNCTI= ON_DECL >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|| !DECL_STRUCT_FUNCTION (target_d= ecl)) >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* We use local aliases for C++ thunks to f= orce the tailcall >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0to bind locally. =A0This is a hack -= to keep it working do >>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0the following (which is not strictly= correct). =A0*/ >>> Index: Makefile.in >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- Makefile.in (revision 184971) >>> +++ Makefile.in (working copy) >>> @@ -1298,6 +1298,7 @@ OBJS =3D \ >>> =A0 =A0 =A0 =A0mcf.o \ >>> =A0 =A0 =A0 =A0mode-switching.o \ >>> =A0 =A0 =A0 =A0modulo-sched.o \ >>> + =A0 =A0 =A0 multiversion.o \ >>> =A0 =A0 =A0 =A0omega.o \ >>> =A0 =A0 =A0 =A0omp-low.o \ >>> =A0 =A0 =A0 =A0optabs.o \ >>> @@ -3030,6 +3031,11 @@ ree.o : ree.c $(CONFIG_H) $(SYSTEM_H) coretypes.h >>> =A0 =A0$(DF_H) $(TIMEVAR_H) tree-pass.h $(RECOG_H) $(EXPR_H) \ >>> =A0 =A0$(REGS_H) $(TREE_H) $(TM_P_H) insn-config.h $(INSN_ATTR_H) $(DIA= GNOSTIC_CORE_H) \ >>> =A0 =A0$(TARGET_H) $(OPTABS_H) insn-codes.h rtlhooks-def.h $(PARAMS_H) = $(CGRAPH_H) >>> +multiversion.o : multiversion.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(= TM_H) \ >>> + =A0 $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) int= l.h \ >>> + =A0 $(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) tree-pass.h= \ >>> + =A0 $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(RTL_H) $(IPA= _PROP_H) \ >>> + =A0 $(BASIC_BLOCK_H) $(TOPLEV_H) $(TREE_DUMP_H) ipa-inline.h >>> =A0cprop.o : cprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_= H) \ >>> =A0 =A0$(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(GGC_H) \ >>> =A0 =A0$(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) $(FUNCTION_H) output.h top= lev.h $(DIAGNOSTIC_CORE_H) \ >>> Index: passes.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- passes.c =A0 =A0(revision 184971) >>> +++ passes.c =A0 =A0(working copy) >>> @@ -1190,6 +1190,7 @@ init_optimization_passes (void) >>> =A0 NEXT_PASS (pass_build_cfg); >>> =A0 NEXT_PASS (pass_warn_function_return); >>> =A0 NEXT_PASS (pass_build_cgraph_edges); >>> + =A0NEXT_PASS (pass_dispatch_versions); >>> =A0 *p =3D NULL; >>> >>> =A0 /* Interprocedural optimization passes. =A0*/ >>> Index: config/i386/i386.c >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- config/i386/i386.c =A0(revision 184971) >>> +++ config/i386/i386.c =A0(working copy) >>> @@ -27446,6 +27473,593 @@ ix86_init_mmx_sse_builtins (void) >>> =A0 =A0 } >>> =A0} >>> >>> +/* This adds a condition to the basic_block NEW_BB in function FUNCTIO= N_DECL >>> + =A0 to return a pointer to VERSION_DECL if the outcome of the function >>> + =A0 PREDICATE_DECL is true. =A0This function will be called during ve= rsion >>> + =A0 dispatch to decide which function version to execute. =A0It retur= ns the >>> + =A0 basic block at the end to which more conditions can be added. =A0= */ >>> + >>> +static basic_block >>> +add_condition_to_bb (tree function_decl, tree version_decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0basic_block new_bb, tree predi= cate_decl) >>> +{ >>> + =A0gimple return_stmt; >>> + =A0tree convert_expr, result_var; >>> + =A0gimple convert_stmt; >>> + =A0gimple call_cond_stmt; >>> + =A0gimple if_else_stmt; >>> + >>> + =A0basic_block bb1, bb2, bb3; >>> + =A0edge e12, e23; >>> + >>> + =A0tree cond_var; >>> + =A0gimple_seq gseq; >>> + >>> + =A0tree old_current_function_decl; >>> + >>> + =A0old_current_function_decl =3D current_function_decl; >>> + =A0push_cfun (DECL_STRUCT_FUNCTION (function_decl)); >>> + =A0current_function_decl =3D function_decl; >>> + >>> + =A0gcc_assert (new_bb !=3D NULL); >>> + =A0gseq =3D bb_seq (new_bb); >>> + >>> + >>> + =A0convert_expr =3D build1 (CONVERT_EXPR, ptr_type_node, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0build_fold_addr_expr (= version_decl)); >>> + =A0result_var =3D create_tmp_var (ptr_type_node, NULL); >>> + =A0convert_stmt =3D gimple_build_assign (result_var, convert_expr); >>> + =A0return_stmt =3D gimple_build_return (result_var); >>> + >>> + =A0if (predicate_decl =3D=3D NULL_TREE) >>> + =A0 =A0{ >>> + =A0 =A0 =A0gimple_seq_add_stmt (&gseq, convert_stmt); >>> + =A0 =A0 =A0gimple_seq_add_stmt (&gseq, return_stmt); >>> + =A0 =A0 =A0set_bb_seq (new_bb, gseq); >>> + =A0 =A0 =A0gimple_set_bb (convert_stmt, new_bb); >>> + =A0 =A0 =A0gimple_set_bb (return_stmt, new_bb); >>> + =A0 =A0 =A0pop_cfun (); >>> + =A0 =A0 =A0current_function_decl =3D old_current_function_decl; >>> + =A0 =A0 =A0return new_bb; >>> + =A0 =A0} >>> + >>> + =A0cond_var =3D create_tmp_var (integer_type_node, NULL); >>> + =A0call_cond_stmt =3D gimple_build_call (predicate_decl, 0); >>> + =A0gimple_call_set_lhs (call_cond_stmt, cond_var); >>> + >>> + =A0gimple_set_block (call_cond_stmt, DECL_INITIAL (function_decl)); >>> + =A0gimple_set_bb (call_cond_stmt, new_bb); >>> + =A0gimple_seq_add_stmt (&gseq, call_cond_stmt); >>> + >>> + =A0if_else_stmt =3D gimple_build_cond (GT_EXPR, cond_var, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i= nteger_zero_node, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 N= ULL_TREE, NULL_TREE); >>> + =A0gimple_set_block (if_else_stmt, DECL_INITIAL (function_decl)); >>> + =A0gimple_set_bb (if_else_stmt, new_bb); >>> + =A0gimple_seq_add_stmt (&gseq, if_else_stmt); >>> + >>> + =A0gimple_seq_add_stmt (&gseq, convert_stmt); >>> + =A0gimple_seq_add_stmt (&gseq, return_stmt); >>> + =A0set_bb_seq (new_bb, gseq); >>> + >>> + =A0bb1 =3D new_bb; >>> + =A0e12 =3D split_block (bb1, if_else_stmt); >>> + =A0bb2 =3D e12->dest; >>> + =A0e12->flags &=3D ~EDGE_FALLTHRU; >>> + =A0e12->flags |=3D EDGE_TRUE_VALUE; >>> + >>> + =A0e23 =3D split_block (bb2, return_stmt); >>> + >>> + =A0gimple_set_bb (convert_stmt, bb2); >>> + =A0gimple_set_bb (return_stmt, bb2); >>> + >>> + =A0bb3 =3D e23->dest; >>> + =A0make_edge (bb1, bb3, EDGE_FALSE_VALUE); >>> + >>> + =A0remove_edge (e23); >>> + =A0make_edge (bb2, EXIT_BLOCK_PTR, 0); >>> + >>> + =A0rebuild_cgraph_edges (); >>> + >>> + =A0pop_cfun (); >>> + =A0current_function_decl =3D old_current_function_decl; >>> + >>> + =A0return bb3; >>> +} >>> + >>> +/* This parses the attribute arguments to targetv in DECL and determin= es >>> + =A0 the right builtin to use to match the platform specification. >>> + =A0 For now, only one target argument ("arch=3D") is allowed. =A0*/ >>> + >>> +static enum ix86_builtins >>> +get_builtin_code_for_version (tree decl) >>> +{ >>> + =A0tree attrs; >>> + =A0struct cl_target_option cur_target; >>> + =A0tree target_node; >>> + =A0struct cl_target_option *new_target; >>> + =A0enum ix86_builtins builtin_code =3D IX86_BUILTIN_MAX; >>> + >>> + =A0attrs =3D lookup_attribute ("targetv", DECL_ATTRIBUTES (decl)); >>> + =A0gcc_assert (attrs !=3D NULL); >>> + >>> + =A0cl_target_option_save (&cur_target, &global_options); >>> + >>> + =A0target_node =3D ix86_valid_target_attribute_tree >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (TREE_VALUE (TREE_VALUE (attrs))); >>> + >>> + =A0gcc_assert (target_node); >>> + =A0new_target =3D TREE_TARGET_OPTION (target_node); >>> + =A0gcc_assert (new_target); >>> + >>> + =A0if (new_target->arch_specified && new_target->arch > 0) >>> + =A0 =A0{ >>> + =A0 =A0 =A0switch (new_target->arch) >>> + =A0 =A0 =A0 =A0{ >>> + =A0 =A0 =A0 case 1: >>> + =A0 =A0 =A0 case 2: >>> + =A0 =A0 =A0 case 3: >>> + =A0 =A0 =A0 case 4: >>> + =A0 =A0 =A0 case 5: >>> + =A0 =A0 =A0 case 6: >>> + =A0 =A0 =A0 case 7: >>> + =A0 =A0 =A0 case 8: >>> + =A0 =A0 =A0 case 9: >>> + =A0 =A0 =A0 case 10: >>> + =A0 =A0 =A0 case 11: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_INTEL; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 12: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_INTEL_CORE2; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 13: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_INTEL_COREI7; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 14: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_INTEL_ATOM; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 15: >>> + =A0 =A0 =A0 case 16: >>> + =A0 =A0 =A0 case 17: >>> + =A0 =A0 =A0 case 18: >>> + =A0 =A0 =A0 case 19: >>> + =A0 =A0 =A0 case 20: >>> + =A0 =A0 =A0 case 21: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_AMD; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 22: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_AMDFAM10H; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 23: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_AMDFAM15H_BDVER1; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 24: >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_AMDFAM15H_BDVER2; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 case 25: /* What is btver1 ? */ >>> + =A0 =A0 =A0 =A0 builtin_code =3D IX86_BUILTIN_CPU_IS_AMD; >>> + =A0 =A0 =A0 =A0 break; >>> + =A0 =A0 =A0 } >>> + =A0 =A0} >>> + >>> + =A0cl_target_option_restore (&global_options, &cur_target); >>> + =A0if (builtin_code =3D=3D IX86_BUILTIN_MAX) >>> + =A0 =A0 =A0error_at (DECL_SOURCE_LOCATION (decl), >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 "No dispatcher found for the versioning a= ttributes"); >>> + >>> + =A0return builtin_code; >>> +} >>> + >>> +/* This is the target hook to generate the dispatch function for >>> + =A0 multi-versioned functions. =A0DISPATCH_DECL is the function which= will >>> + =A0 contain the dispatch logic. =A0FNDECLS are the function choices f= or >>> + =A0 dispatch, and is a tree chain. =A0EMPTY_BB is the basic block poi= nter >>> + =A0 in DISPATCH_DECL in which the dispatch code is generated. =A0*/ >>> + >>> +static int >>> +ix86_dispatch_version (tree dispatch_decl, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0void *fndecls_p, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0basic_block *empty_bb) >>> +{ >>> + =A0tree default_decl; >>> + =A0gimple ifunc_cpu_init_stmt; >>> + =A0gimple_seq gseq; >>> + =A0tree old_current_function_decl; >>> + =A0int ix; >>> + =A0tree ele; >>> + =A0VEC (tree, heap) *fndecls; >>> + >>> + =A0gcc_assert (dispatch_decl !=3D NULL >>> + =A0 =A0 =A0 =A0 =A0 =A0 && fndecls_p !=3D NULL >>> + =A0 =A0 =A0 =A0 =A0 =A0 && empty_bb !=3D NULL); >>> + >>> + =A0/*fndecls_p is actually a vector. =A0*/ >>> + =A0fndecls =3D (VEC (tree, heap) *)fndecls_p; >>> + >>> + =A0/* Atleast one more version other than the default. =A0*/ >>> + =A0gcc_assert (VEC_length (tree, fndecls) >=3D 2); >>> + >>> + =A0/* The first version in the vector is the default decl. =A0*/ >>> + =A0default_decl =3D VEC_index (tree, fndecls, 0); >>> + >>> + =A0old_current_function_decl =3D current_function_decl; >>> + =A0push_cfun (DECL_STRUCT_FUNCTION (dispatch_decl)); >>> + =A0current_function_decl =3D dispatch_decl; >>> + >>> + =A0gseq =3D bb_seq (*empty_bb); >>> + =A0ifunc_cpu_init_stmt =3D gimple_build_call_vec ( >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ix86_builtins [(int) IX86_BUI= LTIN_CPU_INIT], NULL); >>> + =A0gimple_seq_add_stmt (&gseq, ifunc_cpu_init_stmt); >>> + =A0gimple_set_bb (ifunc_cpu_init_stmt, *empty_bb); >>> + =A0set_bb_seq (*empty_bb, gseq); >>> + >>> + =A0pop_cfun (); >>> + =A0current_function_decl =3D old_current_function_decl; >>> + >>> + >>> + =A0for (ix =3D 1; VEC_iterate (tree, fndecls, ix, ele); ++ix) >>> + =A0 =A0{ >>> + =A0 =A0 =A0tree version_decl =3D ele; >>> + =A0 =A0 =A0/* Get attribute string, parse it and find the right predi= cate decl. >>> + =A0 =A0 =A0 =A0 The predicate function could be a lengthy combination= of many >>> + =A0 =A0 =A0 =A0features, like arch-type and various isa-variants. =A0= For now, only >>> + =A0 =A0 =A0 =A0check the arch-type. =A0*/ >>> + =A0 =A0 =A0tree predicate_decl =3D ix86_builtins [ >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 get_builtin_code_for_vers= ion (version_decl)]; >>> + =A0 =A0 =A0*empty_bb =3D add_condition_to_bb (dispatch_decl, version_= decl, *empty_bb, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0predicate_decl); >>> + >>> + =A0 =A0} >>> + =A0/* dispatch default version at the end. =A0*/ >>> + =A0*empty_bb =3D add_condition_to_bb (dispatch_decl, default_decl, *e= mpty_bb, >>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0NU= LL); >>> + =A0return 0; >>> +} >>> >>> @@ -38610,6 +39269,12 @@ ix86_autovectorize_vector_sizes (void) >>> =A0#undef TARGET_BUILD_BUILTIN_VA_LIST >>> =A0#define TARGET_BUILD_BUILTIN_VA_LIST ix86_build_builtin_va_list >>> >>> +#undef TARGET_DISPATCH_VERSION >>> +#define TARGET_DISPATCH_VERSION ix86_dispatch_version >>> + >>> =A0#undef TARGET_ENUM_VA_LIST_P >>> =A0#define TARGET_ENUM_VA_LIST_P ix86_enum_va_list >>> >>> Index: testsuite/g++.dg/mv1.C >>> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> --- testsuite/g++.dg/mv1.C =A0 =A0 =A0(revision 0) >>> +++ testsuite/g++.dg/mv1.C =A0 =A0 =A0(revision 0) >>> @@ -0,0 +1,23 @@ >>> +/* Simple test case to check if Multiversioning works. =A0*/ >>> +/* { dg-do run } */ >>> +/* { dg-options "-O2" } */ >>> + >>> +int foo (); >>> +int foo () __attribute__ ((targetv("arch=3Dcorei7"))); >>> + >>> +int main () >>> +{ >>> + =A0int (*p)() =3D &foo; >>> + =A0return foo () + (*p)(); >>> +} >>> + >>> +int foo () >>> +{ >>> + =A0return 0; >>> +} >>> + >>> +int __attribute__ ((targetv("arch=3Dcorei7"))) >>> +foo () >>> +{ >>> + =A0return 0; >>> +} >>> >>> >>> -- >>> This patch is available for review at http://codereview.appspot.com/575= 2064