On Fri, Jul 8, 2016 at 12:16 AM, Richard Biener <rguent...@suse.de> wrote: > > This is a final candidate patch to add code-hoisting to GIMPLE. > > I've already committed several patches fixing fallout and the following > one adds -fno-code-hoisting (I renamed the option) to a few testcases. > I filed PRs for the cases code-hoisting exposes missed optimization > opportunities in passes that I couldn't quickly fix (I fixed path > splitting and loop distribution but failed to grok SLSR). > > Bootstrapped and tested on x86_64-unknown-linux-gnu. > > I put the patch on the czerny tester for the weekend runs (x86_64 as > well). > > Testing on other archs and comments are of course appreciated, if nothing > unusual happens I plan to commit this on Monday.
I tested this on top of GCC 6 on aarch64-linux-gnu and there was no degradation for SPEC CPU INT 2006 on ThunderX. Thanks, Andrew Pinski > > Richard. > > 2016-07-08 Steven Bosscher <ste...@gcc.gnu.org> > Richard Biener <rguent...@suse.de> > > PR tree-optimization/23286 > PR tree-optimization/70159 > * doc/invoke.texi: Document -fcode-hoisting. > * common.opt (fcode-hoisting): New flag. > * opts.c (default_options_table): Enable -fcode-hoisting at -O2+. > * tree-ssa-pre.c (pre_stats): Add hoist_insert. > (do_regular_insertion): Rename to ... > (do_pre_regular_insertion): ... this and amend general comments > on insertion strathegy. > (do_partial_partial_insertion): Rename to ... > (do_pre_partial_partial_insertion): ... this. > (do_hoist_insertion): New function. > (insert_aux): Take flags on whether to do PRE and/or hoist insertion > and call do_hoist_insertion properly. > (insert): Adjust. > (pass_pre::gate): Enable also if -fcode-hoisting is enabled. > (pass_pre::execute): Register hoist_insert stats. > > * gcc.dg/tree-ssa/ssa-pre-11.c: Disable code hosting. > * gcc.dg/tree-ssa/ssa-pre-27.c: Likewise. > * gcc.dg/tree-ssa/ssa-pre-28.c: Likewise. > * gcc.dg/tree-ssa/ssa-pre-2.c: Likewise. > * gcc.dg/tree-ssa/pr35286.c: Likewise. > * gcc.dg/tree-ssa/pr35287.c: Likewise. > * gcc.dg/hoist-register-pressure-1.c: Likewise. > * gcc.dg/hoist-register-pressure-2.c: Likewise. > * gcc.dg/hoist-register-pressure-3.c: Likewise. > * gcc.dg/pr51879-12.c: Likewise. > * gcc.dg/strlenopt-9.c: Likewise. > * gcc.dg/tree-ssa/pr47392.c: Likewise. > * gcc.dg/tree-ssa/pr68619-4.c: Likewise. > * gcc.dg/tree-ssa/split-path-5.c: Likewise. > * gcc.dg/tree-ssa/slsr-35.c: Likewise. > * gcc.dg/tree-ssa/slsr-36.c: Likewise. > * gcc.dg/tree-ssa/loadpre3.c: Adjust so hosting doesn't apply. > * gcc.dg/tree-ssa/pr43491.c: Scan optimized dump for desired result. > * gcc.dg/tree-ssa/ssa-pre-31.c: Adjust expected outcome for hoisting. > * gcc.dg/tree-ssa/ssa-hoist-1.c: New testcase. > * gcc.dg/tree-ssa/ssa-hoist-2.c: New testcase. > * gcc.dg/tree-ssa/ssa-hoist-3.c: New testcase. > * gcc.dg/tree-ssa/ssa-hoist-4.c: New testcase. > * gcc.dg/tree-ssa/ssa-hoist-5.c: New testcase. > * gcc.dg/tree-ssa/ssa-hoist-6.c: New testcase. > * gfortran.dg/pr43984.f90: Adjust expected outcome. > > Index: gcc/doc/invoke.texi > =================================================================== > *** gcc/doc/invoke.texi.orig 2016-07-07 14:45:27.156657281 +0200 > --- gcc/doc/invoke.texi 2016-07-07 14:45:45.460875808 +0200 > *************** Objective-C and Objective-C++ Dialects}. > *** 404,410 **** > -fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp @gol > -ftree-builtin-call-dce -ftree-ccp -ftree-ch @gol > -ftree-coalesce-vars -ftree-copy-prop -ftree-dce -ftree-dominator-opts @gol > ! -ftree-dse -ftree-forwprop -ftree-fre -ftree-loop-if-convert @gol > -ftree-loop-if-convert-stores -ftree-loop-im @gol > -ftree-phiprop -ftree-loop-distribution -ftree-loop-distribute-patterns @gol > -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize @gol > --- 404,410 ---- > -fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp @gol > -ftree-builtin-call-dce -ftree-ccp -ftree-ch @gol > -ftree-coalesce-vars -ftree-copy-prop -ftree-dce -ftree-dominator-opts @gol > ! -ftree-dse -ftree-forwprop -ftree-fre -fcode-hoisting > -ftree-loop-if-convert @gol > -ftree-loop-if-convert-stores -ftree-loop-im @gol > -ftree-phiprop -ftree-loop-distribution -ftree-loop-distribute-patterns @gol > -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize @gol > *************** also turns on the following optimization > *** 6372,6377 **** > --- 6372,6378 ---- > -fstrict-aliasing -fstrict-overflow @gol > -ftree-builtin-call-dce @gol > -ftree-switch-conversion -ftree-tail-merge @gol > + -fcode-hoisting @gol > -ftree-pre @gol > -ftree-vrp @gol > -fipa-ra} > *************** and the @option{large-stack-frame-growth > *** 7265,7270 **** > --- 7266,7279 ---- > Perform reassociation on trees. This flag is enabled by default > at @option{-O} and higher. > > + @item -fcode-hoisting > + @opindex fcode-hoisting > + Perform code hoisting. Code hoisting tries to move the > + evaluation of expressions executed on all paths to the function exit > + as early as possible. This is especially useful as a code size > + optimization, but it often helps for code speed as well. > + This flag is enabled by defailt at @option{-O2} and higher. > + > @item -ftree-pre > @opindex ftree-pre > Perform partial redundancy elimination (PRE) on trees. This flag is > *************** Dump each function after STORE-CCP@. Th > *** 12222,12229 **** > > @item pre > @opindex fdump-tree-pre > ! Dump trees after partial redundancy elimination. The file name is made > ! by appending @file{.pre} to the source file name. > > @item fre > @opindex fdump-tree-fre > --- 12231,12238 ---- > > @item pre > @opindex fdump-tree-pre > ! Dump trees after partial redundancy elimination and/or code hoisting. > ! The file name is made by appending @file{.pre} to the source file name. > > @item fre > @opindex fdump-tree-fre > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-11.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-11.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-11.c 2016-07-07 14:45:45.520876524 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > double cos (double); > double f(double a) > { > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-pre-stats" } */ > double cos (double); > double f(double a) > { > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-2.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-2.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-2.c 2016-07-07 14:45:45.524876572 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > int motion_test1(int data, int data_0, int data_3, int v) > { > int i; > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-pre-stats" } */ > int motion_test1(int data, int data_0, int data_3, int v) > { > int i; > Index: gcc/testsuite/gcc.dg/tree-ssa/pr35286.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/pr35286.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/pr35286.c 2016-07-07 14:45:45.532876667 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > int g2; > struct A { > int a; int b; > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-pre-stats" } */ > int g2; > struct A { > int a; int b; > Index: gcc/testsuite/gcc.dg/tree-ssa/pr35287.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/pr35287.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/pr35287.c 2016-07-07 14:45:45.532876667 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > int *gp; > int foo(int p) > { > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-pre-stats" } */ > int *gp; > int foo(int p) > { > Index: gcc/testsuite/gcc.dg/tree-ssa/loadpre3.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/loadpre3.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/loadpre3.c 2016-07-07 14:45:45.532876667 > +0200 > *************** > *** 1,5 **** > --- 1,8 ---- > /* { dg-do compile } */ > /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > + > + extern void spoil (void); > + > int foo(int **a,int argc) > { > int b; > *************** int foo(int **a,int argc) > *** 11,17 **** > } > else > { > ! > } > /* Should be able to eliminate one of the *(*a)'s along the if path > by pushing it into the else path. We will also eliminate > --- 14,21 ---- > } > else > { > ! /* Spoil *a and *(*a) to avoid hoisting it before the "if (...)". */ > ! spoil (); > } > /* Should be able to eliminate one of the *(*a)'s along the if path > by pushing it into the else path. We will also eliminate > Index: gcc/opts.c > =================================================================== > *** gcc/opts.c.orig 2016-07-07 09:46:02.655811772 +0200 > --- gcc/opts.c 2016-07-07 14:45:45.544876811 +0200 > *************** static const struct default_options defa > *** 500,505 **** > --- 500,506 ---- > REORDER_BLOCKS_ALGORITHM_STC }, > { OPT_LEVELS_2_PLUS, OPT_freorder_functions, NULL, 1 }, > { OPT_LEVELS_2_PLUS, OPT_ftree_vrp, NULL, 1 }, > + { OPT_LEVELS_2_PLUS, OPT_fcode_hoisting, NULL, 1 }, > { OPT_LEVELS_2_PLUS, OPT_ftree_pre, NULL, 1 }, > { OPT_LEVELS_2_PLUS, OPT_ftree_switch_conversion, NULL, 1 }, > { OPT_LEVELS_2_PLUS, OPT_fipa_cp, NULL, 1 }, > Index: gcc/tree-ssa-pre.c > =================================================================== > *** gcc/tree-ssa-pre.c.orig 2016-07-07 09:46:02.655811772 +0200 > --- gcc/tree-ssa-pre.c 2016-07-07 14:45:45.552876906 +0200 > *************** > *** 1,4 **** > ! /* SSA-PRE for trees. > Copyright (C) 2001-2016 Free Software Foundation, Inc. > Contributed by Daniel Berlin <d...@dberlin.org> and Steven Bosscher > <stev...@suse.de> > --- 1,4 ---- > ! /* Full and partial redundancy elimination and code hoisting on SSA GIMPLE. > Copyright (C) 2001-2016 Free Software Foundation, Inc. > Contributed by Daniel Berlin <d...@dberlin.org> and Steven Bosscher > <stev...@suse.de> > *************** along with GCC; see the file COPYING3. > *** 55,65 **** > --- 55,86 ---- > #include "langhooks.h" > #include "alias.h" > > + /* Even though this file is called tree-ssa-pre.c, we actually > + implement a bit more than just PRE here. All of them piggy-back > + on GVN which is implemented in tree-ssa-sccvn.c. > + > + 1. Full Redundancy Elimination (FRE) > + This is the elimination phase of GVN. > + > + 2. Partial Redundancy Elimination (PRE) > + This is adds computation of AVAIL_OUT and ANTIC_IN and > + doing expression insertion to form GVN-PRE. > + > + 3. Code hoisting > + This optimization uses the ANTIC_IN sets computed for PRE > + to move expressions further up than PRE would do, to make > + multiple computations of the same value fully redundant. > + This pass is explained below (after the explanation of the > + basic algorithm for PRE). > + */ > + > /* TODO: > > 1. Avail sets can be shared by making an avail_find_leader that > walks up the dominator tree and looks in those avail sets. > This might affect code optimality, it's unclear right now. > + Currently the AVAIL_OUT sets are the remaining quadraticness in > + memory of GVN-PRE. > 2. Strength reduction can be performed by anticipating expressions > we can repair later on. > 3. We can do back-substitution or smarter value numbering to catch > *************** along with GCC; see the file COPYING3. > *** 71,77 **** > represent the actual statement containing the expressions we care about, > and we cache the value number by putting it in the expression. */ > > ! /* Basic algorithm > > First we walk the statements to generate the AVAIL sets, the > EXP_GEN sets, and the tmp_gen sets. EXP_GEN sets represent the > --- 92,98 ---- > represent the actual statement containing the expressions we care about, > and we cache the value number by putting it in the expression. */ > > ! /* Basic algorithm for Partial Redundancy Elimination: > > First we walk the statements to generate the AVAIL sets, the > EXP_GEN sets, and the tmp_gen sets. EXP_GEN sets represent the > *************** along with GCC; see the file COPYING3. > *** 111,127 **** > In order to make it fully redundant, we insert the expression into > the predecessors where it is not available, but is ANTIC. > > For the partial anticipation case, we only perform insertion if it > is partially anticipated in some block, and fully available in all > of the predecessors. > > ! insert/insert_aux/do_regular_insertion/do_partial_partial_insertion > ! performs these steps. > > Fourth, we eliminate fully redundant expressions. > This is a simple statement walk that replaces redundant > calculations with the now available values. */ > > /* Representations of value numbers: > > Value numbers are represented by a representative SSA_NAME. We > --- 132,206 ---- > In order to make it fully redundant, we insert the expression into > the predecessors where it is not available, but is ANTIC. > > + When optimizing for size, we only eliminate the partial redundancy > + if we need to insert in only one predecessor. This avoids almost > + completely the code size increase that PRE usually causes. > + > For the partial anticipation case, we only perform insertion if it > is partially anticipated in some block, and fully available in all > of the predecessors. > > ! do_pre_regular_insertion/do_pre_partial_partial_insertion > ! performs these steps, driven by insert/insert_aux. > > Fourth, we eliminate fully redundant expressions. > This is a simple statement walk that replaces redundant > calculations with the now available values. */ > > + /* Basic algorithm for Code Hoisting: > + > + Code hoisting is: Moving value computations up in the control flow > + graph to make multiple copies redundant. Typically this is a size > + optimization, but there are cases where it also is helpful for speed. > + > + A simple code hoisting algorithm is implemented that piggy-backs on > + the PRE infrastructure. For code hoisting, we have to know ANTIC_OUT > + which is effectively ANTIC_IN - AVAIL_OUT. The latter two have to be > + computed for PRE, and we can use them to perform a limited version of > + code hoisting, too. > + > + For the purpose of this implementation, a value is hoistable to a basic > + block B if the following properties are met: > + > + 1. The value is in ANTIC_IN(B) -- the value will be computed on all > + paths from B to function exit and it can be computed in B); > + > + 2. The value is not in AVAIL_OUT(B) -- there would be no need to > + compute the value again and make it available twice; > + > + 3. All successors of B are dominated by B -- makes sure that inserting > + a computation of the value in B will make the remaining > + computations fully redundant; > + > + 4. At least one successor has the value in AVAIL_OUT -- to avoid > + hoisting values up too far; > + > + 5. There are at least two successors of B -- hoisting in straight > + line code is pointless. > + > + The third condition is not strictly necessary, but it would complicate > + the hoisting pass a lot. In fact, I don't know of any code hoisting > + algorithm that does not have this requirement. Fortunately, experiments > + have show that most candidate hoistable values are in regions that meet > + this condition (e.g. diamond-shape regions). > + > + The forth condition is necessary to avoid hoisting things up too far > + away from the uses of the value. Nothing else limits the algorithm > + from hoisting everything up as far as ANTIC_IN allows. Experiments > + with SPEC and CSiBE have shown that hoisting up too far results in more > + spilling, less benefits for code size, and worse benchmark scores. > + Fortunately, in practice most of the interesting hoisting opportunities > + are caught despite this limitation. > + > + For hoistable values that meet all conditions, expressions are inserted > + to make the calculation of the hoistable value fully redundant. We > + perform code hoisting insertions after each round of PRE insertions, > + because code hoisting never exposes new PRE opportunities, but PRE can > + create new code hoisting opportunities. > + > + The code hoisting algorithm is implemented in do_hoist_insert, driven > + by insert/insert_aux. */ > + > /* Representations of value numbers: > > Value numbers are represented by a representative SSA_NAME. We > *************** static struct > *** 446,451 **** > --- 525,533 ---- > /* The number of inserts found due to partial anticipation */ > int pa_insert; > > + /* The number of inserts made for code hoisting. */ > + int hoist_insert; > + > /* The number of new PHI nodes added by PRE. */ > int phis; > } pre_stats; > *************** static pre_expr bitmap_find_leader (bitm > *** 455,460 **** > --- 537,543 ---- > static void bitmap_value_insert_into_set (bitmap_set_t, pre_expr); > static void bitmap_value_replace_in_set (bitmap_set_t, pre_expr); > static void bitmap_set_copy (bitmap_set_t, bitmap_set_t); > + static void bitmap_set_and (bitmap_set_t, bitmap_set_t); > static bool bitmap_set_contains_value (bitmap_set_t, unsigned int); > static void bitmap_insert_into_set (bitmap_set_t, pre_expr); > static void bitmap_insert_into_set_1 (bitmap_set_t, pre_expr, > *************** insert_into_preds_of_block (basic_block > *** 3104,3110 **** > > > > ! /* Perform insertion of partially redundant values. > For BLOCK, do the following: > 1. Propagate the NEW_SETS of the dominator into the current block. > If the block has multiple predecessors, > --- 3187,3193 ---- > > > > ! /* Perform insertion of partially redundant or hoistable values. > For BLOCK, do the following: > 1. Propagate the NEW_SETS of the dominator into the current block. > If the block has multiple predecessors, > *************** insert_into_preds_of_block (basic_block > *** 3115,3129 **** > 2c. Insert a new PHI merging the values of the predecessors. > 2d. Insert the new PHI, and the new expressions, into the > NEW_SETS set. > ! 3. Recursively call ourselves on the dominator children of BLOCK. > ! > ! Steps 1, 2a, and 3 are done by insert_aux. 2b, 2c and 2d are done by > ! do_regular_insertion and do_partial_insertion. > ! > */ > > static bool > ! do_regular_insertion (basic_block block, basic_block dom) > { > bool new_stuff = false; > vec<pre_expr> exprs; > --- 3198,3217 ---- > 2c. Insert a new PHI merging the values of the predecessors. > 2d. Insert the new PHI, and the new expressions, into the > NEW_SETS set. > ! If the block has multiple successors, > ! 3a. Iterate over the ANTIC values for the block to see if > ! any of them are good candidates for hoisting. > ! 3b. If so, insert expressions computing the values in BLOCK, > ! and add the new expressions into the NEW_SETS set. > ! 4. Recursively call ourselves on the dominator children of BLOCK. > ! > ! Steps 1, 2a, and 4 are done by insert_aux. 2b, 2c and 2d are done by > ! do_pre_regular_insertion and do_partial_insertion. 3a and 3b are > ! done in do_hoist_insertion. > */ > > static bool > ! do_pre_regular_insertion (basic_block block, basic_block dom) > { > bool new_stuff = false; > vec<pre_expr> exprs; > *************** do_regular_insertion (basic_block block, > *** 3292,3300 **** > In this case, we know that putting it earlier will enable us to > remove the later computation. */ > > - > static bool > ! do_partial_partial_insertion (basic_block block, basic_block dom) > { > bool new_stuff = false; > vec<pre_expr> exprs; > --- 3380,3387 ---- > In this case, we know that putting it earlier will enable us to > remove the later computation. */ > > static bool > ! do_pre_partial_partial_insertion (basic_block block, basic_block dom) > { > bool new_stuff = false; > vec<pre_expr> exprs; > *************** do_partial_partial_insertion (basic_bloc > *** 3423,3430 **** > return new_stuff; > } > > static bool > ! insert_aux (basic_block block) > { > basic_block son; > bool new_stuff = false; > --- 3510,3647 ---- > return new_stuff; > } > > + /* Insert expressions in BLOCK to compute hoistable values up. > + Return TRUE if something was inserted, otherwise return FALSE. > + The caller has to make sure that BLOCK has at least two successors. */ > + > static bool > ! do_hoist_insertion (basic_block block) > ! { > ! edge e; > ! edge_iterator ei; > ! bool new_stuff = false; > ! unsigned i; > ! gimple_stmt_iterator last; > ! > ! /* At least two successors, or else... */ > ! gcc_assert (EDGE_COUNT (block->succs) >= 2); > ! > ! /* Check that all successors of BLOCK are dominated by block. > ! We could use dominated_by_p() for this, but actually there is a much > ! quicker check: any successor that is dominated by BLOCK can't have > ! more than one predecessor edge. */ > ! FOR_EACH_EDGE (e, ei, block->succs) > ! if (! single_pred_p (e->dest)) > ! return false; > ! > ! /* Determine the insertion point. If we cannot safely insert before > ! the last stmt if we'd have to, bail out. */ > ! last = gsi_last_bb (block); > ! if (!gsi_end_p (last) > ! && !is_ctrl_stmt (gsi_stmt (last)) > ! && stmt_ends_bb_p (gsi_stmt (last))) > ! return false; > ! > ! /* Compute the set of hoistable expressions from ANTIC_IN. First compute > ! hoistable values. */ > ! bitmap_set hoistable_set; > ! > ! /* A hoistable value must be in ANTIC_IN(block) > ! but not in AVAIL_OUT(BLOCK). */ > ! bitmap_initialize (&hoistable_set.values, &grand_bitmap_obstack); > ! bitmap_and_compl (&hoistable_set.values, > ! &ANTIC_IN (block)->values, &AVAIL_OUT (block)->values); > ! > ! /* Short-cut for a common case: hoistable_set is empty. */ > ! if (bitmap_empty_p (&hoistable_set.values)) > ! return false; > ! > ! /* Compute which of the hoistable values is in AVAIL_OUT of > ! at least one of the successors of BLOCK. */ > ! bitmap_head availout_in_some; > ! bitmap_initialize (&availout_in_some, &grand_bitmap_obstack); > ! FOR_EACH_EDGE (e, ei, block->succs) > ! /* Do not consider expressions solely because their availability > ! on loop exits. They'd be ANTIC-IN throughout the whole loop > ! and thus effectively hoisted across loops by combination of > ! PRE and hoisting. */ > ! if (! loop_exit_edge_p (block->loop_father, e)) > ! bitmap_ior_and_into (&availout_in_some, &hoistable_set.values, > ! &AVAIL_OUT (e->dest)->values); > ! bitmap_clear (&hoistable_set.values); > ! > ! /* Short-cut for a common case: availout_in_some is empty. */ > ! if (bitmap_empty_p (&availout_in_some)) > ! return false; > ! > ! /* Hack hoitable_set in-place so we can use sorted_array_from_bitmap_set. > */ > ! hoistable_set.values = availout_in_some; > ! hoistable_set.expressions = ANTIC_IN (block)->expressions; > ! > ! /* Now finally construct the topological-ordered expression set. */ > ! vec<pre_expr> exprs = sorted_array_from_bitmap_set (&hoistable_set); > ! > ! bitmap_clear (&hoistable_set.values); > ! > ! /* If there are candidate values for hoisting, insert expressions > ! strategically to make the hoistable expressions fully redundant. */ > ! pre_expr expr; > ! FOR_EACH_VEC_ELT (exprs, i, expr) > ! { > ! /* While we try to sort expressions topologically above the > ! sorting doesn't work out perfectly. Catch expressions we > ! already inserted. */ > ! unsigned int value_id = get_expr_value_id (expr); > ! if (bitmap_set_contains_value (AVAIL_OUT (block), value_id)) > ! { > ! if (dump_file && (dump_flags & TDF_DETAILS)) > ! { > ! fprintf (dump_file, > ! "Already inserted expression for "); > ! print_pre_expr (dump_file, expr); > ! fprintf (dump_file, " (%04d)\n", value_id); > ! } > ! continue; > ! } > ! > ! /* OK, we should hoist this value. Perform the transformation. */ > ! pre_stats.hoist_insert++; > ! if (dump_file && (dump_flags & TDF_DETAILS)) > ! { > ! fprintf (dump_file, > ! "Inserting expression in block %d for code hoisting: ", > ! block->index); > ! print_pre_expr (dump_file, expr); > ! fprintf (dump_file, " (%04d)\n", value_id); > ! } > ! > ! gimple_seq stmts = NULL; > ! tree res = create_expression_by_pieces (block, expr, &stmts, > ! get_expr_type (expr)); > ! if (gsi_end_p (last) || is_ctrl_stmt (gsi_stmt (last))) > ! gsi_insert_seq_before (&last, stmts, GSI_SAME_STMT); > ! else > ! gsi_insert_seq_after (&last, stmts, GSI_NEW_STMT); > ! > ! /* Make sure to not return true if expression creation ultimately > ! failed but also make sure to insert any stmts produced as they > ! are tracked in inserted_exprs. */ > ! if (! res) > ! continue; > ! > ! new_stuff = true; > ! } > ! > ! exprs.release (); > ! > ! return new_stuff; > ! } > ! > ! /* Do a dominator walk on the control flow graph, and insert computations > ! of values as necessary for PRE and hoisting. */ > ! > ! static bool > ! insert_aux (basic_block block, bool do_pre, bool do_hoist) > { > basic_block son; > bool new_stuff = false; > *************** insert_aux (basic_block block) > *** 3437,3443 **** > { > unsigned i; > bitmap_iterator bi; > ! bitmap_set_t newset = NEW_SETS (dom); > if (newset) > { > /* Note that we need to value_replace both NEW_SETS, and > --- 3654,3664 ---- > { > unsigned i; > bitmap_iterator bi; > ! bitmap_set_t newset; > ! > ! /* First, update the AVAIL_OUT set with anything we may have > ! inserted higher up in the dominator tree. */ > ! newset = NEW_SETS (dom); > if (newset) > { > /* Note that we need to value_replace both NEW_SETS, and > *************** insert_aux (basic_block block) > *** 3451,3475 **** > bitmap_value_replace_in_set (AVAIL_OUT (block), expr); > } > } > ! if (!single_pred_p (block)) > { > ! new_stuff |= do_regular_insertion (block, dom); > if (do_partial_partial) > ! new_stuff |= do_partial_partial_insertion (block, dom); > } > } > } > for (son = first_dom_son (CDI_DOMINATORS, block); > son; > son = next_dom_son (CDI_DOMINATORS, son)) > { > ! new_stuff |= insert_aux (son); > } > > return new_stuff; > } > > ! /* Perform insertion of partially redundant values. */ > > static void > insert (void) > --- 3672,3702 ---- > bitmap_value_replace_in_set (AVAIL_OUT (block), expr); > } > } > ! > ! /* Insert expressions for partial redundancies. */ > ! if (do_pre && !single_pred_p (block)) > { > ! new_stuff |= do_pre_regular_insertion (block, dom); > if (do_partial_partial) > ! new_stuff |= do_pre_partial_partial_insertion (block, dom); > } > + > + /* Insert expressions for hoisting. */ > + if (do_hoist && EDGE_COUNT (block->succs) >= 2) > + new_stuff |= do_hoist_insertion (block); > } > } > for (son = first_dom_son (CDI_DOMINATORS, block); > son; > son = next_dom_son (CDI_DOMINATORS, son)) > { > ! new_stuff |= insert_aux (son, do_pre, do_hoist); > } > > return new_stuff; > } > > ! /* Perform insertion of partially redundant and hoistable values. */ > > static void > insert (void) > *************** insert (void) > *** 3486,3492 **** > num_iterations++; > if (dump_file && dump_flags & TDF_DETAILS) > fprintf (dump_file, "Starting insert iteration %d\n", num_iterations); > ! new_stuff = insert_aux (ENTRY_BLOCK_PTR_FOR_FN (cfun)); > > /* Clear the NEW sets before the next iteration. We have already > fully propagated its contents. */ > --- 3713,3720 ---- > num_iterations++; > if (dump_file && dump_flags & TDF_DETAILS) > fprintf (dump_file, "Starting insert iteration %d\n", num_iterations); > ! new_stuff = insert_aux (ENTRY_BLOCK_PTR_FOR_FN (cfun), flag_tree_pre, > ! flag_code_hoisting); > > /* Clear the NEW sets before the next iteration. We have already > fully propagated its contents. */ > *************** public: > *** 4833,4839 **** > {} > > /* opt_pass methods: */ > ! virtual bool gate (function *) { return flag_tree_pre != 0; } > virtual unsigned int execute (function *); > > }; // class pass_pre > --- 5061,5068 ---- > {} > > /* opt_pass methods: */ > ! virtual bool gate (function *) > ! { return flag_tree_pre != 0 || flag_code_hoisting != 0; } > virtual unsigned int execute (function *); > > }; // class pass_pre > *************** pass_pre::execute (function *fun) > *** 4888,4893 **** > --- 5117,5123 ---- > > statistics_counter_event (fun, "Insertions", pre_stats.insertions); > statistics_counter_event (fun, "PA inserted", pre_stats.pa_insert); > + statistics_counter_event (fun, "HOIST inserted", pre_stats.hoist_insert); > statistics_counter_event (fun, "New PHIs", pre_stats.phis); > statistics_counter_event (fun, "Eliminated", pre_stats.eliminations); > > Index: gcc/common.opt > =================================================================== > *** gcc/common.opt.orig 2016-07-07 09:46:02.655811772 +0200 > --- gcc/common.opt 2016-07-07 14:45:45.572877145 +0200 > *************** fchecking= > *** 1038,1043 **** > --- 1038,1047 ---- > Common Joined RejectNegative UInteger Var(flag_checking) > Perform internal consistency checkings. > > + fcode-hoisting > + Common Report Var(flag_code_hoisting) Optimization > + Enable code hoisting. > + > fcombine-stack-adjustments > Common Report Var(flag_combine_stack_adjustments) Optimization > Looks for opportunities to reduce stack adjustments and stack references. > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-1.c > =================================================================== > *** /dev/null 1970-01-01 00:00:00.000000000 +0000 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-1.c 2016-07-07 14:45:45.572877145 > +0200 > *************** > *** 0 **** > --- 1,16 ---- > + /* { dg-do compile } */ > + /* { dg-options "-O2 -fdump-tree-pre" } */ > + > + unsigned short f(unsigned short a) > + { > + if (a & 0x8000) > + a <<= 1, a = a ^ 0x1021; > + else > + a <<= 1; > + > + return a; > + } > + > + /* We should hoist and CSE the shift. */ > + > + /* { dg-final { scan-tree-dump-times " << 1;" 1 "pre" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-2.c > =================================================================== > *** /dev/null 1970-01-01 00:00:00.000000000 +0000 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-2.c 2016-07-07 14:45:45.572877145 > +0200 > *************** > *** 0 **** > --- 1,15 ---- > + /* { dg-do compile } */ > + /* { dg-options "-O2 -fdump-tree-pre" } */ > + > + int f(int i) > + { > + if (i < 0) > + return i/10+ i; > + return i/10 + i; > + } > + > + /* Hoisting of i/10 + i should make the code straight-line > + with one division. */ > + > + /* { dg-final { scan-tree-dump-times "goto" 0 "pre" } } */ > + /* { dg-final { scan-tree-dump-times " / 10;" 1 "pre" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/pr43491.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/pr43491.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/pr43491.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > > #define REGISTER register > > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-optimized" } */ > > #define REGISTER register > > *************** long foo(long data, long v) > *** 35,41 **** > u = i; > return v * t * u; > } > /* We should not eliminate global register variable when it is the RHS of > ! a single assignment. */ > ! /* { dg-final { scan-tree-dump-times "Eliminated: 2" 1 "pre" { target { > arm*-*-* i?86-*-* mips*-*-* x86_64-*-* } } } } */ > ! /* { dg-final { scan-tree-dump-times "Eliminated: 3" 1 "pre" { target { ! { > arm*-*-* i?86-*-* mips*-*-* x86_64-*-* } } } } } */ > --- 35,45 ---- > u = i; > return v * t * u; > } > + > /* We should not eliminate global register variable when it is the RHS of > ! a single assignment. So the number of loads from data_0 has to match > ! that of the number of adds (we hoist data_0 + data_3 above the > ! if (data) and eliminate the useless copy). */ > ! > ! /* { dg-final { scan-tree-dump-times "= data_0;" 1 "optimized" { target { > arm*-*-* i?86-*-* mips*-*-* x86_64-*-* } } } } */ > ! /* { dg-final { scan-tree-dump-times " \\+ " 1 "optimized" { target { ! { > arm*-*-* i?86-*-* mips*-*-* x86_64-*-* } } } } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-3.c > =================================================================== > *** /dev/null 1970-01-01 00:00:00.000000000 +0000 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-3.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 0 **** > --- 1,18 ---- > + /* { dg-do compile } */ > + /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > + > + int test (int a, int b, int c, int g) > + { > + int d, e; > + if (a) > + d = b * c; > + else > + d = b - c; > + e = b * c + g; > + return d + e; > + } > + > + /* We should hoist and CSE only the multiplication. */ > + > + /* { dg-final { scan-tree-dump-times " \\* " 1 "pre" } } */ > + /* { dg-final { scan-tree-dump "Insertions: 1" "pre" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-4.c > =================================================================== > *** /dev/null 1970-01-01 00:00:00.000000000 +0000 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-4.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 0 **** > --- 1,24 ---- > + /* { dg-do compile } */ > + /* { dg-options "-O2 -fdump-tree-optimized" } */ > + > + /* From PR21485. */ > + > + long > + NumSift (long *array, int b, unsigned long k) > + { > + if (b) > + if (array[k] < array[k + 1L]) > + ++k; > + return array[k]; > + } > + > + /* There should be only two loads left. And the final value in the > + if (b) arm should be if-converted: > + tem1 = array[k]; > + if (b) > + tem1 = MAX (array[k+1], tem1) > + return tem1; */ > + > + /* { dg-final { scan-tree-dump-times "= \\*" 2 "optimized" } } */ > + /* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "optimized" } } */ > + /* { dg-final { scan-tree-dump-times "= PHI" 1 "optimized" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-27.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-27.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-27.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre" } */ > > int foo (int i, int j, int b) > { > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre -fno-code-hoisting" } */ > > int foo (int i, int j, int b) > { > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-28.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-28.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-28.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 1,6 **** > /* PR37997 */ > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-details" } */ > > int foo (int i, int b, int result) > { > --- 1,6 ---- > /* PR37997 */ > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-pre-details -fno-code-hoisting" } */ > > int foo (int i, int b, int result) > { > *************** int foo (int i, int b, int result) > *** 16,20 **** > --- 16,22 ---- > > /* We should insert i + 1 into the if (b) path as well as the simplified > i + 1 & -2 expression. And do replacement with two PHI temps. */ > + /* With hoisting enabled we'd hoist i + 1 to before the if, retaining > + only one PHI node. */ > > /* { dg-final { scan-tree-dump-times "with prephitmp" 2 "pre" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-6.c > =================================================================== > *** /dev/null 1970-01-01 00:00:00.000000000 +0000 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-hoist-6.c 2016-07-07 14:45:45.576877193 > +0200 > *************** > *** 0 **** > --- 1,19 ---- > + /* { dg-do compile } */ > + /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > + > + int a[1024]; > + int b[1024], c[1024]; > + void foo () > + { > + for (int j = 0; j < 1024; ++j) > + { > + for (int i = 0; i < 1024; ++i) > + a[i] = j; > + b[j] = c[j]; > + } > + } > + > + /* We should not hoist/PRE the outer loop IV increment or the load > + from c across the inner loop. */ > + > + /* { dg-final { scan-tree-dump-not "HOIST inserted" "pre" } } */ > Index: gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-31.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-31.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/ssa-pre-31.c 2016-07-07 14:45:45.576877193 > +0200 > *************** int foo (S1 *root, int N) > *** 43,46 **** > return 0; > } > > ! /* { dg-final { scan-tree-dump-times "key" 4 "pre" } } */ > --- 43,46 ---- > return 0; > } > > ! /* { dg-final { scan-tree-dump-times "key" 3 "pre" } } */ > Index: gcc/testsuite/gcc.dg/hoist-register-pressure-1.c > =================================================================== > *** gcc/testsuite/gcc.dg/hoist-register-pressure-1.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/hoist-register-pressure-1.c 2016-07-07 > 14:45:45.608877575 +0200 > *************** > *** 1,4 **** > ! /* { dg-options "-Os -fdump-rtl-hoist" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > --- 1,4 ---- > ! /* { dg-options "-Os -fdump-rtl-hoist -fno-code-hoisting" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > Index: gcc/testsuite/gcc.dg/hoist-register-pressure-2.c > =================================================================== > *** gcc/testsuite/gcc.dg/hoist-register-pressure-2.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/hoist-register-pressure-2.c 2016-07-07 > 14:45:45.612877623 +0200 > *************** > *** 1,4 **** > ! /* { dg-options "-Os -fdump-rtl-hoist" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > --- 1,4 ---- > ! /* { dg-options "-Os -fdump-rtl-hoist -fno-code-hoisting" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > Index: gcc/testsuite/gcc.dg/hoist-register-pressure-3.c > =================================================================== > *** gcc/testsuite/gcc.dg/hoist-register-pressure-3.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/hoist-register-pressure-3.c 2016-07-07 > 14:45:45.624877766 +0200 > *************** > *** 1,4 **** > ! /* { dg-options "-Os -fdump-rtl-hoist" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > --- 1,4 ---- > ! /* { dg-options "-Os -fdump-rtl-hoist -fno-code-hoisting" } */ > /* The rtl hoist pass requires that the expression to be hoisted can > be assigned without clobbering cc. For a PLUS rtx on S/390 this > requires a load address instruction which is fine on 64 bit but > Index: gcc/testsuite/gcc.dg/pr51879-12.c > =================================================================== > *** gcc/testsuite/gcc.dg/pr51879-12.c.orig 2016-07-07 09:46:02.655811772 > +0200 > --- gcc/testsuite/gcc.dg/pr51879-12.c 2016-07-07 14:45:45.624877766 +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -ftree-tail-merge -fdump-tree-pre" } */ > > __attribute__((pure)) int bar (int); > __attribute__((pure)) int bar2 (int); > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -ftree-tail-merge -fdump-tree-pre -fno-code-hoisting" > } */ > > __attribute__((pure)) int bar (int); > __attribute__((pure)) int bar2 (int); > Index: gcc/testsuite/gcc.dg/strlenopt-9.c > =================================================================== > *** gcc/testsuite/gcc.dg/strlenopt-9.c.orig 2016-07-07 09:46:02.655811772 > +0200 > --- gcc/testsuite/gcc.dg/strlenopt-9.c 2016-07-07 14:45:45.640877957 +0200 > *************** > *** 1,5 **** > /* { dg-do run } */ > ! /* { dg-options "-O2 -fdump-tree-strlen -fdump-tree-optimized" } */ > > #include "strlenopt.h" > > --- 1,5 ---- > /* { dg-do run } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-strlen > -fdump-tree-optimized" } */ > > #include "strlenopt.h" > > Index: gcc/testsuite/gfortran.dg/pr43984.f90 > =================================================================== > *** gcc/testsuite/gfortran.dg/pr43984.f90.orig 2016-07-07 09:46:02.655811772 > +0200 > --- gcc/testsuite/gfortran.dg/pr43984.f90 2016-07-07 14:45:45.648878052 > +0200 > *************** end subroutine > *** 50,55 **** > > end > > ! ! There should be three loads from iyz.data, not four. > > ! ! { dg-final { scan-tree-dump-times "= iyz.data" 3 "pre" } } > --- 50,55 ---- > > end > > ! ! There should be two loads from iyz.data, not four. > > ! ! { dg-final { scan-tree-dump-times "= iyz.data" 2 "pre" } } > Index: gcc/testsuite/gcc.dg/tree-ssa/pr47392.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/pr47392.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/pr47392.c 2016-07-07 14:45:45.648878052 > +0200 > *************** > *** 1,5 **** > /* { dg-do run } */ > ! /* { dg-options "-O2 -fdump-tree-pre-stats" } */ > > struct A > { > --- 1,5 ---- > /* { dg-do run } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-pre-stats" } */ > > struct A > { > Index: gcc/testsuite/gcc.dg/tree-ssa/pr68619-4.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/pr68619-4.c.orig 2016-07-07 > 09:46:02.655811772 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/pr68619-4.c 2016-07-07 14:45:45.648878052 > +0200 > *************** > *** 1,5 **** > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fdump-tree-optimized -w" } */ > > typedef struct rtx_def *rtx; > enum rtx_code > --- 1,5 ---- > /* { dg-do compile } */ > ! /* { dg-options "-O2 -fno-code-hoisting -fdump-tree-optimized -w" } */ > > typedef struct rtx_def *rtx; > enum rtx_code > Index: gcc/testsuite/gcc.dg/tree-ssa/slsr-35.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/slsr-35.c.orig 2016-07-05 > 15:24:03.059612220 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/slsr-35.c 2016-07-08 06:54:20.620252838 > +0200 > *************** > *** 3,9 **** > phi has an argument which is a parameter. */ > > /* { dg-do compile } */ > ! /* { dg-options "-O3 -fdump-tree-optimized" } */ > > int > f (int c, int i) > --- 3,9 ---- > phi has an argument which is a parameter. */ > > /* { dg-do compile } */ > ! /* { dg-options "-O3 -fno-code-hoisting -fdump-tree-optimized" } */ > > int > f (int c, int i) > Index: gcc/testsuite/gcc.dg/tree-ssa/slsr-36.c > =================================================================== > *** gcc/testsuite/gcc.dg/tree-ssa/slsr-36.c.orig 2016-07-05 > 15:24:03.059612220 +0200 > --- gcc/testsuite/gcc.dg/tree-ssa/slsr-36.c 2016-07-08 06:54:32.300387549 > +0200 > *************** > *** 3,9 **** > phi has an argument which is a parameter. */ > > /* { dg-do compile } */ > ! /* { dg-options "-O3 -fdump-tree-optimized" } */ > > int > f (int s, int c, int i) > --- 3,9 ---- > phi has an argument which is a parameter. */ > > /* { dg-do compile } */ > ! /* { dg-options "-O3 -fno-code-hoisting -fdump-tree-optimized" } */ > > int > f (int s, int c, int i)