From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lj1-x22c.google.com (mail-lj1-x22c.google.com [IPv6:2a00:1450:4864:20::22c]) by sourceware.org (Postfix) with ESMTPS id B45D03858D32 for ; Mon, 14 Nov 2022 10:10:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org B45D03858D32 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-lj1-x22c.google.com with SMTP id z24so12482702ljn.4 for ; Mon, 14 Nov 2022 02:10:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=OOTBW3M7wwn4tfeRHn0WmOfR+Mm9N62xBTdeQ6W/x18=; b=YfVsb6nZYjnufV6W6HcU06wTNv7jQGrLepO2rCqZoK2vbqrcRQCu0w4JGkE8PC4Egu Pi+lPyoZ8lVnSBvVV8T9k5/y4RVt42NfpIpjX0Tde79vz9oTvBAPaBlFwwiLPBKuQXH0 OGB0EOrPNhGYO/+kGzcnVfID5DbPlWfjKBJeK8Nfdh6KZeg583b2sVqyb2YT/ZVVm614 28awa21PnC0rxhM1Xjkj7ZWMs1ZmV1M4FJ9Lz3MeEzS3/Rfk52kOLSwJVIkj2dCYk6T/ g/8JYmI37BhTfhsDHEmOas/asEZcaaov4ozfBYc+plyb9hRt/G6fWpWNL9THAMRxuVZd Ed0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=OOTBW3M7wwn4tfeRHn0WmOfR+Mm9N62xBTdeQ6W/x18=; b=RJ5880XpUUvaIwcCWSzh95hCX1E0nA5mdZ9ZNI7BvD5j2GfkiyQ59HlPlOg1lPTdOP 4N9aix5R/RhFfIqdkOed1yURRthe2egnWXCA4XKAgUfMotPQ8+YLCDZgEYJEzQtpt1BH aQZe8ljdlFu8VIBvQ1hh5t1vlVgzFY9/wdVRWYnki5FYPYRimtIXh7Re9fXh0uut2jrh 7d8s2JFSKzzQsFgCOy+MzBwT/PzwXWyfgoznZcA5gsxnc3dMVUapxEr1NYkUeCFnXth6 3IzJLOvoeOAopbk2NKDfxriPAYMU0KHHexjXR/tdQPlP3jzrHO/Zae7k7VTUapaZXrhI 2vvQ== X-Gm-Message-State: ANoB5pnN+OxFvtX4QmfqL8nLvZhiXA2ywoga3i8trvKBwlyG2nl8bbMy i9PI+xLGQA0GKyOrjCeJ4RS2klUwxXsU7iyVf34= X-Google-Smtp-Source: AA0mqf4nm6M6vWGStjaId/Al4N/yFzcNaFEN1V8SEzq9ykUZbOxpslhVFPy/PwoDKiOlex0z4XWv0WCXg7EddXpt1bo= X-Received: by 2002:a2e:93c9:0:b0:26d:fc06:b7a9 with SMTP id p9-20020a2e93c9000000b0026dfc06b7a9mr3959006ljh.354.1668420631067; Mon, 14 Nov 2022 02:10:31 -0800 (PST) MIME-Version: 1.0 References: <20221113150912.1292332-1-christoph.muellner@vrull.eu> In-Reply-To: From: Richard Biener Date: Mon, 14 Nov 2022 11:10:18 +0100 Message-ID: Subject: Re: [RFC PATCH] ipa-guarded-deref: Add new pass to dereference function pointers To: =?UTF-8?Q?Christoph_M=C3=BCllner?= Cc: gcc-patches@gcc.gnu.org, Manolis Tsamis , Martin Jambor , Jan Hubicka , Philipp Tomsich Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Mon, Nov 14, 2022 at 10:32 AM Christoph M=C3=BCllner wrote: > > > > On Mon, Nov 14, 2022 at 10:00 AM Richard Biener wrote: >> >> On Mon, Nov 14, 2022 at 9:13 AM Christoph M=C3=BCllner >> wrote: >> > >> > >> > >> > On Mon, Nov 14, 2022 at 8:31 AM Richard Biener wrote: >> >> >> >> On Sun, Nov 13, 2022 at 4:09 PM Christoph Muellner >> >> wrote: >> >> > >> >> > From: Christoph M=C3=BCllner >> >> > >> >> > This patch adds a new pass that looks up function pointer assignmen= ts, >> >> > and adds guarded direct calls to the call sites of the function >> >> > pointers. >> >> > >> >> > E.g.: Lets assume an assignment to a function pointer as follows: >> >> > b->cb =3D &myfun; >> >> > Other part of the program can use the function pointer as fol= lows: >> >> > b->cb (); >> >> > With this pass the invocation will be transformed to: >> >> > if (b->cb =3D=3D myfun) >> >> > myfun(); >> >> > else >> >> > b->cb () >> >> > >> >> > The impact of the dynamic guard is expected to be less than the spe= edup >> >> > gained by enabled optimizations (e.g. inlining or constant propagat= ion). >> >> >> >> We have speculative devirtualization doing this very transform, shoul= dn't you >> >> instead improve that instead of inventing another specialized pass? >> > >> > >> > Yes, it can be integrated into ipa-devirt. >> > >> > The reason we initially decided to move it into its own file was that = C++ devirtualization >> > and function pointer dereferencing/devirtualization will likely not us= e the same analysis. >> > E.g. ODR only applies to C++, C++ tables are not directly exposed to t= he user. >> > So we figured that different things should not be merged together, but= a reuse >> > of common code to avoid duplication is mandatory. >> >> Btw, in other context the idea came up to build candidates based on avai= lable >> API/ABI (that can be indirectly called). That would help for example th= e >> get_ref calls in refine_subpel in the x264 benchmark. Maybe what you >> do is actually >> the very same thing (but look for explicit address-taking) - I didn't >> look into whether >> you prune the list of candidates based on API/ABI. > > > No, I don't consider API/ABI at all (do you have a pointer so I can get a= better understanding of that idea?). No, it was just an idea discussed internally. > Adding guards for all possible functions with the same API/ABI seems expe= nsive (I might misunderstand the idea). > My patch adds a maximum of 1 test per call site. > > What I do is looking which addresses are assigned to the function pointer= . > If there is more than one assigned function, I drop the function pointer = from the list of candidates. OK. If the program is type correct that's probably going to work well enough. If there are more than one candidates then you could prune those by simple API checks, like match up the number of arguments or void vs. non-void return type. More advanced pruning might lose some valid candidates (API vs. ABI compatibility), but it's only heuristic pruning in any case. It would probably help depending on what exactly "assigned to the function pointer" means. If the function pointer is not from directly visible static storage then matching up assignments and uses is going to be a difficult IPA problem itself. So our original idea was fo= r (*fnptr) (args ...); look for all possible definitions in the (LTO) unit that match the call signature and that have their address taken and that possibly could be pointed to by fnptr and if that's a single one, speculatively devirtualize that. > I just checked in the dump file, and the patch also dereferences the indi= rect calls to get_ref in refine_subpel. IIRC the x264 case has a global variable with all the function pointers so your implementation will likely pick up the single assignment to the individual fields. Richard. > >> >> >> > The patch uses the same API like speculative devirtualization in the p= ropagation >> > phase (ipa_make_edge_direct_to_target) and does not do anything in the >> > transformation phase. So there is no duplication of functionality. >> > >> > I will move the code into ipa-devirt. >> > >> > Thanks! >> > >> > >> >> >> >> >> >> Thanks, >> >> Richard. >> >> >> >> > PR ipa/107666 >> >> > gcc/ChangeLog: >> >> > >> >> > * Makefile.in: Add new pass. >> >> > * common.opt: Add flag -fipa-guarded-deref. >> >> > * lto-section-in.cc: Add new section "ipa_guarded_deref". >> >> > * lto-streamer.h (enum lto_section_type): Add new section. >> >> > * passes.def: Add new pass. >> >> > * timevar.def (TV_IPA_GUARDED_DEREF): Add time var. >> >> > * tree-pass.h (make_pass_ipa_guarded_deref): New prototype. >> >> > * ipa-guarded-deref.cc: New file. >> >> > >> >> > Signed-off-by: Christoph M=C3=BCllner >> >> > --- >> >> > gcc/Makefile.in | 1 + >> >> > gcc/common.opt | 4 + >> >> > gcc/ipa-guarded-deref.cc | 1115 ++++++++++++++++++++++++++++++++++= ++++ >> >> > gcc/lto-section-in.cc | 1 + >> >> > gcc/lto-streamer.h | 1 + >> >> > gcc/passes.def | 1 + >> >> > gcc/timevar.def | 1 + >> >> > gcc/tree-pass.h | 1 + >> >> > 8 files changed, 1125 insertions(+) >> >> > create mode 100644 gcc/ipa-guarded-deref.cc >> >> > >> >> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in >> >> > index f672e6ea549..402c4a6ea3f 100644 >> >> > --- a/gcc/Makefile.in >> >> > +++ b/gcc/Makefile.in >> >> > @@ -1462,6 +1462,7 @@ OBJS =3D \ >> >> > ipa-sra.o \ >> >> > ipa-devirt.o \ >> >> > ipa-fnsummary.o \ >> >> > + ipa-guarded-deref.o \ >> >> > ipa-polymorphic-call.o \ >> >> > ipa-split.o \ >> >> > ipa-inline.o \ >> >> > diff --git a/gcc/common.opt b/gcc/common.opt >> >> > index bce3e514f65..8344940ae5b 100644 >> >> > --- a/gcc/common.opt >> >> > +++ b/gcc/common.opt >> >> > @@ -1933,6 +1933,10 @@ fipa-bit-cp >> >> > Common Var(flag_ipa_bit_cp) Optimization >> >> > Perform interprocedural bitwise constant propagation. >> >> > >> >> > +fipa-guarded-deref >> >> > +Common Var(flag_ipa_guarded_deref) Optimization >> >> > +Perform guarded function pointer derferencing. >> >> > + >> >> > fipa-modref >> >> > Common Var(flag_ipa_modref) Optimization >> >> > Perform interprocedural modref analysis. >> >> > diff --git a/gcc/ipa-guarded-deref.cc b/gcc/ipa-guarded-deref.cc >> >> > new file mode 100644 >> >> > index 00000000000..198fb9b33ad >> >> > --- /dev/null >> >> > +++ b/gcc/ipa-guarded-deref.cc >> >> > @@ -0,0 +1,1115 @@ >> >> > +/* IPA pass to transform indirect calls to guarded direct calls. >> >> > + Copyright (C) 2022 Free Software Foundation, Inc. >> >> > + Contributed by Christoph Muellner (Vrull GmbH) >> >> > + Based on work by Erick Ochoa (Vrull GmbH) >> >> > + >> >> > +This file is part of GCC. >> >> > + >> >> > +GCC is free software; you can redistribute it and/or modify it und= er >> >> > +the terms of the GNU General Public License as published by the Fr= ee >> >> > +Software Foundation; either version 3, or (at your option) any lat= er >> >> > +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. See the GNU General Public Lice= nse >> >> > +for more details. >> >> > + >> >> > +You should have received a copy of the GNU General Public License >> >> > +along with GCC; see the file COPYING3. If not see >> >> > +. */ >> >> > + >> >> > +/* Indirect calls are used to separate callees from their call sit= es. >> >> > + This helps to implement proper abstraction layers, but prevents >> >> > + optimizations like constant-propagation or function specializat= ion. >> >> > + >> >> > + Assuming that we identify a function pointer that gets assigned >> >> > + only a small amount of times, we can convert the indirect calls >> >> > + to the target function into guarded direct calls and let later >> >> > + passes apply additional optimizations. >> >> > + >> >> > + This pass does this by: >> >> > + * Identifying function pointers that are assigned up to N=3D1 t= imes >> >> > + to struct fields. >> >> > + * Convert the indirect calls into a test for the call target >> >> > + and a direct call >> >> > + * If the test fails, then the indirect call will be executed. >> >> > + >> >> > + E.g.: >> >> > + - function foo's address is taken and stored in a field of stru= ct >> >> > + o->func =3D foo; >> >> > + - the program writes into this struct field only once >> >> > + - it is possible, that we miss a store (we would need strong gu= arantees) >> >> > + therefore, we do the following conversion: >> >> > + o->func () >> >> > + <--> >> >> > + if (o->func =3D=3D foo) >> >> > + foo () >> >> > + else >> >> > + o->func () >> >> > + >> >> > + This pass is implemented as a full IPA pass that uses the LTO s= ection >> >> > + "ipa_guarded_deref". */ >> >> > + >> >> > +#include "config.h" >> >> > +#include "system.h" >> >> > +#include "coretypes.h" >> >> > +#include "backend.h" >> >> > +#include "tree.h" >> >> > +#include "gimple.h" >> >> > +#include "alloc-pool.h" >> >> > +#include "tree-pass.h" >> >> > +#include "tree-cfg.h" >> >> > +#include "ssa.h" >> >> > +#include "cgraph.h" >> >> > +#include "gimple-pretty-print.h" >> >> > +#include "gimple-iterator.h" >> >> > +#include "symbol-summary.h" >> >> > +#include "ipa-utils.h" >> >> > + >> >> > +#include "attr-fnspec.h" >> >> > +#include "gimple-ssa.h" >> >> > +#include "data-streamer.h" >> >> > +#include "lto-streamer.h" >> >> > +#include "print-tree.h" >> >> > +#include "calls.h" >> >> > +#include "gimple-fold.h" >> >> > +#include "tree-vrp.h" >> >> > +#include "ipa-prop.h" >> >> > +#include "ipa-fnsummary.h" >> >> > +#include "demangle.h" >> >> > +#include "dbgcnt.h" >> >> > +#include "intl.h" >> >> > +#include "stringpool.h" >> >> > +#include "attribs.h" >> >> > +#include "streamer-hooks.h" >> >> > + >> >> > +#include "alloc-pool.h" >> >> > +#include "tree-pass.h" >> >> > +#include "gimple-iterator.h" >> >> > +#include "tree-dfa.h" >> >> > +#include "cgraph.h" >> >> > +#include "ipa-utils.h" >> >> > +#include "symbol-summary.h" >> >> > +#include "gimple-pretty-print.h" >> >> > +#include "gimple-walk.h" >> >> > +#include "print-tree.h" >> >> > +#include "tree-streamer.h" >> >> > +#include "alias.h" >> >> > +#include "calls.h" >> >> > +#include "ipa-modref-tree.h" >> >> > +#include "ipa-modref.h" >> >> > +#include "value-range.h" >> >> > +#include "ipa-prop.h" >> >> > +#include "ipa-fnsummary.h" >> >> > +#include "attr-fnspec.h" >> >> > +#include "symtab-clones.h" >> >> > +#include "gimple-ssa.h" >> >> > +#include "tree-phinodes.h" >> >> > +#include "tree-ssa-operands.h" >> >> > +#include "ssa-iterators.h" >> >> > +#include "stringpool.h" >> >> > +#include "tree-ssanames.h" >> >> > +#include "attribs.h" >> >> > +#include "tree-cfg.h" >> >> > +#include "tree-eh.h" >> >> > +#include "hash-traits.h" >> >> > + >> >> > +/* Struct that holds a function pointer type. >> >> > + In our context a function pointer type is a record-field pair, >> >> > + with the field being of a function pointer type. */ >> >> > + >> >> > +struct function_pointer_type >> >> > +{ >> >> > + /* Record type hosting the function pointer. */ >> >> > + tree record; >> >> > + /* field_decl of the function pointer. */ >> >> > + tree field; >> >> > +}; >> >> > + >> >> > +/* Add a default hash trait for the type function_pointer_type, so= it can be used >> >> > + as key in hash collections (hash_map, hash_set, etc.). */ >> >> > + >> >> > +template <> >> >> > +struct default_hash_traits >> >> > + : typed_noop_remove >> >> > +{ >> >> > + GTY((skip)) typedef function_pointer_type value_type; >> >> > + GTY((skip)) typedef function_pointer_type compare_type; >> >> > + static hashval_t >> >> > + hash (function_pointer_type p) >> >> > + { >> >> > + return TYPE_UID (p.record) ^ DECL_UID (p.field); >> >> > + } >> >> > + static const bool empty_zero_p =3D true; >> >> > + static bool >> >> > + is_empty (function_pointer_type p) >> >> > + { >> >> > + return p.record =3D=3D NULL_TREE; >> >> > + } >> >> > + static bool >> >> > + is_deleted (function_pointer_type p ATTRIBUTE_UNUSED) >> >> > + { >> >> > + return false; >> >> > + } >> >> > + static bool >> >> > + equal (const function_pointer_type &l, >> >> > + const function_pointer_type &r) >> >> > + { >> >> > + return (l.record =3D=3D r.record) && (l.field =3D=3D r.field= ); >> >> > + } >> >> > + static void >> >> > + mark_empty (function_pointer_type &p) >> >> > + { >> >> > + p.record =3D NULL_TREE; >> >> > + p.field =3D NULL_TREE; >> >> > + } >> >> > + static void >> >> > + mark_deleted (function_pointer_type &p) >> >> > + { >> >> > + p.record =3D NULL_TREE; >> >> > + p.field =3D NULL_TREE; >> >> > + } >> >> > +}; >> >> > + >> >> > +/* Store a call target to a function-pointer-type. >> >> > + With this class we can correlate a field-record-pair >> >> > + with a function pointer field with a call target. >> >> > + >> >> > + We maintain a 1:N mapping here, i.e. a fpt can have exactly 1 c= all target, >> >> > + but a call target can be referenced by multiple fpts. >> >> > + >> >> > + Note, that the information needs to be extracted with >> >> > + the function pointer type as key and the call target as value. >> >> > + However, on call graph modification events, we need a reverse >> >> > + lookup (currenlty we don't optimize this code path). */ >> >> > + >> >> > +class function_pointer_type_assignments >> >> > +{ >> >> > +private: >> >> > + /* Track function-pointer-types and their assigned call targets.= */ >> >> > + hash_map m_assignments; >> >> > + >> >> > +public: >> >> > + function_pointer_type_assignments () {} >> >> > + >> >> > + /* Get the call target for a function pointer type (if any). */ >> >> > + cgraph_node *get_target (const function_pointer_type &v) >> >> > + { >> >> > + cgraph_node **pnode =3D m_assignments.get (v); >> >> > + return pnode ? *pnode : NULL; >> >> > + } >> >> > + >> >> > + /* Add a new assignment for a function pointer type. */ >> >> > + >> >> > + void >> >> > + add_assignment (function_pointer_type fpt, cgraph_node *target) >> >> > + { >> >> > + bool existed_p; >> >> > + cgraph_node *&node =3D m_assignments.get_or_insert (fpt, &ex= isted_p); >> >> > + if (existed_p) >> >> > + /* More, than one target -> set call target to NULL (unkn= own). */ >> >> > + node =3D NULL; >> >> > + else >> >> > + node =3D target; >> >> > + } >> >> > + >> >> > + /* Print all stored information. */ >> >> > + >> >> > + void >> >> > + print (void) >> >> > + { >> >> > + if (!dump_file) >> >> > + return; >> >> > + >> >> > + fprintf (dump_file, >> >> > + "Collected the following function pointer assignment= s:\n"); >> >> > + >> >> > + hash_map::iterator iter >> >> > + =3D m_assignments.begin (); >> >> > + for (; iter !=3D m_assignments.end (); ++iter) >> >> > + { >> >> > + function_pointer_type fpt =3D (*iter).first; >> >> > + cgraph_node* callee =3D (*iter).second; >> >> > + >> >> > + if (fpt.record =3D=3D NULL_TREE >> >> > + || fpt.field =3D=3D NULL_TREE >> >> > + || callee =3D=3D NULL) >> >> > + continue; >> >> > + >> >> > + fprintf (dump_file, " "); >> >> > + print_generic_expr (dump_file, fpt.record, TDF_NONE); >> >> > + fprintf (dump_file, "::"); >> >> > + print_generic_expr (dump_file, fpt.field, TDF_NONE); >> >> > + fprintf (dump_file, " :=3D %s\n", callee ? callee->name (= ) : ""); >> >> > + } >> >> > + } >> >> > + >> >> > + /* Callback for node removal. */ >> >> > + >> >> > + void >> >> > + remove (cgraph_node *node) >> >> > + { >> >> > + /* Iterators are not removal-safe. >> >> > + Therefore we need to advance the iterator before >> >> > + we delete the element pointed to by the iterator. >> >> > + To do so, we use a helper pointer. */ >> >> > + function_pointer_type to_delete; >> >> > + bool delete_fpt =3D false; >> >> > + >> >> > + /* We iterate over all entries, which is not optimal. >> >> > + To improve this, we need a way for a reverse-lookup. */ >> >> > + hash_map::iterator iter >> >> > + =3D m_assignments.begin (); >> >> > + for (; iter !=3D m_assignments.end (); ++iter) >> >> > + { >> >> > + /* Deletion comes *after* iterator advancement. */ >> >> > + if (delete_fpt) >> >> > + { >> >> > + m_assignments.remove (to_delete); >> >> > + delete_fpt =3D false; >> >> > + } >> >> > + >> >> > + /* Get the cgraph node and check if it matches. */ >> >> > + cgraph_node* n =3D (*iter).second; >> >> > + if (n =3D=3D node) >> >> > + { >> >> > + /* Mark for removal (see above). */ >> >> > + to_delete =3D (*iter).first; >> >> > + delete_fpt =3D true; >> >> > + } >> >> > + } >> >> > + >> >> > + /* Deletion comes *after* iterator advancement. */ >> >> > + if (delete_fpt) >> >> > + { >> >> > + m_assignments.remove (to_delete); >> >> > + delete_fpt =3D false; >> >> > + } >> >> > + } >> >> > + >> >> > + void >> >> > + serialize (struct output_block *ob, lto_symtab_encoder_t &encode= r) >> >> > + { >> >> > + unsigned HOST_WIDE_INT elements =3D m_assignments.elements (= ); >> >> > + >> >> > + /* Write the number of elements. */ >> >> > + streamer_write_uhwi (ob, elements); >> >> > + >> >> > + hash_map::iterator iter >> >> > + =3D m_assignments.begin (); >> >> > + for (; iter !=3D m_assignments.end (); ++iter) >> >> > + { >> >> > + /* Write the function pointer type. */ >> >> > + function_pointer_type fpt =3D (*iter).first; >> >> > + stream_write_tree_ref (ob, fpt.record); >> >> > + stream_write_tree_ref (ob, fpt.field); >> >> > + >> >> > + /* Write the callee. */ >> >> > + unsigned HOST_WIDE_INT symid; >> >> > + cgraph_node* callee =3D (*iter).second; >> >> > + if (callee) >> >> > + symid =3D lto_symtab_encoder_encode (encoder, callee); >> >> > + else >> >> > + symid =3D 0; >> >> > + >> >> > + streamer_write_uhwi (ob, symid); >> >> > + } >> >> > + } >> >> > + >> >> > + void >> >> > + deserialize (lto_input_block &ib, class data_in *data_in, >> >> > + lto_symtab_encoder_t &encoder) >> >> > + { >> >> > + size_t elements =3D streamer_read_uhwi (&ib); >> >> > + for (size_t i =3D 0; i < elements; i++) >> >> > + { >> >> > + /* Read the function pointer type. */ >> >> > + function_pointer_type fpt; >> >> > + fpt.record =3D stream_read_tree_ref (&ib, data_in); >> >> > + fpt.field =3D stream_read_tree_ref (&ib, data_in); >> >> > + >> >> > + /* Read the callee. */ >> >> > + cgraph_node *callee =3D NULL; >> >> > + unsigned HOST_WIDE_INT symid =3D streamer_read_uhwi (&ib)= ; >> >> > + if (symid) >> >> > + { >> >> > + symtab_node *scallee =3D lto_symtab_encoder_deref (en= coder, symid); >> >> > + callee =3D dyn_cast (scallee); >> >> > + } >> >> > + >> >> > + /* Add the function pointer type assignment. */ >> >> > + add_assignment (fpt, callee); >> >> > + } >> >> > + } >> >> > + >> >> > + ~function_pointer_type_assignments () {} >> >> > +}; >> >> > + >> >> > +/* Store a record-field-pair to a call graph edge. >> >> > + With this class we can correlate an indirect call with >> >> > + the field-record-pair of its call site. >> >> > + >> >> > + Note, that the information needs to be extracted with >> >> > + the edge as key and the function pointer type as value. */ >> >> > + >> >> > +class indirect_call_summary >> >> > + : public call_summary >> >> > +{ >> >> > +public: >> >> > + indirect_call_summary (symbol_table *table) >> >> > + : call_summary (table) >> >> > + { } >> >> > + >> >> > + /* Hook that is called by summary when an edge is duplicated. *= / >> >> > + virtual void duplicate (cgraph_edge *src ATTRIBUTE_UNUSED, >> >> > + cgraph_edge *dst ATTRIBUTE_UNUSED, >> >> > + function_pointer_type *old_fpt, >> >> > + function_pointer_type *new_fpt) >> >> > + { >> >> > + /* We may not have record-field-pair, because not every edge >> >> > + is an indirect call. */ >> >> > + if (!old_fpt) >> >> > + return; >> >> > + >> >> > + new_fpt->record =3D old_fpt->record; >> >> > + new_fpt->field =3D old_fpt->field; >> >> > + } >> >> > + >> >> > + /* Print all stored information. */ >> >> > + >> >> > + void >> >> > + print (void) >> >> > + { >> >> > + if (!dump_file) >> >> > + return; >> >> > + >> >> > + fprintf (dump_file, >> >> > + "Collected the following indirect calls:\n"); >> >> > + >> >> > + cgraph_node *caller =3D NULL; >> >> > + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (caller) >> >> > + { >> >> > + for (cgraph_edge *e =3D caller->indirect_calls; e; e =3D = e->next_callee) >> >> > + { >> >> > + function_pointer_type *fpt =3D get (e); >> >> > + if (fpt && fpt->record && fpt->field) >> >> > + { >> >> > + fprintf (dump_file, " "); >> >> > + fprintf (dump_file, "%s -> ", caller->name ()); >> >> > + print_generic_expr (dump_file, fpt->record, TDF_N= ONE); >> >> > + fprintf (dump_file, "::"); >> >> > + print_generic_expr (dump_file, fpt->field, TDF_NO= NE); >> >> > + fprintf (dump_file, "\n"); >> >> > + } >> >> > + } >> >> > + } >> >> > + } >> >> > + >> >> > + void >> >> > + serialize (struct output_block *ob, lto_symtab_encoder_t encoder= ) >> >> > + { >> >> > + unsigned HOST_WIDE_INT elements =3D 0; >> >> > + >> >> > + /* We iterate over all (cnodes x edges) and store all that h= ave >> >> > + additional information stored. */ >> >> > + >> >> > + lto_symtab_encoder_iterator it; >> >> > + for (it =3D lsei_start_function_in_partition (encoder); !lse= i_end_p (it); >> >> > + lsei_next_function_in_partition (&it)) >> >> > + { >> >> > + cgraph_node *node =3D lsei_cgraph_node (it); >> >> > + if (node->has_gimple_body_p ()) >> >> > + elements++; >> >> > + } >> >> > + >> >> > + /* Write the number of elements. */ >> >> > + streamer_write_uhwi (ob, elements); >> >> > + >> >> > + for (it =3D lsei_start_function_in_partition (encoder); !lse= i_end_p (it); >> >> > + lsei_next_function_in_partition (&it)) >> >> > + { >> >> > + cgraph_node *caller =3D lsei_cgraph_node (it); >> >> > + if (!caller->has_gimple_body_p ()) >> >> > + continue; >> >> > + >> >> > + /* Write caller. */ >> >> > + unsigned HOST_WIDE_INT symid =3D lto_symtab_encoder_encod= e (encoder, >> >> > + = caller); >> >> > + streamer_write_uhwi (ob, symid); >> >> > + >> >> > + for (cgraph_edge *e =3D caller->indirect_calls; e; e =3D = e->next_callee) >> >> > + { >> >> > + function_pointer_type *fpt =3D get (e); >> >> > + if (fpt && fpt->record && fpt->field) >> >> > + { >> >> > + /* Write the function pointer type. */ >> >> > + stream_write_tree_ref (ob, fpt->record); >> >> > + stream_write_tree_ref (ob, fpt->field); >> >> > + } >> >> > + else >> >> > + { >> >> > + stream_write_tree_ref (ob, NULL_TREE); >> >> > + stream_write_tree_ref (ob, NULL_TREE); >> >> > + } >> >> > + } >> >> > + } >> >> > + } >> >> > + >> >> > + void >> >> > + deserialize (lto_input_block &ib, class data_in *data_in, >> >> > + lto_symtab_encoder_t &encoder) >> >> > + { >> >> > + /* Read the number of elements. */ >> >> > + size_t elements =3D streamer_read_uhwi (&ib); >> >> > + >> >> > + for (size_t i =3D 0; i < elements; i++) >> >> > + { >> >> > + /* Read caller. */ >> >> > + unsigned HOST_WIDE_INT symid =3D streamer_read_uhwi (&ib)= ; >> >> > + symtab_node *scaller =3D lto_symtab_encoder_deref (encode= r, symid); >> >> > + cgraph_node *caller =3D dyn_cast (scaller= ); >> >> > + >> >> > + for (cgraph_edge *e =3D caller->indirect_calls; e; e =3D = e->next_callee) >> >> > + { >> >> > + tree record =3D stream_read_tree_ref (&ib, data_in); >> >> > + tree field =3D stream_read_tree_ref (&ib, data_in); >> >> > + if (record =3D=3D NULL_TREE && field =3D=3D NULL_TREE= ) >> >> > + continue; >> >> > + >> >> > + function_pointer_type *fpt =3D get_create (e); >> >> > + fpt->record =3D record; >> >> > + fpt->field =3D field; >> >> > + } >> >> > + } >> >> > + } >> >> > +}; >> >> > + >> >> > +class gimple_walker >> >> > +{ >> >> > +public: >> >> > + gimple_walker () {} >> >> > + >> >> > + void walk (void* data); >> >> > + >> >> > +protected: >> >> > + /* Overload these callbacks. */ >> >> > + virtual void walk_gassign (__attribute__ ((unused)) cgraph_node*= , >> >> > + __attribute__ ((unused)) gassign*, >> >> > + __attribute__ ((unused)) void*) {} >> >> > + virtual void walk_gcall (__attribute__ ((unused)) cgraph_node*, >> >> > + __attribute__ ((unused)) gcall*, >> >> > + __attribute__ ((unused)) void*) {} >> >> > + >> >> > +private: >> >> > + /* Will walk declarations, locals, ssa names, and basic blocks. = */ >> >> > + void _walk_cnode (cgraph_node *cnode, void *data); >> >> > + >> >> > + /* Iterate over all basic blocks in CNODE. */ >> >> > + void _walk_bb (cgraph_node *cnode, basic_block bb, void *data); >> >> > + >> >> > + /* Iterate over all gimple_stmt in BB. */ >> >> > + void _walk_gimple (cgraph_node *cnode, gimple *stmt, void *data)= ; >> >> > +}; >> >> > + >> >> > +void >> >> > +gimple_walker::walk (void *data) >> >> > +{ >> >> > + hash_set fndecls2; >> >> > + cgraph_node *node =3D NULL; >> >> > + >> >> > + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) >> >> > + { >> >> > + node->get_body (); >> >> > + tree decl =3D node->decl; >> >> > + gcc_assert (decl); >> >> > + const bool already_in_set =3D fndecls2.contains (decl); >> >> > + >> >> > + /* I think it is possible for different nodes to point to th= e same >> >> > + declaration. */ >> >> > + if (already_in_set) >> >> > + continue; >> >> > + >> >> > + if (dump_file) >> >> > + dump_function_to_file (node->decl, dump_file, TDF_NONE); >> >> > + >> >> > + _walk_cnode (node, data); >> >> > + >> >> > + /* Add to set of known declarations. */ >> >> > + fndecls2.add (decl); >> >> > + } >> >> > +} >> >> > + >> >> > +/* Walk over all basic blocks in CNODE. */ >> >> > + >> >> > +void >> >> > +gimple_walker::_walk_cnode (cgraph_node *cnode, void *data) >> >> > +{ >> >> > + cnode->get_body (); >> >> > + tree decl =3D cnode->decl; >> >> > + gcc_assert (decl); >> >> > + >> >> > + function *func =3D DECL_STRUCT_FUNCTION (decl); >> >> > + gcc_assert (func); >> >> > + >> >> > + basic_block bb =3D NULL; >> >> > + >> >> > + push_cfun (func); >> >> > + FOR_EACH_BB_FN (bb, func) >> >> > + { >> >> > + _walk_bb (cnode, bb, data); >> >> > + } >> >> > + pop_cfun (); >> >> > +} >> >> > + >> >> > +/* Walk over each gimple statement in BB. */ >> >> > + >> >> > +void >> >> > +gimple_walker::_walk_bb (cgraph_node *cnode, basic_block bb, void = *data) >> >> > +{ >> >> > + gimple_stmt_iterator gsi; >> >> > + for (gsi =3D gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi= )) >> >> > + { >> >> > + gimple *stmt =3D gsi_stmt (gsi); >> >> > + _walk_gimple (cnode, stmt, data); >> >> > + } >> >> > +} >> >> > + >> >> > +/* Switch for different gimple instruction types. */ >> >> > + >> >> > +void >> >> > +gimple_walker::_walk_gimple (cgraph_node *cnode, gimple *stmt, voi= d *data) >> >> > +{ >> >> > + const enum gimple_code code =3D gimple_code (stmt); >> >> > + switch (code) >> >> > + { >> >> > + case GIMPLE_ASSIGN: >> >> > + { >> >> > + gassign *assign =3D dyn_cast (stmt); >> >> > + walk_gassign (cnode, assign, data); >> >> > + break; >> >> > + } >> >> > + case GIMPLE_CALL: >> >> > + { >> >> > + gcall *call =3D dyn_cast (stmt); >> >> > + walk_gcall (cnode, call, data); >> >> > + break; >> >> > + } >> >> > + default: >> >> > + break; >> >> > + } >> >> > +} >> >> > + >> >> > +class gimple_assignment_collector : public gimple_walker >> >> > +{ >> >> > +protected: >> >> > + virtual void walk_gassign (cgraph_node *cnode, gassign *stmt, vo= id *data) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + function_pointer_type_assignments *fpas >> >> > + =3D (function_pointer_type_assignments*) data; >> >> > + >> >> > + tree lhs =3D gimple_assign_lhs (stmt); >> >> > + gcc_assert (lhs); >> >> > + >> >> > + /* We only care about a rhs which is a variable or a constan= t. >> >> > + Therefore, we only need to look at unary or single rhs. *= / >> >> > + const enum gimple_rhs_class gclass =3D gimple_assign_rhs_cla= ss (stmt); >> >> > + if (gclass !=3D GIMPLE_UNARY_RHS >> >> > + && gclass !=3D GIMPLE_SINGLE_RHS) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: RHS class not matching.\n", __= func__); >> >> > + return; >> >> > + } >> >> > + >> >> > + tree rhs =3D gimple_assign_rhs1 (stmt); >> >> > + >> >> > + if (dump_file) >> >> > + { >> >> > + fprintf (dump_file, "%s: Analysing assignment:\n", __func= __); >> >> > + fprintf (dump_file, " Function: %s\n", cnode->name ()); >> >> > + fprintf (dump_file, " LHS: "); >> >> > + print_generic_expr (dump_file, lhs, TDF_NONE); >> >> > + fprintf (dump_file, "\n RHS: "); >> >> > + print_generic_expr (dump_file, rhs, TDF_NONE); >> >> > + fprintf (dump_file, "\n"); >> >> > + } >> >> > + >> >> > + /* We are only interested in function pointers. */ >> >> > + tree rhs_t =3D TREE_TYPE (rhs); >> >> > + tree lhs_t =3D TREE_TYPE (lhs); >> >> > + if (TREE_CODE (rhs_t) !=3D POINTER_TYPE >> >> > + || TREE_CODE (lhs_t) !=3D POINTER_TYPE) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: LHS not pointer type.\n", __fu= nc__); >> >> > + return; >> >> > + } >> >> > + if (TREE_CODE (TREE_TYPE (rhs_t)) !=3D FUNCTION_TYPE >> >> > + || TREE_CODE (TREE_TYPE (lhs_t)) !=3D FUNCTION_TYPE) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: RHS not function type.\n", __f= unc__); >> >> > + return; >> >> > + } >> >> > + >> >> > + /* We only care about function pointers assigned to fields. >> >> > + So we look for COMPONENT_REF. */ >> >> > + const enum tree_code code =3D TREE_CODE (lhs); >> >> > + if (code !=3D COMPONENT_REF) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: LHS not component ref.\n", __f= unc__); >> >> > + return; >> >> > + } >> >> > + >> >> > + tree base =3D TREE_OPERAND (lhs, 0); >> >> > + tree base_t =3D TREE_TYPE (base); >> >> > + >> >> > + /* We either have a record or a pointer to a record. */ >> >> > + if (TREE_CODE (base_t) =3D=3D POINTER_TYPE) >> >> > + base_t =3D TREE_TYPE (base_t); >> >> > + >> >> > + if (TREE_CODE (base_t) !=3D RECORD_TYPE) >> >> > + { >> >> > + if (dump_file) >> >> > + { >> >> > + fprintf (dump_file, "%s: Base type not record type.\n= ", __func__); >> >> > + fprintf (dump_file, "%s: base: ", __func__); >> >> > + print_generic_expr (dump_file, base, TDF_DETAILS); >> >> > + fprintf (dump_file, "%s: base_t: ", __func__); >> >> > + print_generic_expr (dump_file, base_t, TDF_DETAILS); >> >> > + } >> >> > + return; >> >> > + } >> >> > + >> >> > + /* We only care about addr expressions. */ >> >> > + if (TREE_CODE (rhs) !=3D ADDR_EXPR) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: RHS is not addr expr.\n", __fu= nc__); >> >> > + return; >> >> > + } >> >> > + >> >> > + tree possible_decl =3D TREE_OPERAND (rhs, 0); >> >> > + if (TREE_CODE (possible_decl) !=3D FUNCTION_DECL) >> >> > + { >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: RHS addr expr is not a functio= n decl.\n", >> >> > + __func__); >> >> > + return; >> >> > + } >> >> > + >> >> > + tree field =3D TREE_OPERAND (lhs, 1); >> >> > + >> >> > + /* Add record type and field decl to global summary. */ >> >> > + function_pointer_type pair; >> >> > + pair.record =3D base_t; >> >> > + pair.field =3D field; >> >> > + cgraph_node *node =3D cgraph_node::get (possible_decl); >> >> > + >> >> > + /* This is a candidate for optimization. */ >> >> > + if (dump_file) >> >> > + { >> >> > + cgraph_node *orig =3D cgraph_node::get (cfun->decl); >> >> > + fprintf (dump_file, "Candidate found in %s:\n", orig->nam= e ()); >> >> > + print_gimple_stmt (dump_file, stmt, dump_flags); >> >> > + } >> >> > + >> >> > + fpas->add_assignment (pair, node); >> >> > + } >> >> > + >> >> > + virtual void walk_gcall (cgraph_node *cnode, gcall *stmt, void *= data) >> >> > + { >> >> > + (void)cnode; >> >> > + >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + function_pointer_type_assignments *fpas >> >> > + =3D (function_pointer_type_assignments*) data; >> >> > + >> >> > + gcc_assert (stmt); >> >> > + tree lhs =3D gimple_call_lhs (stmt); >> >> > + if (!lhs) >> >> > + return; >> >> > + >> >> > + tree lhs_t =3D TREE_TYPE (lhs); >> >> > + /* We are only interested in function pointers. */ >> >> > + if (TREE_CODE (lhs_t) !=3D POINTER_TYPE) >> >> > + return; >> >> > + if (TREE_CODE (TREE_TYPE (lhs_t)) !=3D FUNCTION_TYPE) >> >> > + return; >> >> > + >> >> > + /* We only care about function pointers assigned to fields. >> >> > + So we look for COMPONENT_REF. */ >> >> > + const enum tree_code code =3D TREE_CODE (lhs); >> >> > + if (code !=3D COMPONENT_REF) >> >> > + return; >> >> > + >> >> > + /* We either have a record or a pointer to a record. */ >> >> > + tree base =3D TREE_OPERAND (lhs, 0); >> >> > + tree base_t =3D TREE_TYPE (base); >> >> > + if (TREE_CODE (base_t) !=3D POINTER_TYPE) >> >> > + return; >> >> > + base_t =3D TREE_TYPE (base_t); >> >> > + if (TREE_CODE (base_t) !=3D RECORD_TYPE) >> >> > + return; >> >> > + if (!TYPE_P (base_t)) >> >> > + return; >> >> > + >> >> > + tree field =3D TREE_OPERAND (lhs, 1); >> >> > + >> >> > + /* Add record type and field decl to global summary. */ >> >> > + function_pointer_type pair; >> >> > + pair.record =3D base_t; >> >> > + pair.field =3D field; >> >> > + >> >> > + /* This is a reason to not optimize this pointer. */ >> >> > + if (dump_file) >> >> > + { >> >> > + cgraph_node *orig =3D cgraph_node::get (cfun->decl); >> >> > + fprintf (dump_file, "Counter-candidate found in %s:\n", o= rig->name ()); >> >> > + print_gimple_stmt (dump_file, stmt, dump_flags); >> >> > + } >> >> > + >> >> > + fpas->add_assignment (pair, NULL); >> >> > + } >> >> > +}; >> >> > + >> >> > +/* Globals (prefixed by '_'). */ >> >> > +static function_pointer_type_assignments *_function_pointer_type_a= ssignments; >> >> > +static indirect_call_summary *_indirect_call_summaries; >> >> > +static struct cgraph_node_hook_list *_cgraph_removal_hook_holder; >> >> > + >> >> > +/* Function updates our global summary. */ >> >> > + >> >> > +static void >> >> > +remove_cgraph_callback (cgraph_node *node, void *data ATTRIBUTE_UN= USED) >> >> > +{ >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: node removal: %s\n", __func__, node->= name ()); >> >> > + _function_pointer_type_assignments->remove (node); >> >> > +} >> >> > + >> >> > +/* Register notification callbacks. */ >> >> > + >> >> > +static void >> >> > +guarded_deref_register_cgraph_hooks (void) >> >> > +{ >> >> > + _cgraph_removal_hook_holder >> >> > + =3D symtab->add_cgraph_removal_hook (&remove_cgraph_callback, = NULL); >> >> > +} >> >> > + >> >> > +/* Unregister notification callbacks. */ >> >> > + >> >> > +static void >> >> > +guarded_deref_unregister_cgraph_hooks (void) >> >> > +{ >> >> > + if (_cgraph_removal_hook_holder) >> >> > + symtab->remove_cgraph_removal_hook (_cgraph_removal_hook_holde= r); >> >> > + _cgraph_removal_hook_holder =3D NULL; >> >> > +} >> >> > + >> >> > +static void >> >> > +guarded_deref_find_indirect (struct cgraph_node *node, >> >> > + indirect_call_summary *ics) >> >> > +{ >> >> > + if (!node || node->inlined_to || !node->has_gimple_body_p ()) >> >> > + return; >> >> > + >> >> > + for (cgraph_edge *e =3D node->indirect_calls; e; e =3D e->next_c= allee) >> >> > + { >> >> > + gimple *stmt =3D e->call_stmt; >> >> > + if (gimple_code (stmt) !=3D GIMPLE_CALL) >> >> > + continue; >> >> > + >> >> > + gcall *call_stmt =3D dyn_cast (stmt); >> >> > + tree target =3D gimple_call_fn (call_stmt); >> >> > + if (!target) >> >> > + continue; >> >> > + >> >> > + if (TREE_CODE (target) !=3D SSA_NAME) >> >> > + continue; >> >> > + >> >> > + gimple *def =3D SSA_NAME_DEF_STMT (target); >> >> > + >> >> > + if (!gimple_assign_load_p (def)) >> >> > + continue; >> >> > + >> >> > + const enum gimple_rhs_class gclass =3D gimple_assign_rhs_cla= ss (def); >> >> > + const bool valid =3D gclass =3D=3D GIMPLE_UNARY_RHS || gclas= s =3D=3D GIMPLE_SINGLE_RHS; >> >> > + if (!valid) >> >> > + continue; >> >> > + >> >> > + tree rhs =3D gimple_assign_rhs1 (def); >> >> > + const enum tree_code code =3D TREE_CODE (rhs); >> >> > + bool is_load =3D COMPONENT_REF =3D=3D code; >> >> > + if (!is_load) >> >> > + continue; >> >> > + >> >> > + tree base =3D TREE_OPERAND (rhs, 0); >> >> > + tree field =3D TREE_OPERAND (rhs, 1); >> >> > + if (RECORD_TYPE !=3D TREE_CODE (TREE_TYPE (base))) >> >> > + continue; >> >> > + >> >> > + function_pointer_type *fpt =3D ics->get_create (e); >> >> > + fpt->record =3D TREE_TYPE (base); >> >> > + fpt->field =3D field; >> >> > + } >> >> > +} >> >> > + >> >> > +static void >> >> > +guarded_deref_generate_summary (void) >> >> > +{ >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + /* Allocate globals. */ >> >> > + _function_pointer_type_assignments =3D new function_pointer_type= _assignments; >> >> > + _indirect_call_summaries =3D new indirect_call_summary (symtab); >> >> > + >> >> > + /* First collect all function pointer assignments. */ >> >> > + gimple_assignment_collector collector; >> >> > + collector.walk (_function_pointer_type_assignments); >> >> > + >> >> > + /* Now collect all indirect calls. */ >> >> > + cgraph_node *cnode =3D NULL; >> >> > + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cnode) >> >> > + { >> >> > + guarded_deref_find_indirect (cnode, _indirect_call_summaries= ); >> >> > + } >> >> > + >> >> > + /* Print collected information. */ >> >> > + _function_pointer_type_assignments->print (); >> >> > + _indirect_call_summaries-> print (); >> >> > + >> >> > + /* Register hooks for cgraph changes in other passes. */ >> >> > + guarded_deref_register_cgraph_hooks (); >> >> > +} >> >> > + >> >> > +static void >> >> > +guarded_deref_write_summary (void) >> >> > +{ >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + /* Only run if we are in a sane state. */ >> >> > + if (!_function_pointer_type_assignments || !_indirect_call_summa= ries) >> >> > + return; >> >> > + >> >> > + /* Print collected information. */ >> >> > + _function_pointer_type_assignments->print (); >> >> > + _indirect_call_summaries-> print (); >> >> > + >> >> > + /* Unregister cgraph change hooks. */ >> >> > + guarded_deref_unregister_cgraph_hooks (); >> >> > + >> >> > + /* Create an output block to write out information into. */ >> >> > + struct output_block *ob =3D create_output_block (LTO_section_ipa= _guarded_deref); >> >> > + >> >> > + /* Get the cgraph_node encoder. */ >> >> > + lto_symtab_encoder_t encoder =3D ob->decl_state->symtab_node_enc= oder; >> >> > + >> >> > + /* Write collected function pointer assignments to the OB. */ >> >> > + _function_pointer_type_assignments->serialize (ob, encoder); >> >> > + >> >> > + /* Write edge summaries. */ >> >> > + _indirect_call_summaries->serialize (ob, encoder); >> >> > + >> >> > + /* Delete the information in memory. */ >> >> > + delete _function_pointer_type_assignments; >> >> > + _function_pointer_type_assignments =3D NULL; >> >> > + delete _indirect_call_summaries; >> >> > + _indirect_call_summaries =3D NULL; >> >> > + >> >> > + /* Write the contents of the output block into the instruction s= tream. */ >> >> > + produce_asm (ob, NULL); >> >> > + >> >> > + /* Now destroy the output block. */ >> >> > + destroy_output_block (ob); >> >> > +} >> >> > + >> >> > +static void >> >> > +guarded_deref_read_summary (void) >> >> > +{ >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + if (_indirect_call_summaries || _function_pointer_type_assignmen= ts) >> >> > + return; >> >> > + >> >> > + /* Allocate globals. */ >> >> > + _indirect_call_summaries =3D new indirect_call_summary (symtab); >> >> > + _function_pointer_type_assignments =3D new function_pointer_type= _assignments; >> >> > + >> >> > + struct lto_file_decl_data **file_data_vec =3D lto_get_file_decl_= data (); >> >> > + struct lto_file_decl_data *file_data; >> >> > + unsigned int j =3D 0; >> >> > + while ((file_data =3D file_data_vec[j++])) >> >> > + { >> >> > + size_t len; >> >> > + const char *data =3D lto_get_summary_section_data (file_data= , >> >> > + LTO_section_= ipa_guarded_deref, >> >> > + &len); >> >> > + if (!data) >> >> > + continue; >> >> > + >> >> > + const struct lto_function_header *header >> >> > + =3D (const struct lto_function_header*) data; >> >> > + >> >> > + const int cfg_offset =3D sizeof (*header); >> >> > + const int main_offset =3D cfg_offset + header->cfg_size; >> >> > + const int string_offset =3D main_offset + header->main_size; >> >> > + class data_in *data_in; >> >> > + >> >> > + lto_input_block ib ((const char *) data + main_offset, >> >> > + header->main_size, file_data->mode_table)= ; >> >> > + data_in =3D lto_data_in_create (file_data, >> >> > + (const char *) data + string_of= fset, >> >> > + header->string_size, vNULL); >> >> > + >> >> > + lto_symtab_encoder_t encoder =3D file_data->symtab_node_enco= der; >> >> > + >> >> > + /* Read collected function pointer assignments from LTO stre= am. */ >> >> > + _function_pointer_type_assignments->deserialize (ib, data_in= , encoder); >> >> > + >> >> > + /* Read collected indirect call summary from LTO stream. *= / >> >> > + _indirect_call_summaries->deserialize (ib, data_in, encoder)= ; >> >> > + >> >> > + lto_free_section_data (file_data, LTO_section_ipa_guarded_de= ref, NULL, >> >> > + data, len); >> >> > + lto_data_in_delete (data_in); >> >> > + } >> >> > + >> >> > + /* Print collected information. */ >> >> > + _function_pointer_type_assignments->print (); >> >> > + _indirect_call_summaries-> print (); >> >> > + >> >> > + /* Register hooks for cgraph changes in other passes. */ >> >> > + guarded_deref_register_cgraph_hooks (); >> >> > +} >> >> > + >> >> > +static unsigned int >> >> > +guarded_deref_execute (void) >> >> > +{ >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Entering.\n", __func__); >> >> > + >> >> > + if (!_function_pointer_type_assignments >> >> > + || !_indirect_call_summaries) >> >> > + return 0; >> >> > + >> >> > + /* Unregister cgraph change hooks. */ >> >> > + guarded_deref_unregister_cgraph_hooks (); >> >> > + >> >> > + /* Print collected information. */ >> >> > + _function_pointer_type_assignments->print (); >> >> > + _indirect_call_summaries-> print (); >> >> > + >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Starting propagation.\n", __func__); >> >> > + >> >> > + cgraph_node *cnode =3D NULL; >> >> > + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cnode) >> >> > + { >> >> > + if (cnode->inlined_to) >> >> > + continue; >> >> > + >> >> > + for (cgraph_edge *e =3D cnode->indirect_calls; e; e =3D e->n= ext_callee) >> >> > + { >> >> > + /* Get the function pointer type for the edge (if any). = */ >> >> > + function_pointer_type *fpt =3D _indirect_call_summaries->= get (e); >> >> > + if (!fpt || !fpt->record || !fpt->field) >> >> > + continue; >> >> > + >> >> > + if (dump_file) >> >> > + { >> >> > + fprintf (dump_file, "looking for...:"); >> >> > + print_generic_expr (dump_file, fpt->record, TDF_NONE)= ; >> >> > + fprintf (dump_file, " "); >> >> > + print_generic_expr (dump_file, fpt->field, TDF_NONE); >> >> > + fprintf (dump_file, "\n"); >> >> > + } >> >> > + >> >> > + /* Now get the call target (if any). */ >> >> > + cgraph_node *target =3D _function_pointer_type_assignment= s->get_target (*fpt); >> >> > + if (!target || !target->decl) >> >> > + continue; >> >> > + >> >> > + if (dump_file) >> >> > + { >> >> > + fprintf (dump_file, >> >> > + "Replacing indirect call in %s by " >> >> > + "speculative direct call to %s\n", >> >> > + e->caller->name (), target->name ()); >> >> > + } >> >> > + >> >> > + /* Convert the indirect call to a direct (speculative) ca= ll. */ >> >> > + ipa_make_edge_direct_to_target (e, target->decl, true); >> >> > + >> >> > + /* Update the function summaries. */ >> >> > + ipa_update_overall_fn_summary (cnode); >> >> > + } >> >> > + } >> >> > + >> >> > + if (dump_file) >> >> > + fprintf (dump_file, "%s: Finished propagation.\n", __func__); >> >> > + >> >> > + return 0; >> >> > +} >> >> > + >> >> > +namespace { >> >> > + >> >> > +const pass_data pass_data_ipa_guarded_deref =3D >> >> > +{ >> >> > + IPA_PASS, /* type */ >> >> > + "guarded-deref", /* name */ >> >> > + OPTGROUP_NONE, /* optinfo_flags */ >> >> > + TV_IPA_GUARDED_DEREF, /* tv_id */ >> >> > + 0, /* properties_required */ >> >> > + 0, /* properties_provided */ >> >> > + 0, /* properties_destroyed */ >> >> > + 0, /* todo_flags_start */ >> >> > + 0, /* todo_flags_finish */ >> >> > +}; >> >> > + >> >> > +class pass_ipa_guarded_deref : public ipa_opt_pass_d >> >> > +{ >> >> > +public: >> >> > + pass_ipa_guarded_deref (gcc::context *ctxt) >> >> > + : ipa_opt_pass_d (pass_data_ipa_guarded_deref, ctxt, >> >> > + guarded_deref_generate_summary, /* generate_s= ummary */ >> >> > + guarded_deref_write_summary, /* write_summary= */ >> >> > + guarded_deref_read_summary, /* read_summary *= / >> >> > + NULL, /* write_optimization_summary */ >> >> > + NULL, /* read_optimization_summary */ >> >> > + NULL, /* stmt_fixup */ >> >> > + 0, /* function_transform_todo_flags_start */ >> >> > + NULL, /* function_transform */ >> >> > + NULL) /* variable_transform */ >> >> > + {} >> >> > + >> >> > + /* opt_pass methods: */ >> >> > + bool gate (function *) final override >> >> > + { >> >> > + return ((in_lto_p || flag_lto) && flag_ipa_guarded_deref); >> >> > + } >> >> > + >> >> > + unsigned int execute (function *) final override >> >> > + { >> >> > + return guarded_deref_execute (); >> >> > + } >> >> > + >> >> > +}; // class pass_ipa_guarded_deref >> >> > + >> >> > +} // anon namespace >> >> > + >> >> > +ipa_opt_pass_d * >> >> > +make_pass_ipa_guarded_deref (gcc::context *ctxt) >> >> > +{ >> >> > + return new pass_ipa_guarded_deref (ctxt); >> >> > +} >> >> > diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc >> >> > index ba87c727670..22f6b66a291 100644 >> >> > --- a/gcc/lto-section-in.cc >> >> > +++ b/gcc/lto-section-in.cc >> >> > @@ -57,6 +57,7 @@ const char *lto_section_name[LTO_N_SECTION_TYPES]= =3D >> >> > "ipa_sra", >> >> > "odr_types", >> >> > "ipa_modref", >> >> > + "ipa_guarded_deref", >> >> > }; >> >> > >> >> > /* Hooks so that the ipa passes can call into the lto front end to= get >> >> > diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h >> >> > index 2e3abd97959..744e8738376 100644 >> >> > --- a/gcc/lto-streamer.h >> >> > +++ b/gcc/lto-streamer.h >> >> > @@ -229,6 +229,7 @@ enum lto_section_type >> >> > LTO_section_ipa_sra, >> >> > LTO_section_odr_types, >> >> > LTO_section_ipa_modref, >> >> > + LTO_section_ipa_guarded_deref, >> >> > LTO_N_SECTION_TYPES /* Must be last. */ >> >> > }; >> >> > >> >> > diff --git a/gcc/passes.def b/gcc/passes.def >> >> > index 193b5794749..60c029e0515 100644 >> >> > --- a/gcc/passes.def >> >> > +++ b/gcc/passes.def >> >> > @@ -154,6 +154,7 @@ along with GCC; see the file COPYING3. If not = see >> >> > NEXT_PASS (pass_ipa_whole_program_visibility); >> >> > NEXT_PASS (pass_ipa_profile); >> >> > NEXT_PASS (pass_ipa_icf); >> >> > + NEXT_PASS (pass_ipa_guarded_deref); >> >> > NEXT_PASS (pass_ipa_devirt); >> >> > NEXT_PASS (pass_ipa_cp); >> >> > NEXT_PASS (pass_ipa_sra); >> >> > diff --git a/gcc/timevar.def b/gcc/timevar.def >> >> > index 63d9b005180..38fd7798768 100644 >> >> > --- a/gcc/timevar.def >> >> > +++ b/gcc/timevar.def >> >> > @@ -72,6 +72,7 @@ DEFTIMEVAR (TV_CGRAPH_FUNC_EXPANSION , "callgraph= functions expansion") >> >> > DEFTIMEVAR (TV_CGRAPH_IPA_PASSES , "callgraph ipa passes") >> >> > DEFTIMEVAR (TV_IPA_ODR , "ipa ODR types") >> >> > DEFTIMEVAR (TV_IPA_FNSUMMARY , "ipa function summary") >> >> > +DEFTIMEVAR (TV_IPA_GUARDED_DEREF , "ipa guarded deref") >> >> > DEFTIMEVAR (TV_IPA_UNREACHABLE , "ipa dead code removal") >> >> > DEFTIMEVAR (TV_IPA_INHERITANCE , "ipa inheritance graph") >> >> > DEFTIMEVAR (TV_IPA_VIRTUAL_CALL , "ipa virtual call target") >> >> > diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h >> >> > index 8480d41384b..6cc200bd83e 100644 >> >> > --- a/gcc/tree-pass.h >> >> > +++ b/gcc/tree-pass.h >> >> > @@ -525,6 +525,7 @@ extern ipa_opt_pass_d *make_pass_ipa_inline (gc= c::context *ctxt); >> >> > extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::con= text *ctxt); >> >> > extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary (gcc::co= ntext *ctxt); >> >> > extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt); >> >> > +extern ipa_opt_pass_d *make_pass_ipa_guarded_deref (gcc::context *= ctxt); >> >> > extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt); >> >> > extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt); >> >> > extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt); >> >> > -- >> >> > 2.38.1 >> >> >