From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28380 invoked by alias); 8 Mar 2012 21:00:59 -0000 Received: (qmail 28360 invoked by uid 22791); 8 Mar 2012 21:00:49 -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:00:26 +0000 Received: by lagw12 with SMTP id w12so1038808lag.20 for ; Thu, 08 Mar 2012 13:00:23 -0800 (PST) Received: by 10.152.114.35 with SMTP id jd3mr5582765lab.18.1331240423862; Thu, 08 Mar 2012 13:00:23 -0800 (PST) MIME-Version: 1.0 Received: by 10.152.114.35 with SMTP id jd3mr5582749lab.18.1331240423668; Thu, 08 Mar 2012 13:00:23 -0800 (PST) Received: by 10.152.132.138 with HTTP; Thu, 8 Mar 2012 13:00:23 -0800 (PST) In-Reply-To: References: <20120307004630.A503DB21B6@azwildcat.mtv.corp.google.com> Date: Thu, 08 Mar 2012 21:00:00 -0000 Message-ID: Subject: Re: User directed Function Multiversioning via Function Overloading (issue5752064) From: Xinliang David Li To: Richard Guenther Cc: Sriraman Tallam , 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: ALoCoQl7+RPk8N6YZTR00THN9QLzfJfFLcK+bQ9PV1yZPtMaYscvR7vISdJ65McCuKGo0XDtLmuUeYB5QaONf+MpMzfb5A9HjAtdr9AXnzxqm92T3cTSZj+PJYILnbR2rtfU7AznyjZSxLFBvYMAUDUM10ITGxXjWQ== 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/msg00590.txt.bz2 > 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 dire= ctly > =A0 resolve calls from a "sse" function to another "sse" function without= going > =A0 through the 2nd IFUNC > > =A0- cgraph also does not know about the "overloading", so it cannot do s= uch > =A0 "devirtualization" either > > you seem to have implemented something inbetween a pure frontend > solution and a proper middle-end solution. =A0For optimization and eventu= ally > automatically selecting functions for cloning (like, callees of a manual = "sse" > versioned function should be cloned?) it would be nice if the cgraph would > know about the different versions and their relationships (and the dispat= cher). > 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. The implementation is very similar to the case when the user writes its own ifunc and resolver. The difference here is that the resolver/dispatcher is synthesized by the compiler. Thunk is different -- as it is completely user invisible. Promoting ifunc to cgraph level has its advantage, but can also introduce burdens to ipa passes as it has to be understood by them. thanks, David > > 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_VER= SION. >> =A0 =A0 =A0 =A0* doc/tm.texi: Regenerate. >> =A0 =A0 =A0 =A0* c-family/c-common.c (handle_targetv_attribute): New fun= ction. >> =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 ver= sioned >> =A0 =A0 =A0 =A0functions. >> =A0 =A0 =A0 =A0* cp/class.c: Include multiversion.h >> =A0 =A0 =A0 =A0(add_method): aggregate function versions. Change assembl= er names of >> =A0 =A0 =A0 =A0versioned functions. >> =A0 =A0 =A0 =A0(resolve_address_of_overloaded_function): Match address o= f function >> =A0 =A0 =A0 =A0version with default function. =A0Return address of ifunc= dispatcher >> =A0 =A0 =A0 =A0for address of versioned functions. >> =A0 =A0 =A0 =A0* cp/decl.c (decls_match): Make decls unmatched for versi= oned >> =A0 =A0 =A0 =A0functions. >> =A0 =A0 =A0 =A0(duplicate_decls): Remove ambiguity for versioned functio= ns. Notify >> =A0 =A0 =A0 =A0of deleted function version decls. >> =A0 =A0 =A0 =A0(start_decl): Change assembler name of versioned function= s. >> =A0 =A0 =A0 =A0(start_function): Change assembler name of versioned func= tions. >> =A0 =A0 =A0 =A0(cxx_comdat_group): Make comdat group of versioned functi= ons be the >> =A0 =A0 =A0 =A0same. >> =A0 =A0 =A0 =A0* cp/semantics.c (expand_or_defer_fn_1): Mark as needed v= ersioned >> =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 functions= to call the >> =A0 =A0 =A0 =A0dispatcher. >> =A0 =A0 =A0 =A0(joust): For calls to multi-versioned functions, make the= 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 simplif= ied >> =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{dispatc= h_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 with= in 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 simplif= ied >> =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 with= in 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, handle= r, >> =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_targetv= _attribute, false }, >> =A0 { "packed", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 0, false, false, fals= e, >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0handle_packed= _attribute , false}, >> =A0 { "nocommon", =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 0, true, =A0false, fals= e, >> @@ -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 whic= h " >> + =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, fla= gs, >> + =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-vers= ioned >> + =A0 functions. =A0DISPATCH_DECL is the function that will have the dis= patching >> + =A0 logic. =A0FNDECLS are the list of choices for dispatch and EMPTY_B= B 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 (tre >> =A0#define DECL_FUNCTION_SPECIFIC_OPTIMIZATION(NODE) \ >> =A0 =A0(FUNCTION_DECL_CHECK (NODE)->function_decl.function_specific_opti= mization) >> >> +/* In FUNCTION_DECL, this is set if this function has other versions ge= nerated >> + =A0 using "targetv" attributes. =A0The default version is the one whic= h 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 eith= er inherit >> =A0 =A0FUNCTION_DECL from non_common, or inherit non_common from FUNCTIO= N_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 functio= ns >> + =A0 will be done here. >> + >> + =A0 Function versions are created by using the same function signature= 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 functi= on 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 th= is >> + =A0 structure. =A0 Since this is called by the front-end, decl merging= 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 decl. >> + =A0 IFUNC_DECL is the decl of the ifunc function for default decls. >> + =A0 IFUNC_RESOLVER_DECL is the decl of the dispatch function. =A0VERSI= ONS >> + =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 stor= es the >> + =A0 list of all the other function versions. =A0Each entry is a struct= ure >> + =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_d= escriptor, >> + =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 whi= ch >> + =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 "target= v" >> + =A0 or if the "targetv" attribute strings of DECL1 and DECL2 dont matc= h. =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_inl= ine," >> + =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 (decl= )); >> + =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 vers= ion %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 func= tion, >> + =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 (decl= )) >> + =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_p= ointer (decl)); >> + >> + =A0if (slot !=3D NULL) >> + =A0 =A0return (version_function *)slot; >> + >> + =A0return NULL; >> +} >> + >> +/* Record DECL as a function version by creating a version_function str= uct >> + =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 ht= ab_hash_pointer ((const void_p)decl), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0INS= ERT); >> + >> + =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 dupl= icate >> + =A0 decl is merged with the original decl and the duplicate decl is de= leted. >> + =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. =A0O= ne >> + =A0 of DECL1 and DECL2 must be the default, otherwise this function do= es >> + =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 a= nd >> + =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 functi= on has >> + =A0 =A0 to be externally visible for the loader to find it. =A0So, app= ending >> + =A0 =A0 the filename will prevent conflicts with a resolver function f= rom >> + =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_typ= e_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_referenced= _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 (if= unc_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 Return= 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 TY= PE_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 not= 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); ++i= x) >> + =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 tar= get >> + =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); ++i= x) >> + =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 in= dex or >> + =A0 =A0 =A0 =A0not. =A0This happens for methods in derived classes tha= t 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 suppo= rted\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_b= b); >> + =A0decl_v->ifunc_resolver_decl =3D ifunc_resolver_decl; >> + >> + =A0return ifunc_resolver_decl; >> +} >> + >> +/* Main entry point to pass_dispatch_versions. For multi-versioned func= tions, >> + =A0 generate the dispatching code. =A0*/ >> + >> +static unsigned int >> +do_dispatch_versions (void) >> +{ >> + =A0/* A new pass for generating dispatch code for multi-versioned func= tions. >> + =A0 =A0 Other forms of dispatch can be added when ifunc support is not= available >> + =A0 =A0 like just calling the function directly after checking for tar= get type. >> + =A0 =A0 Currently, dispatching is done through IFUNC. =A0This pass wil= l become >> + =A0 =A0 more meaningful when other dispatch mechanisms are added. =A0*/ >> + >> + =A0/* Cloning a function to produce more versions will happen here whe= n the >> + =A0 =A0 user requests that via the targetv attribute. For example, >> + =A0 =A0 int foo () __attribute__ ((targetv(("arch=3Dcore2"), ("arch=3D= corei7")))); >> + =A0 =A0 means that the user wants the same body of foo to be versioned= for core2 >> + =A0 =A0 and corei7. =A0In that case, this function will be cloned duri= ng 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_func= tion_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 vers= ion >> + =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_fl= ags_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 uniqu= e. >> + =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. DE= CL >> + =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 "target= v" >> + =A0 or if the "targetv" attribute strings of =A0DECL1 and DECL2 dont m= atch. =A0*/ >> +bool has_different_version_attributes (const tree decl1, const tree dec= l2); >> + >> +/* If DECL is a function version and not the default version, the assem= bler >> + =A0 name of DECL is changed to include the attribute string to keep the >> + =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_dec >> =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 (me= thod_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 function= 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_dec >> =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 targe >> =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 targe >> =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. =A0Thi= s works >> + =A0 =A0 well because indirectly calling the function will dispatch the= right >> + =A0 =A0 function version at run-time. Also, the function address is ke= pt >> + =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 different= 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 he= re. =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", old= decl); >> =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 (newdecl= )), >> + =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 (oldde= cl)) >> + =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_TYPE= S (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 asse= mbler >> + =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 *declspec= s, >> =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_nod= e)); >> >> + =A0/* If this decl is a function version and not the default, its asse= mbler >> + =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 m= ark >> =A0 =A0 =A0 =A0 this function as needed so that finish_file will make su= re to >> =A0 =A0 =A0 =A0 output it later. =A0Similarly, all dllexport'd functions= 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 inli= ne 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 TEMPL= ATE_DECL)) >> =A0 =A0 =A0 =A0 =A0 =A0continue; >> >> + =A0 =A0 =A0 =A0 /* While finding a match, same types and params are no= t enough >> + =A0 =A0 =A0 =A0 =A0 =A0if the function is versioned. =A0Also check ver= sion ("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_TYPE= (fndecl))) >> =A0 =A0 =A0 =A0 =A0 =A0 =A0&& compparms (p1, p2) >> + =A0 =A0 =A0 =A0 =A0 =A0 && !has_different_version_attributes (function= , fndecl) >> =A0 =A0 =A0 =A0 =A0 =A0 =A0&& (!is_template >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|| comp_template_parms (template_parm= s, >> =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 fla >> =A0 if (!already_used) >> =A0 =A0 mark_used (fn); >> >> + =A0/* For a call to a multi-versioned function, the call should actual= ly 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_d= ecl, >> + =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_candida >> =A0 size_t i; >> =A0 size_t len; >> >> + =A0/* For Candidates of a multi-versioned function, the one marked def= ault >> + =A0 =A0 wins. =A0This is because the default decl is used as key to ag= gregate >> + =A0 =A0 all the other versions provided for it in multiversion.c. =A0W= hen >> + =A0 =A0 generating the actual call, the appropriate dispatcher is crea= ted >> + =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 tho= se >> =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, "tree= if-co >> =A0DEFTIMEVAR (TV_TREE_UNINIT =A0 =A0 =A0 =A0 =A0 , "uninit var analysis= ") >> =A0DEFTIMEVAR (TV_PLUGIN_INIT =A0 =A0 =A0 =A0 =A0 , "plugin initializati= on") >> =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 FUNCTIO= N_DECL >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0|| !DECL_STRUCT_FUNCTION (target_de= cl)) >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* We use local aliases for C++ thunks to fo= rce 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) $(DIAG= NOSTIC_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 $(T= M_H) \ >> + =A0 $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl= .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 topl= ev.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 FUNCTION= _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 ver= sion >> + =A0 dispatch to decide which function version to execute. =A0It return= s 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 predic= ate_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 (v= ersion_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 in= teger_zero_node, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NU= LL_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 determines >> + =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 at= tributes"); >> + >> + =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 for >> + =A0 dispatch, and is a tree chain. =A0EMPTY_BB is the basic block poin= ter >> + =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_BUIL= TIN_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 predic= ate 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. =A0F= or 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_versi= on (version_decl)]; >> + =A0 =A0 =A0*empty_bb =3D add_condition_to_bb (dispatch_decl, version_d= ecl, *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, *em= pty_bb, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0NUL= L); >> + =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/5752= 064