LCOV - code coverage report
Current view: top level - gcc - fold-mem-offsets.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 77.5 % 546 423
Test Date: 2026-07-11 15:47:05 Functions: 88.0 % 25 22
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Late RTL pass to fold memory offsets.
       2              :    Copyright (C) 2023-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify
       7              : it under the terms of the GNU General Public License as published by
       8              : the Free Software Foundation; either version 3, or (at your option)
       9              : any later version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful,
      12              : but WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14              : GNU General Public License for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : #define INCLUDE_ALGORITHM
      21              : #define INCLUDE_FUNCTIONAL
      22              : #include "config.h"
      23              : #include "system.h"
      24              : #include "coretypes.h"
      25              : #include "backend.h"
      26              : #include "rtl.h"
      27              : #include "rtlanal.h"
      28              : #include "df.h"
      29              : #include "rtl-ssa.h"
      30              : 
      31              : #include "predict.h"
      32              : #include "cfgrtl.h"
      33              : #include "cfgcleanup.h"
      34              : #include "tree-pass.h"
      35              : #include "target.h"
      36              : 
      37              : #include "tm.h"
      38              : #include "tree.h"
      39              : #include "expr.h"
      40              : #include "regs.h"
      41              : #include "memmodel.h"
      42              : #include "emit-rtl.h"
      43              : #include "insn-config.h"
      44              : #include "recog.h"
      45              : #include "diagnostic-core.h"
      46              : 
      47              : /* This pass tries to optimize memory offset calculations by moving constants
      48              :    from add instructions to the memory instructions (loads / stores).
      49              :    For example it can transform code like this:
      50              : 
      51              :      add  t4, sp, 16
      52              :      add  t2, a6, t4
      53              :      shl  t3, t2, 1
      54              :      ld   a2, 0(t3)
      55              :      add  a2, 1
      56              :      sd   a2, 8(t2)
      57              : 
      58              :    into the following (one instruction less):
      59              : 
      60              :      add  t2, a6, sp
      61              :      shl  t3, t2, 1
      62              :      ld   a2, 32(t3)
      63              :      add  a2, 1
      64              :      sd   a2, 24(t2)
      65              : 
      66              :    Although the previous passes try to emit efficient offset calculations
      67              :    this pass is still beneficial because:
      68              : 
      69              :     - The mechanisms that optimize memory offsets usually work with specific
      70              :       patterns or have limitations.  This pass is designed to fold offsets
      71              :       through complex calculations that affect multiple memory operations
      72              :       and have partially overlapping calculations.
      73              : 
      74              :     - There are cases where add instructions are introduced in late rtl passes
      75              :       and the rest of the pipeline cannot eliminate them.  Arrays and structs
      76              :       allocated on the stack can result in unwanted add instructions that
      77              :       cannot be eliminated easily.
      78              : 
      79              :    The pass differentiates between the following instructions:
      80              : 
      81              :    - fold-mem-offset root insn: loads/stores where constants will be folded into
      82              :      the address offset.  E.g.:
      83              :        (set (mem:DI (plus:DI (reg:DI sp) (const_int 40))) (reg:DI ra))
      84              :    - fold-agnostic insns: instructions that may have an impact on the offset
      85              :      calculation, but that don't require any fixup when folding.  E.g.:
      86              :        (set (reg:DI a0) (ashift:DI (reg:DI s1) (const_int 1)))
      87              :    - fold insns: instructions that provide constants, which will be forwarded
      88              :      into the loads/stores as offset.  When folding, the constants will be
      89              :      set to zero.  E.g.:
      90              :        (set (reg:DI s0) (plus:DI (reg:DI sp) (const_int 8)))
      91              : 
      92              :    The pass utilizes the RTL SSA framework to get the data dependencies
      93              :    and operates in the following phases:
      94              : 
      95              :    - Phase 1: Iterate over all instructions to identify fold-mem-offset roots.
      96              :    - Phase 2: Walk back along the def-chain of fold-agnostic or fold insns.
      97              :               When successful a new offset of the fold-mem-offset is calculated
      98              :               and a vec of fold insns that need adjustments is created.
      99              :    - Phase 3: Drop all fold-mem-offset roots that won't accept the updated
     100              :               offset.
     101              :    - Phase 4: Ensure that the defs of all fold insns are used only by
     102              :               fold-mem-offsets insns (only needed if DEFs with multiple USEs
     103              :               are handled, which is the default (see multi_use_mode below),
     104              :               or forced on via -fexpensive-optimizations).
     105              :    - Phase 5: Update all fold-mem-offset roots and adjust the fold insns.
     106              : 
     107              :    When we walk the DEF-chain we have two choices of operations:
     108              : 
     109              :    - We only allow DEFs that have exactly one USE (in the instruction
     110              :      that we come from).  This greatly simplifies the problem, but also misses
     111              :      some cases.
     112              :    - We allow DEFs to have multiple USEs.  E.g. a single ADDI may define a
     113              :      value that is used by two LOADs.  In this case, we need to ensure that all
     114              :      USE-chains remain correct after we apply our transformation.  We do this
     115              :      by allowing only USEs that are part of any other fold-mem-offset chain in
     116              :      phase 4 above.  This mode is enabled by default, but is disabled for
     117              :      highly-connected CFGs (unless -fexpensive-optimizations forces it on).
     118              : 
     119              :    This pass should run before hard register propagation because it creates
     120              :    register moves that we expect to be eliminated.  */
     121              : 
     122              : using namespace rtl_ssa;
     123              : 
     124              : namespace {
     125              : 
     126              : /* Class that holds in FOLD_INSNS the instructions that if folded the offset
     127              :    of a memory instruction would increase by ADDED_OFFSET.  */
     128     28898076 : class fold_mem_info {
     129              : public:
     130              :   /* fold-mem-offset root details.  */
     131              :   insn_info *insn;
     132              :   rtx mem;
     133              :   rtx reg;
     134              :   HOST_WIDE_INT offset;
     135              :   /* Resulting offset if def-chain gets folded into fold-mem-offset root.  */
     136              :   HOST_WIDE_INT added_offset;
     137              : 
     138              :   /* Def-chain for offset.  */
     139              :   auto_vec<insn_info *> fold_agnostic_insns;
     140              :   auto_vec<insn_info *> fold_insns;
     141              : 
     142     28898076 :   fold_mem_info (insn_info *insn, rtx mem, rtx reg, HOST_WIDE_INT off)
     143     28898076 :     : insn (insn),
     144     28898076 :       mem (mem),
     145     28898076 :       reg (reg),
     146     28898076 :       offset (off),
     147     28898076 :       added_offset (0)
     148              :   {
     149              :   }
     150              : };
     151              : 
     152              : class change_info {
     153              : public:
     154              :   insn_change *change;
     155              :   /* Index specifying the order in RTL SSA's instruction changes.  */
     156              :   int change_index;
     157              : 
     158              :   change_info (insn_change *change)
     159              :     : change (change), change_index (0)
     160              :   {
     161              :   }
     162              : 
     163           58 :   change_info (insn_change *change, int index)
     164           58 :     : change (change), change_index (index)
     165              :   {
     166              :   }
     167              : };
     168              : 
     169              : /* Test if INSN is a memory load / store that can have an offset folded to it.
     170              :    Return true when INSN is such an instruction and return through MEM,
     171              :    REG and OFFSET the RTX that has a MEM code, the register that is
     172              :    used as a base address and the offset accordingly.  */
     173              : bool
     174    119586972 : get_fold_mem_offset_root (insn_info *insn, rtx *mem, rtx *reg,
     175              :                           HOST_WIDE_INT *offset)
     176              : {
     177    119586972 :   rtx set = single_set (insn->rtl ());
     178    119586972 :   if (set != NULL_RTX)
     179              :     {
     180    110282178 :       rtx src = SET_SRC (set);
     181    110282178 :       rtx dest = SET_DEST (set);
     182              : 
     183              :       /* Don't fold when we have unspec / volatile.  */
     184    110282178 :       if (GET_CODE (src) == UNSPEC
     185    110282178 :           || GET_CODE (src) == UNSPEC_VOLATILE)
     186              :         return false;
     187              : 
     188    109469768 :       if (MEM_P (src))
     189     19168348 :         *mem = src;
     190     90301420 :       else if (MEM_P (dest))
     191     21518460 :         *mem = dest;
     192     68782960 :       else if ((GET_CODE (src) == SIGN_EXTEND
     193     68782960 :                 || GET_CODE (src) == ZERO_EXTEND)
     194      1103948 :                && MEM_P (XEXP (src, 0)))
     195       329108 :         *mem = XEXP (src, 0);
     196              :       else
     197              :         return false;
     198              :     }
     199              :   else
     200              :     return false;
     201              : 
     202     41015916 :   rtx mem_addr = XEXP (*mem, 0);
     203     41015916 :   if (REG_P (mem_addr))
     204              :     {
     205      4810802 :       *reg = mem_addr;
     206      4810802 :       *offset = 0;
     207              :     }
     208     36205114 :   else if (GET_CODE (mem_addr) == PLUS
     209     25916398 :            && REG_P (XEXP (mem_addr, 0))
     210     24684912 :            && CONST_INT_P (XEXP (mem_addr, 1)))
     211              :     {
     212     24087274 :       *reg = XEXP (mem_addr, 0);
     213     24087274 :       *offset = INTVAL (XEXP (mem_addr, 1));
     214              :     }
     215              :   else
     216              :     return false;
     217              : 
     218              :   return true;
     219              : }
     220              : 
     221              : /* Return true if DEF's register is conditionally redefined by a cond_exec
     222              :    before DEF is fully overwritten.  RTL-SSA models a cond_exec set as a plain
     223              :    def, missing the old value preserved on the predicate-false path, so such a
     224              :    redefinition hides a use of DEF and makes folding it unsafe.  */
     225              : 
     226              : static bool
     227     11819380 : def_shadowed_by_cond_exec_p (set_info *def)
     228              : {
     229     23771584 :   for (def_info *next = def->next_def (); next; next = next->next_def ())
     230              :     {
     231     11194518 :       insn_info *next_insn = next->insn ();
     232     11194518 :       if (next_insn->is_artificial ())
     233              :         return false;
     234              : 
     235      5128080 :       rtx_insn *next_rtl = next_insn->rtl ();
     236      5128080 :       if (GET_CODE (PATTERN (next_rtl)) == COND_EXEC)
     237              :         return true;
     238              : 
     239              :       /* A full set kills DEF; once dead it cannot reach a later cond_exec.
     240              :          Keep scanning past defs that leave DEF partially live.  */
     241      5128080 :       if (simple_regno_set (PATTERN (next_rtl), def->regno ()))
     242              :         return false;
     243              :     }
     244              :   return false;
     245              : }
     246              : 
     247              : /* Get the single reaching definition of an instruction inside a BB.
     248              :    Return the definition or NULL if there's no definition with the desired
     249              :    criteria.  If SINGLE_USE is set to true the DEF must have exactly one
     250              :    USE resulting in a 1:1 DEF-USE relationship.  If set to false, then a
     251              :    1:n DEF-USE relationship is accepted and the caller must take care to
     252              :    ensure all USEs are safe folding.  */
     253              : static set_info *
     254     11819392 : get_single_def_in_bb (insn_info *insn, rtx reg, bool single_use)
     255              : {
     256              :   /* Get the use_info of the base register.  */
     257     13856712 :   for (use_info *use : insn->uses ())
     258              :     {
     259              :       /* Other USEs can be ignored and multiple equal USEs are fine.  */
     260     13856712 :       if (use->regno () != REGNO (reg))
     261      2037320 :         continue;
     262              : 
     263              :       /* Don't handle subregs for now.  */
     264     11819392 :       if (use->includes_subregs ())
     265     11819392 :         return NULL;
     266              : 
     267              :       /* Get the DEF of the register.  Bail on a cond_exec redefinition of
     268              :          REG, which hides uses of DEF from RTL-SSA.  */
     269     11819380 :       set_info *def = use->def ();
     270     11819380 :       if (!def || def_shadowed_by_cond_exec_p (def))
     271            0 :         return NULL;
     272              : 
     273              :       /* Limit the amount of USEs of DEF to 1.  */
     274     11819380 :       if (single_use && !def->single_nondebug_use ())
     275              :         return NULL;
     276              : 
     277              :       /* Don't handle multiregs for now.  */
     278      6851544 :       if (def->includes_multiregs ())
     279              :         return NULL;
     280              : 
     281              :       /* Only consider uses whose definition comes from a real instruction
     282              :          and has no notes attached.  */
     283      6845337 :       insn_info *def_insn = def->insn ();
     284      6845337 :       rtx_insn *def_rtl = def_insn->rtl ();
     285      6845337 :       if (def_insn->is_artificial ()
     286      2733164 :           || find_reg_note (def_rtl, REG_EQUIV, NULL_RTX)
     287      9040253 :           || find_reg_note (def_rtl, REG_EQUAL, NULL_RTX))
     288      4657681 :         return NULL;
     289              : 
     290              :       /* No parallel expressions or clobbers.  */
     291      2187656 :       if (def_insn->num_defs () != 1)
     292              :         return NULL;
     293              : 
     294      1816037 :       if (!NONJUMP_INSN_P (def_rtl) || RTX_FRAME_RELATED_P (def_rtl))
     295              :         return NULL;
     296              : 
     297              :       /* Check if the DEF is a SET of the expected form.  */
     298      1565993 :       rtx def_set = simple_regno_set (PATTERN (def_rtl), def->regno ());
     299      1565993 :       if (!def_set)
     300              :         return NULL;
     301              : 
     302              :       /* Ensure DEF and USE are in the same BB.  */
     303      1565813 :       if (def->bb () != insn->bb ())
     304              :         return NULL;
     305              : 
     306              :       return def;
     307              :     }
     308              : 
     309            0 :   return NULL;
     310              : }
     311              : 
     312              : static bool
     313              : fold_offsets (insn_info *insn, rtx reg, HOST_WIDE_INT *offset_out,
     314              :               fold_mem_info *info, bool single_use);
     315              : 
     316              : /* Return the offset computed by fold_offsets, or 0 if the analysis fails.
     317              :    Used in fold_offsets_1 where failure means no constant contribution.  */
     318              : static HOST_WIDE_INT
     319       204058 : fold_offsets_value (insn_info *insn, rtx reg, fold_mem_info *info,
     320              :                     bool single_use)
     321              : {
     322       204058 :   HOST_WIDE_INT offset;
     323       204058 :   fold_offsets (insn, reg, &offset, info, single_use);
     324       204058 :   return offset;
     325              : }
     326              : 
     327              : /* Helper function for fold_offsets () that analyses the given INSN.
     328              : 
     329              :    For INSN with known pattern, we calculate the value of the propagated
     330              :    constant and store that in OFFSET_OUT.  Foldable INSNs are added to
     331              :    INFO->fold_insns and fold-agnostic INSNs are added to
     332              :    INFO->fold_agnostic_insns.  It is possible that some INSNs are added to
     333              :    both lists; when that happens the INSN is a fold insn.
     334              : 
     335              :    Returns true when the analysis succeeds.  Otherwise false.  */
     336              : static bool
     337       699397 : fold_offsets_1 (insn_info *insn, HOST_WIDE_INT *offset_out,
     338              :                 fold_mem_info *info, bool single_use)
     339              : {
     340       699397 :   bool fold_agnostic = true;
     341       699397 :   rtx_insn *insn_rtl = insn->rtl ();
     342       699397 :   rtx set = single_set (insn_rtl);
     343       699397 :   if (!set)
     344              :     return false;
     345              : 
     346       699397 :   rtx src = SET_SRC (set);
     347       699397 :   HOST_WIDE_INT offset = 0;
     348              : 
     349       699397 :   switch (GET_CODE (src))
     350              :     {
     351        44441 :     case PLUS:
     352        44441 :       {
     353              :         /* Propagate through add.  */
     354        44441 :         rtx arg1 = XEXP (src, 0);
     355        44441 :         rtx arg2 = XEXP (src, 1);
     356              : 
     357        44441 :         if (REG_P (arg1))
     358        12908 :           offset += fold_offsets_value (insn, arg1, info, single_use);
     359        31533 :         else if (GET_CODE (arg1) == ASHIFT
     360        23453 :                  && REG_P (XEXP (arg1, 0))
     361        23453 :                  && CONST_INT_P (XEXP (arg1, 1)))
     362              :           {
     363              :             /* Handle R1 = (R2 << C) + ...  */
     364        23453 :             rtx reg = XEXP (arg1, 0);
     365        23453 :             rtx shamt = XEXP (arg1, 1);
     366        23453 :             HOST_WIDE_INT scale = HOST_WIDE_INT_1U << INTVAL (shamt);
     367        23453 :             offset += scale * fold_offsets_value (insn, reg, info, single_use);
     368        23453 :           }
     369         8080 :         else if (GET_CODE (arg1) == PLUS
     370         8028 :                  && REG_P (XEXP (arg1, 0))
     371         6956 :                  && REG_P (XEXP (arg1, 1)))
     372              :           {
     373              :             /* Handle R1 = (R2 + R3) + ...  */
     374         6956 :             rtx reg1 = XEXP (arg1, 0);
     375         6956 :             rtx reg2 = XEXP (arg1, 1);
     376         6956 :             if (REGNO (reg1) != REGNO (reg2))
     377              :               {
     378         6956 :                 offset += fold_offsets_value (insn, reg1, info, single_use);
     379         6956 :                 offset += fold_offsets_value (insn, reg2, info, single_use);
     380              :               }
     381              :             else
     382            0 :               offset += 2 * fold_offsets_value (insn, reg1, info, single_use);
     383              :           }
     384         1124 :         else if (GET_CODE (arg1) == PLUS
     385         1072 :                  && GET_CODE (XEXP (arg1, 0)) == ASHIFT
     386         1066 :                  && REG_P (XEXP (XEXP (arg1, 0), 0))
     387         1066 :                  && CONST_INT_P (XEXP (XEXP (arg1, 0), 1))
     388         1066 :                  && REG_P (XEXP (arg1, 1)))
     389              :           {
     390              :             /* Handle R1 = ((R2 << C) + R3) + ...  */
     391         1066 :             rtx reg1 = XEXP (XEXP (arg1, 0), 0);
     392         1066 :             rtx shamt = XEXP (XEXP (arg1, 0), 1);
     393         1066 :             rtx reg2 = XEXP (arg1, 1);
     394         1066 :             HOST_WIDE_INT scale = HOST_WIDE_INT_1U << INTVAL (shamt);
     395         1066 :             if (REGNO (reg1) != REGNO (reg2))
     396              :               {
     397         2072 :                 offset += scale
     398         1036 :                          * fold_offsets_value (insn, reg1, info, single_use);
     399         1036 :                 offset += fold_offsets_value (insn, reg2, info, single_use);
     400              :               }
     401              :             else
     402           30 :               offset += (scale + 1) * fold_offsets_value (insn, reg1, info,
     403              :                                                     single_use);
     404              :           }
     405              :         else
     406              :           return false;
     407              : 
     408        44383 :         if (REG_P (arg2))
     409        33337 :           offset += fold_offsets_value (insn, arg2, info, single_use);
     410        11046 :         else if (CONST_INT_P (arg2))
     411              :           {
     412         9584 :             if (REG_P (arg1))
     413              :               {
     414         1736 :                 offset += INTVAL (arg2);
     415              :                 /* This is a R1 = R2 + C instruction, candidate for
     416              :                    folding.  */
     417         1736 :                 fold_agnostic = false;
     418              :               }
     419              :           }
     420              :         else
     421              :           return false;
     422              : 
     423              :         /* Pattern recognized for folding.  */
     424              :         break;
     425              :       }
     426            0 :     case MINUS:
     427            0 :       {
     428              :         /* Propagate through minus.  */
     429            0 :         rtx arg1 = XEXP (src, 0);
     430            0 :         rtx arg2 = XEXP (src, 1);
     431              : 
     432            0 :         if (REG_P (arg1))
     433            0 :           offset += fold_offsets_value (insn, arg1, info, single_use);
     434              :         else
     435              :           return false;
     436              : 
     437            0 :         if (REG_P (arg2))
     438            0 :           offset -= fold_offsets_value (insn, arg2, info, single_use);
     439            0 :         else if (CONST_INT_P (arg2))
     440              :           {
     441            0 :             if (REG_P (arg1))
     442              :               {
     443            0 :                 offset -= INTVAL (arg2);
     444              :                 /* This is a R1 = R2 - C instruction, candidate for
     445              :                    folding.  */
     446            0 :                 fold_agnostic = false;
     447              :               }
     448              :           }
     449              :         else
     450              :           return false;
     451              : 
     452              :         /* Pattern recognized for folding.  */
     453              :         break;
     454              :       }
     455            0 :     case NEG:
     456            0 :       {
     457              :         /* Propagate through negation.  */
     458            0 :         rtx arg1 = XEXP (src, 0);
     459            0 :         if (REG_P (arg1))
     460            0 :           offset = -fold_offsets_value (insn, arg1, info, single_use);
     461              :         else
     462              :           return false;
     463              : 
     464              :         /* Pattern recognized for folding.  */
     465            0 :         break;
     466              :       }
     467         1093 :     case MULT:
     468         1093 :       {
     469              :         /* Propagate through multiply by constant.  */
     470         1093 :         rtx arg1 = XEXP (src, 0);
     471         1093 :         rtx arg2 = XEXP (src, 1);
     472              : 
     473         1093 :         if (REG_P (arg1) && CONST_INT_P (arg2))
     474              :           {
     475         1093 :             HOST_WIDE_INT scale = INTVAL (arg2);
     476         1093 :             offset = scale * fold_offsets_value (insn, arg1, info, single_use);
     477              :           }
     478              :         else
     479              :           return false;
     480              : 
     481              :         /* Pattern recognized for folding.  */
     482         1093 :         break;
     483              :       }
     484            0 :     case ASHIFT:
     485            0 :       {
     486              :         /* Propagate through shift left by constant.  */
     487            0 :         rtx arg1 = XEXP (src, 0);
     488            0 :         rtx arg2 = XEXP (src, 1);
     489              : 
     490            0 :         if (REG_P (arg1) && CONST_INT_P (arg2))
     491              :           {
     492            0 :             HOST_WIDE_INT scale = (HOST_WIDE_INT_1U << INTVAL (arg2));
     493            0 :             offset = scale * fold_offsets_value (insn, arg1, info, single_use);
     494              :           }
     495              :         else
     496              :           return false;
     497              : 
     498              :         /* Pattern recognized for folding.  */
     499            0 :         break;
     500              :       }
     501       117253 :     case REG:
     502       117253 :       {
     503              :         /* Propagate through register move.  */
     504       117253 :         offset = fold_offsets_value (insn, src, info, single_use);
     505              : 
     506              :         /* Pattern recognized for folding.  */
     507       117253 :         break;
     508              :       }
     509            6 :     case CONST_INT:
     510            6 :       {
     511            6 :         offset = INTVAL (src);
     512              :         /* R1 = C is candidate for folding.  */
     513            6 :         fold_agnostic = false;
     514              : 
     515              :         /* Pattern recognized for folding.  */
     516            6 :         break;
     517              :       }
     518              :     default:
     519              :       /* Cannot recognize.  */
     520              :       return false;
     521              :     }
     522              : 
     523       161273 :   if (offset_out)
     524       161273 :     *offset_out = offset;
     525              : 
     526       161273 :   if (fold_agnostic)
     527              :     {
     528       159531 :       if (!single_use)
     529       139710 :         info->fold_agnostic_insns.safe_push (insn);
     530              :     }
     531         1742 :   else if (!info->fold_insns.contains (insn))
     532         1742 :     info->fold_insns.safe_push (insn);
     533              : 
     534              :   return true;
     535              : }
     536              : 
     537              : /* Returns true when all USEs of DEF (which defines REG) meet certain criteria
     538              :    to be foldable.  Otherwise false.  */
     539              : static bool
     540      1021182 : has_foldable_uses_p (set_info *def, rtx reg)
     541              : {
     542              :   /* We only fold through instructions that are transitively used as
     543              :      memory addresses and do not have other uses.  Use the same logic
     544              :      from offset calculation to visit instructions that can propagate
     545              :      offsets and keep track of them in CAN_FOLD_INSNS.  */
     546     10633162 :   for (use_info *use : def->nondebug_insn_uses ())
     547              :     {
     548      4617184 :       insn_info *use_insn = use->insn ();
     549      4617184 :       if (use_insn->is_artificial ())
     550              :         return false;
     551              : 
     552              :       /* Only handle an insn with a simple single set here.  */
     553      4472423 :       rtx_insn *use_rtl = use_insn->rtl ();
     554      4472423 :       if (!NONJUMP_INSN_P (use_rtl) || GET_CODE (PATTERN (use_rtl)) != SET)
     555              :         return false;
     556              : 
     557              :       /* Special case: A foldable memory store is not foldable if it
     558              :          mentions DEST outside of the address calculation.  */
     559      4396056 :       rtx use_set = PATTERN (use_rtl);
     560      4396056 :       if (use_set && MEM_P (SET_DEST (use_set))
     561      1605031 :           && reg_mentioned_p (reg, SET_SRC (use_set)))
     562              :         return false;
     563              : 
     564      8678744 :       if (use->bb () != def->bb ())
     565              :         return false;
     566              :     }
     567              : 
     568              :   return true;
     569              : }
     570              : 
     571              : 
     572              : /* Function that calculates the offset for INSN that would have to be added to
     573              :    all its USEs of REG.  Foldable INSNs are added to INFO->fold_insns and
     574              :    fold-agnostic INSNs are added to INFO->fold_agnostic_insns.
     575              :    It is possible that some INSNs are added to both lists; when that happens
     576              :    the INSN is a fold insn.
     577              : 
     578              :    On success, returns true and stores the offset in *OFFSET_OUT.  On failure,
     579              :    returns false and sets *OFFSET_OUT to 0.  */
     580              : static bool
     581     29102134 : fold_offsets (insn_info *insn, rtx reg, HOST_WIDE_INT *offset_out,
     582              :               fold_mem_info *info, bool single_use = true)
     583              : {
     584     29102134 :   *offset_out = 0;
     585              : 
     586              :   /* We can only affect the values of GPR registers.  */
     587     29102134 :   unsigned int regno = REGNO (reg);
     588     29102134 :   if (fixed_regs[regno]
     589     29102134 :       || !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
     590              :     return false;
     591              : 
     592              :   /* Get the DEF for REG in INSN.  */
     593     11819392 :   set_info *def = get_single_def_in_bb (insn, reg, single_use);
     594     11819392 :   if (!def)
     595              :     return false;
     596              : 
     597      1021182 :   insn_info *def_insn = def->insn ();
     598      1021182 :   rtx_insn *def_rtl = def_insn->rtl ();
     599              : 
     600      1021182 :   if (dump_file && (dump_flags & TDF_DETAILS))
     601              :     {
     602            0 :       fprintf (dump_file, "For INSN: ");
     603            0 :       print_rtl_single (dump_file, insn->rtl ());
     604            0 :       fprintf (dump_file, "...found DEF: ");
     605            0 :       print_rtl_single (dump_file, def_rtl);
     606              :     }
     607              : 
     608      1021182 :   gcc_assert (REGNO (reg) == def->regno ());
     609              : 
     610              :   /* Check if all USEs of DEF are safe.  */
     611      1021182 :   if (!has_foldable_uses_p (def, reg))
     612              :     {
     613       321785 :       if (dump_file && (dump_flags & TDF_DETAILS))
     614              :         {
     615            0 :           fprintf (dump_file, "has_foldable_uses_p failed for: ");
     616            0 :           print_rtl_single (dump_file, def_rtl);
     617              :         }
     618       321785 :       return false;
     619              :     }
     620              : 
     621              :   /* Check if we know how to handle DEF.  */
     622       699397 :   HOST_WIDE_INT offset;
     623       699397 :   if (!fold_offsets_1 (def_insn, &offset, info, single_use))
     624              :     {
     625       538124 :       if (dump_file && (dump_flags & TDF_DETAILS))
     626              :         {
     627            0 :           fprintf (dump_file, "fold_offsets_1 failed for: ");
     628            0 :           print_rtl_single (dump_file, def_rtl);
     629              :         }
     630       538124 :       return false;
     631              :     }
     632              : 
     633       161273 :   if (dump_file && (dump_flags & TDF_DETAILS))
     634              :     {
     635            0 :       fprintf (dump_file, "Instruction marked for propagation: ");
     636            0 :       print_rtl_single (dump_file, def_rtl);
     637              :     }
     638              : 
     639       161273 :   *offset_out = offset;
     640       161273 :   return true;
     641              : }
     642              : 
     643              : /* Check if any of the provided INSNs in INSN_LIST is not marked in the
     644              :    given bitmap.  Return true if at least one INSN is not in the bitmap and
     645              :    false otherwise.  */
     646              : static bool
     647         1406 : insn_uses_not_in_bitmap (vec<insn_info *> *insn_list, bitmap bm)
     648              : {
     649         4181 :   for (insn_info *insn : *insn_list)
     650              :     {
     651         1354 :       gcc_assert (insn->num_defs () == 1);
     652         1354 :       set_info *def = dyn_cast<set_info *>(insn->defs ()[0]);
     653         5928 :       for (use_info *use : def->nondebug_insn_uses ())
     654              :         {
     655         2897 :           if (!bitmap_bit_p (bm, use->insn ()->uid ()))
     656              :             {
     657         1287 :               if (dump_file && (dump_flags & TDF_DETAILS))
     658              :                 {
     659            0 :                   fprintf (dump_file, "Cannot ensure correct transformation as "
     660              :                            "INSN %u has a USE INSN %u that was not analysed.\n",
     661              :                            insn->uid (), use->insn ()->uid ());
     662              :                 }
     663              : 
     664         1287 :               return true;
     665              :             }
     666              :         }
     667              :     }
     668              : 
     669              :   return false;
     670              : }
     671              : 
     672              : /* Check if all USEs of all instructions have been analysed.
     673              :    If a fold_mem_info is found that has an unknown USE, then
     674              :    drop it from the list.  When this function returns all
     675              :    fold_mem_infos in the worklist reference instructions that
     676              :    have been analysed before and can therefore be committed.  */
     677              : static void
     678       976481 : drop_unsafe_candidates (vec<fold_mem_info *> *worklist)
     679              : {
     680       977768 :   bool changed;
     681      1955536 :   do
     682              :     {
     683       977768 :       changed = false;
     684              : 
     685              :       /* First mark all analysed INSNs in a bitmap.  */
     686       977768 :       auto_bitmap insn_closure;
     687      2939427 :       for (fold_mem_info *info : worklist)
     688              :         {
     689         6123 :           bitmap_set_bit (insn_closure, info->insn->uid ());
     690        25886 :           for (insn_info *insn : info->fold_agnostic_insns)
     691         7779 :             bitmap_set_bit (insn_closure, insn->uid ());
     692        25758 :           for (insn_info *insn : info->fold_insns)
     693         7389 :             bitmap_set_bit (insn_closure, insn->uid ());
     694              :         }
     695              : 
     696              :       /* Now check if all uses of fold_insns are marked.  */
     697              :       unsigned i;
     698              :       fold_mem_info *info;
     699       979517 :       FOR_EACH_VEC_ELT (*worklist, i, info)
     700              :         {
     701         1319 :           if (insn_uses_not_in_bitmap (&info->fold_agnostic_insns, insn_closure)
     702         1319 :               || insn_uses_not_in_bitmap (&info->fold_insns, insn_closure))
     703              :             {
     704         1287 :               if (dump_file && (dump_flags & TDF_DETAILS))
     705              :                 {
     706            0 :                   fprintf (dump_file,
     707              :                            "Dropping fold-mem-offset root INSN %u.\n",
     708            0 :                            info->insn->uid ());
     709              :                 }
     710              : 
     711              :               /* Drop INFO from worklist and restart.  */
     712         1287 :               worklist->unordered_remove (i);
     713         2574 :               delete info;
     714              :               changed = true;
     715              :               break;
     716              :             }
     717              :         }
     718       977768 :     }
     719              :   while (changed);
     720       976481 : }
     721              : 
     722              : /* If INSN is a root memory instruction that was affected by any folding
     723              :    then update its offset as necessary.  */
     724              : static rtx
     725           51 : do_commit_offset (fold_mem_info *info)
     726              : {
     727           51 :   rtx mem = info->mem;
     728           51 :   rtx reg = info->reg;
     729           51 :   HOST_WIDE_INT new_offset = info->offset + info->added_offset;
     730              : 
     731           51 :   if (info->added_offset == 0)
     732              :     return NULL_RTX;
     733              : 
     734           51 :   rtx new_mem = copy_rtx (mem);
     735              : 
     736           51 :   machine_mode mode = GET_MODE (XEXP (new_mem, 0));
     737           51 :   if (new_offset != 0)
     738           51 :     XEXP (new_mem, 0)
     739           51 :       = gen_rtx_PLUS (mode, reg, gen_int_mode (new_offset, mode));
     740              :   else
     741            0 :     XEXP (new_mem, 0) = reg;
     742              : 
     743           51 :   rtx new_insn = simplify_replace_rtx (info->insn->rtl (), mem, new_mem);
     744              : 
     745           51 :   return new_insn;
     746              : }
     747              : 
     748              : /* If INSN is a move / add instruction that was folded then replace its
     749              :    constant with zero.  Append the resulting insn_change to CHANGES for the
     750              :    caller to commit.  */
     751              : static rtx_insn *
     752           16 : do_commit_insn (insn_info *insn, auto_vec<change_info *> *changes)
     753              : {
     754           16 :   rtx_insn *insn_rtl = insn->rtl ();
     755           16 :   rtx_insn *new_insn_rtl = as_a<rtx_insn *> (copy_rtx (insn_rtl));
     756              : 
     757              :   /* If we deleted this INSNs before, then nothing left to do here.  */
     758           16 :   if (insn_rtl->deleted ())
     759              :     return NULL;
     760              : 
     761           16 :   rtx set = single_set (new_insn_rtl);
     762           16 :   rtx src = SET_SRC (set);
     763              : 
     764              :   /* Emit a move and let subsequent passes eliminate it if possible.  */
     765           16 :   if (CONST_INT_P (src))
     766              :     {
     767              :       /* Only change if necessary.  */
     768            0 :       if (INTVAL (src))
     769              :         {
     770              :           /* INSN is R1 = C.  Set C to 0 because it was folded.  */
     771            0 :           SET_SRC (set) = CONST0_RTX (GET_MODE (SET_SRC (set)));
     772            0 :           change_info *change = new change_info (
     773            0 :                                   new insn_change (insn),
     774            0 :                                   num_validated_changes ());
     775            0 :           changes->safe_push (change);
     776              : 
     777            0 :           return new_insn_rtl;
     778              :         }
     779              :     }
     780              :   else
     781              :     {
     782           16 :       if (!BINARY_P (src))
     783              :         return NULL;
     784              : 
     785            6 :       rtx sec_src_op = XEXP (src, 1);
     786              : 
     787              :       /* Only change if necessary.  */
     788            6 :       if (INTVAL (sec_src_op))
     789              :         {
     790              :           /* Mark self-assignments for deletion.  */
     791            6 :           rtx dest = SET_DEST (set);
     792            6 :           change_info *change = nullptr;
     793            6 :           if (REGNO (dest) == REGNO (XEXP (src, 0)))
     794            0 :             change = new change_info (
     795            0 :                        new insn_change (insn, insn_change::DELETE),
     796            0 :                        num_validated_changes ());
     797              :           else
     798              :             {
     799              :               /* If INSN is R1 = R2 + C, C is folded to 0, so emit a mov
     800              :                  instead.  */
     801            6 :               new_insn_rtl = gen_move_insn (SET_DEST (set), XEXP (src, 0));
     802            6 :               change = new change_info (
     803            6 :                          new insn_change (insn), num_validated_changes ());
     804              :             }
     805              : 
     806            6 :           changes->safe_push (change);
     807            6 :           return new_insn_rtl;
     808              :         }
     809              :     }
     810              : 
     811              :   return NULL;
     812              : }
     813              : 
     814              : /* Order two insn_change objects by the program order of their insns.  */
     815              : 
     816              : static bool
     817           61 : sort_changes (insn_change *a, insn_change *b)
     818              : {
     819           61 :   return a->insn ()->compare_with (b->insn ()) < 0;
     820              : }
     821              : 
     822              : /* A changes_map entry copied out for deterministic, regno-ordered traversal:
     823              :    REGNO is the map key and CHANGES the insn_change list for it.  */
     824              : 
     825              : struct regno_changes
     826              : {
     827              :   unsigned regno;
     828              :   auto_vec<insn_change *> *changes;
     829              : };
     830              : 
     831              : /* qsort comparator that orders regno_changes entries by ascending regno.  */
     832              : 
     833              : static int
     834            0 : sort_pairs (const void *p1, const void *p2)
     835              : {
     836            0 :   const regno_changes *a = (const regno_changes *) p1;
     837            0 :   const regno_changes *b = (const regno_changes *) p2;
     838              : 
     839            0 :   if (a->regno < b->regno)
     840              :     return -1;
     841            0 :   if (a->regno > b->regno)
     842            0 :     return 1;
     843              :   return 0;
     844              : }
     845              : 
     846              : /* Find and return the last definition of INSN.  */
     847              : 
     848              : static def_info *
     849            0 : get_last_def (insn_info *insn)
     850              : {
     851            0 :   for (def_info *def : insn->defs ())
     852            0 :     if (def->insn () == insn)
     853            0 :       return def;
     854              : 
     855            0 :   return NULL;
     856              : }
     857              : 
     858              : /* Move uses of DEF to the previous definition.  */
     859              : 
     860              : static void
     861            0 : move_uses_to_prev_def (def_info *def)
     862              : {
     863            0 :   auto set = dyn_cast<set_info *> (def);
     864            0 :   while (set->first_use ())
     865              :     {
     866            0 :       auto prev_set = dyn_cast<set_info *> (def->prev_def ());
     867              :       if (!prev_set)
     868              :         break;
     869            0 :       crtl->ssa->reparent_use (set->first_use (), prev_set);
     870              :     }
     871            0 : }
     872              : 
     873              : /* Check if CHANGE exists in CHANGES.  */
     874              : 
     875              : static bool
     876           22 : change_in_vec_p (const auto_vec<change_info *> &changes,
     877              :                  const change_info &change)
     878              : {
     879          100 :   for (const change_info *other_change : changes)
     880           46 :     if (other_change->change->insn () == change.change->insn ())
     881              :       return true;
     882              : 
     883              :   return false;
     884              : }
     885              : 
     886              : /* Cancel current changes, clear CHANGES vector and update REMOVED_REGNOS.  */
     887              : static void
     888           36 : cancel_changes_for_group (int change_index, bitmap removed_regnos,
     889              :                           unsigned regno, int *min_index)
     890              : {
     891            1 :   if (*min_index == -1 || change_index < *min_index)
     892           35 :     *min_index = change_index;
     893           36 :   bitmap_set_bit (removed_regnos, regno);
     894            0 : }
     895              : 
     896              : /* Find the keys in CHANGES_MAP that need to be removed, based on
     897              :    CANCEL_MIN_INDEX and store them in KEYS_TO_REMOVE.  We do this by iterating
     898              :    the entries of the map recalculating the minimum index, until reaching a
     899              :    fixed-point.  */
     900              : 
     901              : static void
     902           35 : find_keys_to_remove (const hash_map<int_hash<unsigned, -1U, -2U>,
     903              :                               auto_vec<change_info *>> &changes_map,
     904              :                      bitmap keys_to_remove,
     905              :                      int *cancel_min_index)
     906              : {
     907           35 :   bool index_changed;
     908           70 :   do {
     909           35 :     index_changed = false;
     910           74 :     for (const auto &entry : changes_map)
     911              :       {
     912            2 :         int min_index = INT_MAX;
     913            2 :         bool cancelled_group = bitmap_bit_p (keys_to_remove, entry.first);
     914           10 :         for (change_info *change : entry.second)
     915              :           {
     916            4 :             int change_index = change->change_index;
     917            4 :             if (change_index < min_index)
     918              :               min_index = change_index;
     919              : 
     920            4 :             if (!cancelled_group && change_index >= *cancel_min_index)
     921              :               {
     922            1 :                 bitmap_set_bit (keys_to_remove, entry.first);
     923            1 :                 cancelled_group = true;
     924              :               }
     925              :           }
     926              : 
     927            2 :         if (cancelled_group && min_index < *cancel_min_index)
     928              :           {
     929            0 :             *cancel_min_index = min_index;
     930            0 :             index_changed = true;
     931            0 :             break;
     932              :           }
     933              :       }
     934              :   }
     935              :   while (index_changed);
     936           35 : }
     937              : 
     938              : /* Free all change_info objects (and their inner insn_change) owned by
     939              :    CHANGES_INFO.  Used on early returns in update_insns before ownership
     940              :    is transferred to changes_map.  */
     941              : 
     942              : static void
     943           36 : free_changes_info (auto_vec<change_info *> &changes_info)
     944              : {
     945          144 :   for (change_info *ci : changes_info)
     946              :     {
     947           36 :       delete ci->change;
     948           36 :       delete ci;
     949              :     }
     950           36 : }
     951              : 
     952              : /* Update the memory offsets and constants in fold insns based on the analysis
     953              :    done in fold_mem_offsets_1, using RTL SSA.  ATTEMPT is the attempt object
     954              :    for the current changes.  CHANGES_MAP holds the changes that are going
     955              :    to performed and is updated inside the function.  REMOVED_REGNOS holds the
     956              :    keys of the map that have been removed, in order to prevent new attempts
     957              :    on these.  */
     958              : static unsigned int
     959           52 : update_insns (fold_mem_info *info,
     960              :               obstack_watermark *attempt,
     961              :               hash_map<int_hash<unsigned, -1U, -2U>, auto_vec<change_info *>>
     962              :               *changes_map,
     963              :               bitmap removed_regnos,
     964              :               int *cancel_min_index)
     965              : {
     966           52 :   insn_info *insn = info->insn;
     967           52 :   unsigned int stats_fold_count = 0;
     968              : 
     969           52 :   auto_vec<change_info *> changes_info;
     970              : 
     971           52 :   insn_change *change = new insn_change (insn);
     972           52 :   change_info *change_inf = new change_info (change, num_validated_changes ());
     973           52 :   int change_index = change_inf->change_index;
     974           52 :   changes_info.safe_push (change_inf);
     975              : 
     976           52 :   if (info->fold_insns.is_empty ())
     977              :     {
     978            0 :       free_changes_info (changes_info);
     979            0 :       return stats_fold_count;
     980              :     }
     981              : 
     982           52 :   const rtx_insn *last_fold_insn_rtl = info->fold_insns.last ()->rtl ();
     983           52 :   rtx last_set = single_set (last_fold_insn_rtl);
     984           52 :   if (!last_set)
     985              :     {
     986            0 :       free_changes_info (changes_info);
     987            0 :       return stats_fold_count;
     988              :     }
     989           52 :   unsigned regno_key = REGNO (SET_DEST (last_set));
     990           52 :   auto_vec<change_info *> *prev_changes = changes_map->get (regno_key);
     991              : 
     992              :   /* Abort if changes for this key have been cancelled before.  */
     993           52 :   if (bitmap_bit_p (removed_regnos, regno_key))
     994              :     {
     995            1 :       cancel_changes_for_group (change_index, removed_regnos, regno_key,
     996              :                                 cancel_min_index);
     997            1 :       free_changes_info (changes_info);
     998            1 :       return stats_fold_count;
     999              :     }
    1000              : 
    1001              :   /* Keep a copy of insn_change elements only.  */
    1002          102 :   auto_vec<insn_change *> changes (changes_info.length ());
    1003          204 :   for (change_info *ci : changes_info)
    1004           51 :     changes.quick_push (ci->change);
    1005              : 
    1006          102 :   auto ignore = ignore_changing_insns (changes);
    1007           51 :   if (!rtl_ssa::restrict_movement (*change, ignore))
    1008              :     {
    1009            0 :       if (dump_file && (dump_flags & TDF_DETAILS))
    1010            0 :         fprintf (dump_file, "Restrict movement: Cannot update INSN %u.\n",
    1011              :                  insn->uid ());
    1012            0 :       cancel_changes_for_group (change_index, removed_regnos, regno_key,
    1013              :                                 cancel_min_index);
    1014            0 :       free_changes_info (changes_info);
    1015            0 :       return stats_fold_count;
    1016              :     }
    1017              : 
    1018           51 :   rtx new_insn = do_commit_offset (info);
    1019           51 :   if (new_insn == NULL_RTX)
    1020              :     {
    1021            0 :       free_changes_info (changes_info);
    1022            0 :       return stats_fold_count;
    1023              :     }
    1024              : 
    1025           51 :   rtx_insn *insn_rtl = info->insn->rtl ();
    1026           51 :   validate_change (insn_rtl, &PATTERN (insn_rtl), PATTERN (new_insn), 1);
    1027              : 
    1028              :   /* Check change validity and new instruction cost.  */
    1029           51 :   if (!recog (*attempt, *change, ignore)
    1030           45 :       || !changes_are_worthwhile (changes)
    1031           83 :       || !crtl->ssa->verify_insn_changes (changes))
    1032              :     {
    1033           35 :       if (dump_file && (dump_flags & TDF_DETAILS))
    1034            0 :         fprintf (dump_file, "Recog/verify: Cannot update INSN %u.\n",
    1035              :                  insn->uid ());
    1036           35 :       cancel_changes_for_group (change_index, removed_regnos, regno_key,
    1037              :                                 cancel_min_index);
    1038           35 :       free_changes_info (changes_info);
    1039           35 :       return stats_fold_count;
    1040              :     }
    1041              : 
    1042           16 :   if (dump_file)
    1043            0 :     fprintf (dump_file, "INSN %u: Memory offset changed from "
    1044              :          HOST_WIDE_INT_PRINT_DEC " to " HOST_WIDE_INT_PRINT_DEC ".\n",
    1045            0 :          insn->uid (), info->offset, info->offset + info->added_offset);
    1046              : 
    1047           32 :   while (!info->fold_insns.is_empty ())
    1048              :     {
    1049           16 :       insn_info *fold_insn = info->fold_insns.pop ();
    1050           16 :       rtx_insn *fold_insn_rtl = fold_insn->rtl ();
    1051              : 
    1052           16 :       rtx_insn *new_fold_insn = do_commit_insn (fold_insn, &changes_info);
    1053           16 :       if (!new_fold_insn)
    1054           10 :         continue;
    1055              : 
    1056            6 :       change_info *last_change = changes_info.last ();
    1057            6 :       changes.safe_push (last_change->change);
    1058              : 
    1059           12 :       std::sort (changes.begin (), changes.end (), sort_changes);
    1060              : 
    1061           12 :       auto ignore = ignore_changing_insns (changes);
    1062            6 :       if (!rtl_ssa::restrict_movement (*last_change->change, ignore))
    1063              :         {
    1064            0 :           if (dump_file && (dump_flags & TDF_DETAILS))
    1065            0 :             fprintf (dump_file, "Restrict movement: Cannot update INSN %u.\n",
    1066              :                      fold_insn->uid ());
    1067            0 :           cancel_changes_for_group (change_index, removed_regnos, regno_key,
    1068              :                                     cancel_min_index);
    1069            0 :           free_changes_info (changes_info);
    1070            0 :           return 0;
    1071              :         }
    1072              : 
    1073           12 :       if (!changes_are_worthwhile (changes)
    1074           12 :           || !crtl->ssa->verify_insn_changes (changes))
    1075              :         {
    1076            0 :           if (dump_file && (dump_flags & TDF_DETAILS))
    1077            0 :             fprintf (dump_file, "Verify: Cannot update INSN %u.\n",
    1078              :                      fold_insn->uid ());
    1079            0 :           cancel_changes_for_group (change_index, removed_regnos, regno_key,
    1080              :                                     cancel_min_index);
    1081            0 :           free_changes_info (changes_info);
    1082            0 :           return 0;
    1083              :         }
    1084              : 
    1085            6 :       if (last_change->change->is_deletion ())
    1086              :         {
    1087              :           /* Find last instruction's def.  */
    1088            0 :           def_info *insn_def = get_last_def (last_change->change->insn ());
    1089              : 
    1090              :           /* Move uses of deleted instruction to the previous def.  */
    1091            0 :           move_uses_to_prev_def (insn_def);
    1092              :         }
    1093              :       else
    1094              :         {
    1095            6 :           last_change->change_index = num_validated_changes ();
    1096            6 :           validate_change (fold_insn_rtl, &PATTERN (fold_insn_rtl),
    1097            6 :                            PATTERN (new_fold_insn), 1);
    1098            6 :           if (!recog (*attempt, *last_change->change, ignore))
    1099              :             {
    1100            0 :               if (dump_file && (dump_flags & TDF_DETAILS))
    1101            0 :                 fprintf (dump_file, "Recog: Cannot update INSN %u.\n",
    1102              :                          fold_insn->uid ());
    1103            0 :               cancel_changes_for_group (change_index, removed_regnos, regno_key,
    1104              :                                         cancel_min_index);
    1105            0 :               free_changes_info (changes_info);
    1106            0 :               return 0;
    1107              :             }
    1108              :         }
    1109              : 
    1110            6 :       if (dump_file)
    1111              :         {
    1112            0 :           const int last_change_uid = last_change->change->insn ()->uid ();
    1113            0 :           if (last_change->change->is_deletion ())
    1114            0 :             fprintf (dump_file, "INSN %u: Marked for deletion.\n",
    1115              :                      last_change_uid);
    1116              :           else
    1117            0 :             fprintf (dump_file, "INSN %u: Constant set to zero.\n",
    1118              :                      last_change_uid);
    1119              :         }
    1120              : 
    1121            6 :       stats_fold_count++;
    1122              :     }
    1123              : 
    1124              :   /* Add new changes to changes_map.  */
    1125           16 :   if (prev_changes)
    1126              :     {
    1127           40 :       for (change_info *change : changes_info)
    1128           10 :         if (!change_in_vec_p (*prev_changes, *change))
    1129           10 :           prev_changes->safe_push (change);
    1130              :         else
    1131              :           {
    1132            0 :             delete change->change;
    1133            0 :             delete change;
    1134              :           }
    1135              :     }
    1136              :   else
    1137           30 :     for (change_info *change : changes_info)
    1138              :       {
    1139           12 :         auto_vec<change_info *> &change_vect
    1140           12 :           = changes_map->get_or_insert (regno_key);
    1141              : 
    1142           12 :         if (!change_in_vec_p (change_vect, *change))
    1143           12 :           change_vect.safe_push (change);
    1144              :         else
    1145              :           {
    1146            0 :             delete change->change;
    1147            0 :             delete change;
    1148              :           }
    1149              :       }
    1150              : 
    1151              :   return stats_fold_count;
    1152           51 : }
    1153              : 
    1154              : /* Helper function for fold_mem_offsets.  Fold memory offsets by analysing the
    1155              :    DEF-USE chain.  If SINGLE_USE is true the DEFs will only have a single use,
    1156              :    otherwise they can have multiple uses.  */
    1157              : static unsigned int
    1158      1952962 : fold_mem_offsets_1 (bool single_use)
    1159              : {
    1160      1952962 :   unsigned int stats_fold_count = 0;
    1161              : 
    1162              :   /* This maps the instruction changes to the register defined by the last
    1163              :      fold_insn of the def-chain (the one nearest the fold-mem-offset root).
    1164              :      We use this so that we can group interdependent instructions.  In this
    1165              :      way, we can restrict the change cancellation in a group only, if anything
    1166              :      goes wrong.  */
    1167      1952962 :   hash_map<int_hash<unsigned, -1U, -2U>, auto_vec<change_info *>> changes_map;
    1168              : 
    1169      1952962 :   auto attempt = crtl->ssa->new_change_attempt ();
    1170      1952962 :   insn_change_watermark watermark;
    1171              : 
    1172              :   /* Set of removed reg numbers (keys to changes_map).  If a change for a reg
    1173              :      number has been cancelled, we need to invalidate any future changes.  */
    1174      1952962 :   auto_bitmap removed_regnos;
    1175              : 
    1176      1952962 :   int cancel_min_index = -1;
    1177              : 
    1178              :   /* Iterate over all nondebug INSNs get our candidates and fold them.  */
    1179      1952962 :   auto_vec<fold_mem_info *> worklist;
    1180    378707586 :   for (auto insn : iterate_safely (crtl->ssa->nondebug_insns ()))
    1181              :     {
    1182    188377312 :       if (!insn->is_real () || !insn->can_be_optimized ())
    1183    188375973 :         continue;
    1184              : 
    1185    119586972 :       rtx mem, reg;
    1186    119586972 :       HOST_WIDE_INT offset;
    1187    119586972 :       if (!get_fold_mem_offset_root (insn, &mem, &reg, &offset))
    1188     90688896 :         continue;
    1189              : 
    1190     28898076 :       fold_mem_info *info = new fold_mem_info (insn, mem, reg, offset);
    1191              : 
    1192     28898076 :       if (dump_file && (dump_flags & TDF_DETAILS))
    1193              :         {
    1194            0 :           fprintf (dump_file, "Starting analysis from root: ");
    1195            0 :           print_rtl_single (dump_file, info->insn->rtl ());
    1196              :         }
    1197              : 
    1198              :       /* Walk DEF-chain and collect info.fold_insns and the resulting
    1199              :          offset.  */
    1200     28898076 :       if (!fold_offsets (info->insn, info->reg, &info->added_offset, info,
    1201              :                          single_use)
    1202     28898076 :           || info->added_offset == 0)
    1203              :         {
    1204     57793474 :           delete info;
    1205     28896737 :           continue;
    1206              :         }
    1207              : 
    1208         1339 :       if (dump_file && (dump_flags & TDF_DETAILS))
    1209            0 :         fprintf (dump_file,
    1210              :                  "Found root offset delta: " HOST_WIDE_INT_PRINT_DEC "\n",
    1211              :                  info->added_offset);
    1212              : 
    1213         1339 :       if (single_use)
    1214              :         {
    1215           20 :           stats_fold_count += update_insns (info, &attempt, &changes_map,
    1216              :                                             removed_regnos, &cancel_min_index);
    1217           40 :           delete info;
    1218              :         }
    1219              :       else
    1220              :         /* Append candidate.  */
    1221         1319 :         worklist.safe_push (info);
    1222              :     }
    1223              : 
    1224      1952962 :   if (!single_use)
    1225              :     {
    1226              :       /* Now drop all fold_mem_infos, which contain INSNs that have unknown
    1227              :          USEs and are therefore not safe to change.  */
    1228       976481 :       drop_unsafe_candidates (&worklist);
    1229              : 
    1230      1952994 :       while (!worklist.is_empty ())
    1231              :         {
    1232           32 :           fold_mem_info *info = worklist.pop ();
    1233           32 :           stats_fold_count += update_insns (info, &attempt, &changes_map,
    1234              :                                             removed_regnos, &cancel_min_index);
    1235           64 :           delete info;
    1236              :         }
    1237              :     }
    1238              : 
    1239              :   /* In case that instructions have been cancelled, remove related
    1240              :      instructions from the map and find the minimum index to use in
    1241              :      cancel_changes.  */
    1242      1952962 :   if (cancel_min_index != -1)
    1243              :     {
    1244           35 :       find_keys_to_remove (changes_map, removed_regnos, &cancel_min_index);
    1245              : 
    1246           35 :       bitmap_iterator bi;
    1247           35 :       unsigned int key;
    1248           71 :       EXECUTE_IF_SET_IN_BITMAP (removed_regnos, 0, key, bi)
    1249              :         {
    1250           36 :           auto_vec<change_info *> *changes = changes_map.get (key);
    1251           36 :           if (changes)
    1252              :             {
    1253            5 :               for (change_info *change : *changes)
    1254              :                 {
    1255            2 :                   if (dump_file)
    1256            0 :                     fprintf (dump_file, "Change cancelled for insn %u.\n",
    1257            0 :                              change->change->insn ()->uid ());
    1258            2 :                   delete change->change;
    1259            2 :                   delete change;
    1260              :                 }
    1261              :             }
    1262           36 :           changes_map.remove (key);
    1263              :         }
    1264              : 
    1265           35 :       cancel_changes (cancel_min_index);
    1266              :     }
    1267              : 
    1268      1952962 :   if (cancel_min_index != 0)
    1269      1952928 :     confirm_change_group ();
    1270              : 
    1271              :   /* Copy the map into a vector and sort it for traversal.  */
    1272      1952962 :   unsigned int map_entries_num = changes_map.elements ();
    1273      1952962 :   auto_vec<regno_changes> regno_changes_vec (map_entries_num);
    1274              : 
    1275      1952967 :   for (auto entry : changes_map)
    1276              :     {
    1277            5 :       auto_vec<insn_change *> *changes_vec
    1278           10 :          = new auto_vec<insn_change *> (entry.second.length ());
    1279              : 
    1280           35 :       for (change_info *change : entry.second)
    1281           20 :         changes_vec->quick_push (change->change);
    1282              : 
    1283            5 :       regno_changes rc;
    1284            5 :       rc.regno = static_cast<unsigned> (entry.first);
    1285            5 :       rc.changes = changes_vec;
    1286            5 :       regno_changes_vec.quick_push (rc);
    1287              :     }
    1288              : 
    1289      1952962 :   regno_changes_vec.qsort (sort_pairs);
    1290              : 
    1291      1952977 :   for (const auto &rc : regno_changes_vec)
    1292              :     {
    1293            5 :       auto_vec<insn_change *> &changes = *rc.changes;
    1294              : 
    1295              :       /* Skip already deleted instructions.  */
    1296           10 :       auto_vec<insn_change *> live_changes (changes.length ());
    1297           35 :       for (insn_change *change : changes)
    1298           20 :         if (change->insn ()->has_been_deleted ())
    1299            0 :           delete change;
    1300              :         else
    1301           20 :           live_changes.quick_push (change);
    1302              : 
    1303           10 :       std::sort (live_changes.begin (), live_changes.end (), sort_changes);
    1304           10 :       crtl->ssa->change_insns (live_changes);
    1305              : 
    1306           30 :       for (insn_change *change : live_changes)
    1307           20 :         delete change;
    1308              : 
    1309           10 :       delete rc.changes;
    1310            5 :     }
    1311              : 
    1312              :   /* Free the change_info wrappers for successful (non-cancelled) entries.
    1313              :      Their inner insn_change has already been deleted above.  Cancelled
    1314              :      entries were removed from changes_map and freed earlier.  */
    1315      1952972 :   for (auto entry : changes_map)
    1316           35 :     for (change_info *ci : entry.second)
    1317           20 :       delete ci;
    1318              : 
    1319      1952962 :   return stats_fold_count;
    1320      1952962 : }
    1321              : 
    1322              : /* Main function of fold-mem-offsets pass.  */
    1323              : static unsigned int
    1324       976481 : fold_mem_offsets (function *fn)
    1325              : {
    1326       976481 :   bool multi_use_mode = true;
    1327              : 
    1328              :   /* Analysing every USE of a DEF (multi-use mode, below) is expensive on
    1329              :      flow graphs which have a high connectivity and is unlikely to be
    1330              :      particularly useful there, so gate it on the edge count.
    1331              : 
    1332              :      In normal circumstances a cfg should have about twice as many
    1333              :      edges as blocks.  But we do not want to punish small functions
    1334              :      which have a couple switch statements.  Rather than simply
    1335              :      threshold the number of blocks, use something with a more
    1336              :      graceful degradation.  */
    1337       976481 :   if (n_edges_for_fn (fn) > 20000 + n_basic_blocks_for_fn (fn) * 4)
    1338            0 :     multi_use_mode = false;
    1339              : 
    1340              :   /* Initialize RTL SSA.  */
    1341       976481 :   calculate_dominance_info (CDI_DOMINATORS);
    1342       976481 :   df_analyze ();
    1343       976481 :   crtl->ssa = new rtl_ssa::function_info (cfun);
    1344              : 
    1345              :   /* The number of instructions that were simplified or eliminated.  */
    1346       976481 :   int stats_fold_count = 0;
    1347              : 
    1348              :   /* Fold mem offsets with DEFs that have a single USE.  */
    1349       976481 :   stats_fold_count += fold_mem_offsets_1 (true);
    1350              : 
    1351              :   /* Fold mem offsets with DEFs that have multiple USEs.  This expensive
    1352              :      analysis is skipped for highly-connected CFGs, unless forced on with
    1353              :      -fexpensive-optimizations.  As the latter is enabled by default at -O2,
    1354              :      the skip only takes effect under -fno-expensive-optimizations.  */
    1355       976481 :   if (multi_use_mode || flag_expensive_optimizations)
    1356              :     {
    1357       976481 :       if (dump_file)
    1358           22 :         fprintf (dump_file, "Starting multi-use analysis\n");
    1359       976481 :       stats_fold_count += fold_mem_offsets_1 (false);
    1360              :     }
    1361              :   else
    1362            0 :     warning (OPT_Wdisabled_optimization,
    1363              :              "fold-mem-offsets: disabling multi-use analysis for %d basic "
    1364              :              "blocks and %d edges/basic block",
    1365              :              n_basic_blocks_for_fn (fn),
    1366            0 :              n_edges_for_fn (fn) / n_basic_blocks_for_fn (fn));
    1367              : 
    1368       976481 :   statistics_counter_event (cfun, "Number of folded instructions",
    1369              :                             stats_fold_count);
    1370              : 
    1371       976481 :   free_dominance_info (CDI_DOMINATORS);
    1372       976481 :   if (crtl->ssa->perform_pending_updates ())
    1373            0 :     cleanup_cfg (0);
    1374              : 
    1375       976481 :   delete crtl->ssa;
    1376       976481 :   crtl->ssa = nullptr;
    1377              : 
    1378       976481 :   return 0;
    1379              : }
    1380              : 
    1381              : const pass_data pass_data_fold_mem =
    1382              : {
    1383              :   RTL_PASS, /* type */
    1384              :   "fold_mem_offsets", /* name */
    1385              :   OPTGROUP_NONE, /* optinfo_flags */
    1386              :   TV_FOLD_MEM_OFFSETS, /* tv_id */
    1387              :   0, /* properties_required */
    1388              :   0, /* properties_provided */
    1389              :   0, /* properties_destroyed */
    1390              :   0, /* todo_flags_start */
    1391              :   TODO_df_finish, /* todo_flags_finish */
    1392              : };
    1393              : 
    1394              : class pass_fold_mem_offsets : public rtl_opt_pass
    1395              : {
    1396              : public:
    1397       292371 :   pass_fold_mem_offsets (gcc::context *ctxt)
    1398       584742 :     : rtl_opt_pass (pass_data_fold_mem, ctxt)
    1399              :   {}
    1400              : 
    1401              :   /* opt_pass methods: */
    1402      1504958 :   bool gate (function *) final override
    1403              :     {
    1404      1504958 :       return flag_fold_mem_offsets && optimize >= 2;
    1405              :     }
    1406              : 
    1407       976481 :   unsigned int execute (function *fn) final override
    1408              :     {
    1409       976481 :       return fold_mem_offsets (fn);
    1410              :     }
    1411              : }; // class pass_fold_mem_offsets
    1412              : 
    1413              : } // anon namespace
    1414              : 
    1415              : rtl_opt_pass *
    1416       292371 : make_pass_fold_mem_offsets (gcc::context *ctxt)
    1417              : {
    1418       292371 :   return new pass_fold_mem_offsets (ctxt);
    1419              : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.