LCOV - code coverage report
Current view: top level - gcc - gimple-harden-conditionals.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 83.9 % 249 209
Test Date: 2024-04-20 14:03:02 Functions: 83.3 % 12 10
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Harden conditionals.
       2                 :             :    Copyright (C) 2021-2024 Free Software Foundation, Inc.
       3                 :             :    Contributed by Alexandre Oliva <oliva@adacore.com>.
       4                 :             : 
       5                 :             : This file is part of GCC.
       6                 :             : 
       7                 :             : GCC is free software; you can redistribute it and/or modify it under
       8                 :             : the terms of the GNU General Public License as published by the Free
       9                 :             : Software Foundation; either version 3, or (at your option) any later
      10                 :             : version.
      11                 :             : 
      12                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15                 :             : for more details.
      16                 :             : 
      17                 :             : You should have received a copy of the GNU General Public License
      18                 :             : along with GCC; see the file COPYING3.  If not see
      19                 :             : <http://www.gnu.org/licenses/>.  */
      20                 :             : 
      21                 :             : #include "config.h"
      22                 :             : #include "system.h"
      23                 :             : #include "coretypes.h"
      24                 :             : #include "backend.h"
      25                 :             : #include "target.h"
      26                 :             : #include "rtl.h"
      27                 :             : #include "tree.h"
      28                 :             : #include "fold-const.h"
      29                 :             : #include "gimple.h"
      30                 :             : #include "gimplify.h"
      31                 :             : #include "tree-pass.h"
      32                 :             : #include "ssa.h"
      33                 :             : #include "gimple-iterator.h"
      34                 :             : #include "tree-cfg.h"
      35                 :             : #include "basic-block.h"
      36                 :             : #include "cfghooks.h"
      37                 :             : #include "cfgloop.h"
      38                 :             : #include "tree-eh.h"
      39                 :             : #include "sbitmap.h"
      40                 :             : #include "diagnostic.h"
      41                 :             : #include "intl.h"
      42                 :             : 
      43                 :             : namespace {
      44                 :             : 
      45                 :             : /* These passes introduces redundant, but reversed conditionals at
      46                 :             :    compares, such as those used in conditional branches, and those
      47                 :             :    that compute boolean results.  This doesn't make much sense for
      48                 :             :    abstract CPUs, but this kind of hardening may avoid undesirable
      49                 :             :    execution paths on actual CPUs under such attacks as of power
      50                 :             :    deprivation.  */
      51                 :             : 
      52                 :             : /* Define a pass to harden conditionals other than branches.  */
      53                 :             : 
      54                 :             : const pass_data pass_data_harden_compares = {
      55                 :             :   GIMPLE_PASS,
      56                 :             :   "hardcmp",
      57                 :             :   OPTGROUP_NONE,
      58                 :             :   TV_NONE,
      59                 :             :   PROP_cfg | PROP_ssa, // properties_required
      60                 :             :   0,        // properties_provided
      61                 :             :   0,        // properties_destroyed
      62                 :             :   0,        // properties_start
      63                 :             :   TODO_update_ssa
      64                 :             :   | TODO_cleanup_cfg
      65                 :             :   | TODO_verify_il, // properties_finish
      66                 :             : };
      67                 :             : 
      68                 :             : class pass_harden_compares : public gimple_opt_pass
      69                 :             : {
      70                 :             : public:
      71                 :      281914 :   pass_harden_compares (gcc::context *ctxt)
      72                 :      563828 :     : gimple_opt_pass (pass_data_harden_compares, ctxt)
      73                 :             :   {}
      74                 :           0 :   opt_pass *clone ()  final override
      75                 :             :   {
      76                 :           0 :     return new pass_harden_compares (m_ctxt);
      77                 :             :   }
      78                 :     1427379 :   bool gate (function *) final override
      79                 :             :   {
      80                 :     1427379 :     return flag_harden_compares;
      81                 :             :   }
      82                 :             :   unsigned int execute (function *) final override;
      83                 :             : };
      84                 :             : 
      85                 :             : /* Define a pass to harden conditionals in branches.  This pass must
      86                 :             :    run after the above, otherwise it will re-harden the checks
      87                 :             :    introduced by the above.  */
      88                 :             : 
      89                 :             : const pass_data pass_data_harden_conditional_branches = {
      90                 :             :   GIMPLE_PASS,
      91                 :             :   "hardcbr",
      92                 :             :   OPTGROUP_NONE,
      93                 :             :   TV_NONE,
      94                 :             :   PROP_cfg | PROP_ssa, // properties_required
      95                 :             :   0,        // properties_provided
      96                 :             :   0,        // properties_destroyed
      97                 :             :   0,        // properties_start
      98                 :             :   TODO_update_ssa
      99                 :             :   | TODO_cleanup_cfg
     100                 :             :   | TODO_verify_il, // properties_finish
     101                 :             : };
     102                 :             : 
     103                 :             : class pass_harden_conditional_branches : public gimple_opt_pass
     104                 :             : {
     105                 :             : public:
     106                 :      281914 :   pass_harden_conditional_branches (gcc::context *ctxt)
     107                 :      563828 :     : gimple_opt_pass (pass_data_harden_conditional_branches, ctxt)
     108                 :             :   {}
     109                 :           0 :   opt_pass *clone () final override
     110                 :             :   {
     111                 :           0 :     return new pass_harden_conditional_branches (m_ctxt);
     112                 :             :   }
     113                 :     1427379 :   bool gate (function *) final override
     114                 :             :   {
     115                 :     1427379 :     return flag_harden_conditional_branches;
     116                 :             :   }
     117                 :             :   unsigned int execute (function *) final override;
     118                 :             : };
     119                 :             : 
     120                 :             : }
     121                 :             : 
     122                 :             : /* If VAL is an SSA name, return an SSA name holding the same value,
     123                 :             :    but without the compiler's knowing that it holds the same value, so
     124                 :             :    that uses thereof can't be optimized the way VAL might.  Insert
     125                 :             :    stmts that initialize it before *GSIP, with LOC.
     126                 :             : 
     127                 :             :    Otherwise, VAL must be an invariant, returned unchanged.  */
     128                 :             : 
     129                 :             : static inline tree
     130                 :         234 : detach_value (location_t loc, gimple_stmt_iterator *gsip, tree val)
     131                 :             : {
     132                 :         234 :   if (TREE_CONSTANT (val) || TREE_CODE (val) != SSA_NAME)
     133                 :             :     {
     134                 :          84 :       gcc_checking_assert (is_gimple_min_invariant (val));
     135                 :             :       return val;
     136                 :             :     }
     137                 :             : 
     138                 :             :   /* Create a SSA "copy" of VAL.  It would be nice to have it named
     139                 :             :      after the corresponding variable, but sharing the same decl is
     140                 :             :      problematic when VAL is a DECL_BY_REFERENCE RESULT_DECL, and
     141                 :             :      copying just the identifier hits -fcompare-debug failures.  */
     142                 :         150 :   tree ret = make_ssa_name (TREE_TYPE (val));
     143                 :             : 
     144                 :             :   /* Some modes won't fit in general regs, so we fall back to memory
     145                 :             :      for them.  ??? It would be ideal to try to identify an alternate,
     146                 :             :      wider or more suitable register class, and use the corresponding
     147                 :             :      constraint, but there's no logic to go from register class to
     148                 :             :      constraint, even if there is a corresponding constraint, and even
     149                 :             :      if we could enumerate constraints, we can't get to their string
     150                 :             :      either.  So this will do for now.  */
     151                 :         150 :   bool need_memory = true;
     152                 :         150 :   enum machine_mode mode = TYPE_MODE (TREE_TYPE (val));
     153                 :         150 :   if (mode != BLKmode)
     154                 :         150 :     for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     155                 :         150 :       if (TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], i)
     156                 :         150 :           && targetm.hard_regno_mode_ok (i, mode))
     157                 :             :         {
     158                 :             :           need_memory = false;
     159                 :             :           break;
     160                 :             :         }
     161                 :             : 
     162                 :         150 :   tree asminput = val;
     163                 :         150 :   tree asmoutput = ret;
     164                 :         150 :   const char *constraint_out = need_memory ? "=m" : "=g";
     165                 :         150 :   const char *constraint_in = need_memory ? "m" : "0";
     166                 :             : 
     167                 :           0 :   if (need_memory)
     168                 :             :     {
     169                 :           0 :       tree temp = create_tmp_var (TREE_TYPE (val), "dtch");
     170                 :           0 :       mark_addressable (temp);
     171                 :             : 
     172                 :           0 :       gassign *copyin = gimple_build_assign (temp, asminput);
     173                 :           0 :       gimple_set_location (copyin, loc);
     174                 :           0 :       gsi_insert_before (gsip, copyin, GSI_SAME_STMT);
     175                 :             : 
     176                 :           0 :       asminput = asmoutput = temp;
     177                 :             :     }
     178                 :             : 
     179                 :             :   /* Output an asm statement with matching input and output.  It does
     180                 :             :      nothing, but after it the compiler no longer knows the output
     181                 :             :      still holds the same value as the input.  */
     182                 :         150 :   vec<tree, va_gc> *inputs = NULL;
     183                 :         150 :   vec<tree, va_gc> *outputs = NULL;
     184                 :         300 :   vec_safe_push (outputs,
     185                 :             :                  build_tree_list
     186                 :         150 :                  (build_tree_list
     187                 :             :                   (NULL_TREE, build_string (strlen (constraint_out),
     188                 :             :                                             constraint_out)),
     189                 :             :                   asmoutput));
     190                 :         300 :   vec_safe_push (inputs,
     191                 :             :                  build_tree_list
     192                 :         150 :                  (build_tree_list
     193                 :             :                   (NULL_TREE, build_string (strlen (constraint_in),
     194                 :             :                                             constraint_in)),
     195                 :             :                   asminput));
     196                 :         150 :   gasm *detach = gimple_build_asm_vec ("", inputs, outputs,
     197                 :             :                                        NULL, NULL);
     198                 :         150 :   gimple_set_location (detach, loc);
     199                 :         150 :   gsi_insert_before (gsip, detach, GSI_SAME_STMT);
     200                 :             : 
     201                 :         150 :   if (need_memory)
     202                 :             :     {
     203                 :           0 :       gassign *copyout = gimple_build_assign (ret, asmoutput);
     204                 :           0 :       gimple_set_location (copyout, loc);
     205                 :           0 :       gsi_insert_before (gsip, copyout, GSI_SAME_STMT);
     206                 :           0 :       SSA_NAME_DEF_STMT (ret) = copyout;
     207                 :             : 
     208                 :           0 :       gassign *clobber = gimple_build_assign (asmoutput,
     209                 :             :                                               build_clobber
     210                 :           0 :                                               (TREE_TYPE (asmoutput)));
     211                 :           0 :       gimple_set_location (clobber, loc);
     212                 :           0 :       gsi_insert_before (gsip, clobber, GSI_SAME_STMT);
     213                 :             :     }
     214                 :             :   else
     215                 :         150 :     SSA_NAME_DEF_STMT (ret) = detach;
     216                 :             : 
     217                 :             :   return ret;
     218                 :             : }
     219                 :             : 
     220                 :             : /* Build a cond stmt out of COP, LHS, RHS, insert it before *GSIP with
     221                 :             :    location LOC.  *GSIP must be at the end of a basic block.  The succ
     222                 :             :    edge out of the block becomes the true or false edge opposite to
     223                 :             :    that in FLAGS.  Create a new block with a single trap stmt, in the
     224                 :             :    cold partition if the function is partitioned,, and a new edge to
     225                 :             :    it as the other edge for the cond.  */
     226                 :             : 
     227                 :             : static inline void
     228                 :         163 : insert_check_and_trap (location_t loc, gimple_stmt_iterator *gsip,
     229                 :             :                        int flags, enum tree_code cop, tree lhs, tree rhs)
     230                 :             : {
     231                 :         163 :   basic_block chk = gsi_bb (*gsip);
     232                 :             : 
     233                 :         163 :   gcond *cond = gimple_build_cond (cop, lhs, rhs, NULL, NULL);
     234                 :         163 :   gimple_set_location (cond, loc);
     235                 :         163 :   gsi_insert_before (gsip, cond, GSI_SAME_STMT);
     236                 :             : 
     237                 :         163 :   basic_block trp = create_empty_bb (chk);
     238                 :         163 :   trp->count = profile_count::zero ();
     239                 :             : 
     240                 :         163 :   gimple_stmt_iterator gsit = gsi_after_labels (trp);
     241                 :         163 :   gcall *trap = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
     242                 :         163 :   gimple_call_set_ctrl_altering (trap, true);
     243                 :         163 :   gimple_set_location (trap, loc);
     244                 :         163 :   gsi_insert_before (&gsit, trap, GSI_SAME_STMT);
     245                 :             : 
     246                 :         163 :   if (dump_file)
     247                 :          98 :     fprintf (dump_file,
     248                 :             :              "Adding reversed compare to block %i, and trap to block %i\n",
     249                 :             :              chk->index, trp->index);
     250                 :             : 
     251                 :         163 :   if (BB_PARTITION (chk))
     252                 :           0 :     BB_SET_PARTITION (trp, BB_COLD_PARTITION);
     253                 :             : 
     254                 :         163 :   int true_false_flag = flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
     255                 :         163 :   gcc_assert (true_false_flag);
     256                 :         163 :   int neg_true_false_flag = (~flags) & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
     257                 :             : 
     258                 :             :   /* Remove the fallthru bit, and set the truth value for the
     259                 :             :      preexisting edge and for the newly-created one.  In hardcbr,
     260                 :             :      FLAGS is taken from the edge of the original cond expr that we're
     261                 :             :      dealing with, so the reversed compare is expected to yield the
     262                 :             :      negated result, and the same result calls for a trap.  In
     263                 :             :      hardcmp, we're comparing the boolean results of the original and
     264                 :             :      of the reversed compare, so we're passed FLAGS to trap on
     265                 :             :      equality.  */
     266                 :         163 :   single_succ_edge (chk)->flags &= ~EDGE_FALLTHRU;
     267                 :         163 :   single_succ_edge (chk)->flags |= neg_true_false_flag;
     268                 :         163 :   single_succ_edge (chk)->probability = profile_probability::always ();
     269                 :         163 :   edge e = make_edge (chk, trp, true_false_flag);
     270                 :         163 :   e->goto_locus = loc;
     271                 :         163 :   e->probability = profile_probability::never ();
     272                 :             : 
     273                 :         163 :   if (dom_info_available_p (CDI_DOMINATORS))
     274                 :         163 :     set_immediate_dominator (CDI_DOMINATORS, trp, chk);
     275                 :         163 :   if (current_loops)
     276                 :         163 :     add_bb_to_loop (trp, current_loops->tree_root);
     277                 :         163 : }
     278                 :             : 
     279                 :             : /* Split edge E, and insert_check_and_trap (see above) in the
     280                 :             :    newly-created block, using already-detached copies of LHS's and
     281                 :             :    RHS's values (see detach_value above) for the COP compare.  */
     282                 :             : 
     283                 :             : static inline void
     284                 :          92 : insert_edge_check_and_trap (location_t loc, edge e,
     285                 :             :                             enum tree_code cop, tree lhs, tree rhs)
     286                 :             : {
     287                 :          92 :   int flags = e->flags;
     288                 :          92 :   basic_block src = e->src;
     289                 :          92 :   basic_block dest = e->dest;
     290                 :          92 :   location_t eloc = e->goto_locus;
     291                 :             : 
     292                 :          92 :   basic_block chk = split_edge (e);
     293                 :          92 :   e = NULL;
     294                 :             : 
     295                 :          92 :   single_pred_edge (chk)->goto_locus = loc;
     296                 :          92 :   single_succ_edge (chk)->goto_locus = eloc;
     297                 :             : 
     298                 :          92 :   if (dump_file)
     299                 :          56 :     fprintf (dump_file,
     300                 :             :              "Splitting edge %i->%i into block %i\n",
     301                 :             :              src->index, dest->index, chk->index);
     302                 :             : 
     303                 :          92 :   gimple_stmt_iterator gsik = gsi_after_labels (chk);
     304                 :             : 
     305                 :          92 :   insert_check_and_trap (loc, &gsik, flags, cop, lhs, rhs);
     306                 :          92 : }
     307                 :             : 
     308                 :             : /* Harden cond stmts at the end of FUN's blocks.  */
     309                 :             : 
     310                 :             : unsigned int
     311                 :          41 : pass_harden_conditional_branches::execute (function *fun)
     312                 :             : {
     313                 :             :   /* Record the preexisting blocks, to avoid visiting newly-created
     314                 :             :      blocks.  */
     315                 :          41 :   auto_sbitmap to_visit (last_basic_block_for_fn (fun));
     316                 :          41 :   bitmap_clear (to_visit);
     317                 :             : 
     318                 :          41 :   basic_block bb;
     319                 :         227 :   FOR_EACH_BB_FN (bb, fun)
     320                 :         186 :     bitmap_set_bit (to_visit, bb->index);
     321                 :             : 
     322                 :          41 :   sbitmap_iterator it;
     323                 :          41 :   unsigned i;
     324                 :         268 :   EXECUTE_IF_SET_IN_BITMAP (to_visit, 0, i, it)
     325                 :             :     {
     326                 :         186 :       bb = BASIC_BLOCK_FOR_FN (fun, i);
     327                 :             : 
     328                 :         186 :       gimple_stmt_iterator gsi = gsi_last_bb (bb);
     329                 :             : 
     330                 :         186 :       if (gsi_end_p (gsi))
     331                 :         140 :         continue;
     332                 :             : 
     333                 :         161 :       gcond *cond = dyn_cast <gcond *> (gsi_stmt (gsi));
     334                 :         161 :       if (!cond)
     335                 :         115 :         continue;
     336                 :             : 
     337                 :             :       /* Turn:
     338                 :             : 
     339                 :             :          if (x op y) goto l1; else goto l2;
     340                 :             : 
     341                 :             :          into:
     342                 :             : 
     343                 :             :          if (x op y) goto l1'; else goto l2';
     344                 :             :          l1': if (x' cop y') goto l1'trap; else goto l1;
     345                 :             :          l1'trap: __builtin_trap ();
     346                 :             :          l2': if (x' cop y') goto l2; else goto l2'trap;
     347                 :             :          l2'trap: __builtin_trap ();
     348                 :             : 
     349                 :             :          where cop is a complementary boolean operation to op; l1', l1'trap,
     350                 :             :          l2' and l2'trap are newly-created labels; and x' and y' hold the same
     351                 :             :          value as x and y, but in a way that does not enable the compiler to
     352                 :             :          optimize the redundant compare away.
     353                 :             :       */
     354                 :             : 
     355                 :          46 :       enum tree_code op = gimple_cond_code (cond);
     356                 :          46 :       tree lhs = gimple_cond_lhs (cond);
     357                 :          46 :       tree rhs = gimple_cond_rhs (cond);
     358                 :          46 :       location_t loc = gimple_location (cond);
     359                 :             : 
     360                 :          46 :       enum tree_code cop = invert_tree_comparison (op, HONOR_NANS (lhs));
     361                 :             : 
     362                 :          46 :       if (cop == ERROR_MARK)
     363                 :             :         /* ??? Can we do better?  */
     364                 :           0 :         continue;
     365                 :             : 
     366                 :             :       /* Detach the values before the compares.  If we do so later,
     367                 :             :          the compiler may use values inferred from the compares.  */
     368                 :          46 :       bool same_p = (lhs == rhs);
     369                 :          46 :       lhs = detach_value (loc, &gsi, lhs);
     370                 :          46 :       rhs = same_p ? lhs : detach_value (loc, &gsi, rhs);
     371                 :             : 
     372                 :          46 :       insert_edge_check_and_trap (loc, EDGE_SUCC (bb, 0), cop, lhs, rhs);
     373                 :          46 :       insert_edge_check_and_trap (loc, EDGE_SUCC (bb, 1), cop, lhs, rhs);
     374                 :             :     }
     375                 :             : 
     376                 :          41 :   return 0;
     377                 :          41 : }
     378                 :             : 
     379                 :             : /* Instantiate a hardcbr pass.  */
     380                 :             : 
     381                 :             : gimple_opt_pass *
     382                 :      281914 : make_pass_harden_conditional_branches (gcc::context *ctxt)
     383                 :             : {
     384                 :      281914 :   return new pass_harden_conditional_branches (ctxt);
     385                 :             : }
     386                 :             : 
     387                 :             : /* Return the fallthru edge of a block whose other edge is an EH
     388                 :             :    edge.  If EHP is not NULL, store the EH edge in it.  */
     389                 :             : static inline edge
     390                 :           8 : non_eh_succ_edge (basic_block bb, edge *ehp = NULL)
     391                 :             : {
     392                 :           8 :   gcc_checking_assert (EDGE_COUNT (bb->succs) == 2);
     393                 :             : 
     394                 :           8 :   edge ret = find_fallthru_edge (bb->succs);
     395                 :             : 
     396                 :           8 :   int eh_idx = EDGE_SUCC (bb, 0) == ret;
     397                 :           8 :   edge eh = EDGE_SUCC (bb, eh_idx);
     398                 :             : 
     399                 :           8 :   gcc_checking_assert (!(ret->flags & EDGE_EH)
     400                 :             :                        && (eh->flags & EDGE_EH));
     401                 :             : 
     402                 :           8 :   if (ehp)
     403                 :           4 :     *ehp = eh;
     404                 :             : 
     405                 :           8 :   return ret;
     406                 :             : }
     407                 :             : 
     408                 :             : /* Harden boolean-yielding compares in FUN.  */
     409                 :             : 
     410                 :             : unsigned int
     411                 :          53 : pass_harden_compares::execute (function *fun)
     412                 :             : {
     413                 :             :   /* Record the preexisting blocks, to avoid visiting newly-created
     414                 :             :      blocks.  */
     415                 :          53 :   auto_sbitmap to_visit (last_basic_block_for_fn (fun));
     416                 :          53 :   bitmap_clear (to_visit);
     417                 :             : 
     418                 :          53 :   basic_block bb;
     419                 :         305 :   FOR_EACH_BB_FN (bb, fun)
     420                 :         252 :     bitmap_set_bit (to_visit, bb->index);
     421                 :             : 
     422                 :          53 :   sbitmap_iterator it;
     423                 :          53 :   unsigned i;
     424                 :         358 :   EXECUTE_IF_SET_IN_BITMAP (to_visit, 0, i, it)
     425                 :             :     {
     426                 :         252 :       bb = BASIC_BLOCK_FOR_FN (fun, i);
     427                 :             : 
     428                 :         504 :       for (gimple_stmt_iterator gsi = gsi_last_bb (bb);
     429                 :        1520 :            !gsi_end_p (gsi); gsi_prev (&gsi))
     430                 :             :         {
     431                 :         634 :           gassign *asgn = dyn_cast <gassign *> (gsi_stmt (gsi));
     432                 :         634 :           if (!asgn)
     433                 :         563 :             continue;
     434                 :             : 
     435                 :             :           /* Turn:
     436                 :             : 
     437                 :             :              z = x op y;
     438                 :             : 
     439                 :             :              into:
     440                 :             : 
     441                 :             :              z = x op y;
     442                 :             :              z' = x' cop y';
     443                 :             :              if (z == z') __builtin_trap ();
     444                 :             : 
     445                 :             :              where cop is a complementary boolean operation to op; and x'
     446                 :             :              and y' hold the same value as x and y, but in a way that does
     447                 :             :              not enable the compiler to optimize the redundant compare
     448                 :             :              away.
     449                 :             :           */
     450                 :             : 
     451                 :         292 :           enum tree_code op = gimple_assign_rhs_code (asgn);
     452                 :             : 
     453                 :         292 :           enum tree_code cop;
     454                 :             : 
     455                 :         292 :           switch (op)
     456                 :             :             {
     457                 :          71 :             case EQ_EXPR:
     458                 :          71 :             case NE_EXPR:
     459                 :          71 :             case GT_EXPR:
     460                 :          71 :             case GE_EXPR:
     461                 :          71 :             case LT_EXPR:
     462                 :          71 :             case LE_EXPR:
     463                 :          71 :             case LTGT_EXPR:
     464                 :          71 :             case UNEQ_EXPR:
     465                 :          71 :             case UNGT_EXPR:
     466                 :          71 :             case UNGE_EXPR:
     467                 :          71 :             case UNLT_EXPR:
     468                 :          71 :             case UNLE_EXPR:
     469                 :          71 :             case ORDERED_EXPR:
     470                 :          71 :             case UNORDERED_EXPR:
     471                 :          71 :               cop = invert_tree_comparison (op,
     472                 :             :                                             HONOR_NANS
     473                 :          71 :                                             (gimple_assign_rhs1 (asgn)));
     474                 :             : 
     475                 :          71 :               if (cop == ERROR_MARK)
     476                 :             :                 /* ??? Can we do better?  */
     477                 :           0 :                 continue;
     478                 :             : 
     479                 :          71 :               break;
     480                 :             : 
     481                 :             :               /* ??? Maybe handle these too?  */
     482                 :         221 :             case TRUTH_NOT_EXPR:
     483                 :             :               /* ??? The code below assumes binary ops, it would have to
     484                 :             :                  be adjusted for TRUTH_NOT_EXPR, since it's unary.  */
     485                 :         221 :             case TRUTH_ANDIF_EXPR:
     486                 :         221 :             case TRUTH_ORIF_EXPR:
     487                 :         221 :             case TRUTH_AND_EXPR:
     488                 :         221 :             case TRUTH_OR_EXPR:
     489                 :         221 :             case TRUTH_XOR_EXPR:
     490                 :         221 :             default:
     491                 :         221 :               continue;
     492                 :             :             }
     493                 :             : 
     494                 :             :           /* These are the operands for the verification.  */
     495                 :          71 :           tree lhs = gimple_assign_lhs (asgn);
     496                 :          71 :           tree op1 = gimple_assign_rhs1 (asgn);
     497                 :          71 :           tree op2 = gimple_assign_rhs2 (asgn);
     498                 :          71 :           location_t loc = gimple_location (asgn);
     499                 :             : 
     500                 :             :           /* Vector booleans can't be used in conditional branches.  ???
     501                 :             :              Can we do better?  How to reduce compare and
     502                 :             :              reversed-compare result vectors to a single boolean?  */
     503                 :          71 :           if (VECTOR_TYPE_P (TREE_TYPE (op1)))
     504                 :           0 :             continue;
     505                 :             : 
     506                 :             :           /* useless_type_conversion_p enables conversions from 1-bit
     507                 :             :              integer types to boolean to be discarded.  */
     508                 :          71 :           gcc_checking_assert (TREE_CODE (TREE_TYPE (lhs)) == BOOLEAN_TYPE
     509                 :             :                                || (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
     510                 :             :                                    && TYPE_PRECISION (TREE_TYPE (lhs)) == 1));
     511                 :             : 
     512                 :          71 :           tree rhs = copy_ssa_name (lhs);
     513                 :             : 
     514                 :             :           /* Detach the values before the compares, so that the
     515                 :             :              compiler infers nothing from them, not even from a
     516                 :             :              throwing compare that didn't throw.  */
     517                 :          71 :           bool same_p = (op1 == op2);
     518                 :          71 :           op1 = detach_value (loc, &gsi, op1);
     519                 :          71 :           op2 = same_p ? op1 : detach_value (loc, &gsi, op2);
     520                 :             : 
     521                 :          71 :           gimple_stmt_iterator gsi_split = gsi;
     522                 :             :           /* Don't separate the original assignment from debug stmts
     523                 :             :              that might be associated with it, and arrange to split the
     524                 :             :              block after debug stmts, so as to make sure the split block
     525                 :             :              won't be debug stmts only.  */
     526                 :          71 :           gsi_next_nondebug (&gsi_split);
     527                 :             : 
     528                 :          71 :           bool throwing_compare_p = stmt_ends_bb_p (asgn);
     529                 :          71 :           if (throwing_compare_p)
     530                 :             :             {
     531                 :           4 :               basic_block nbb = split_edge (non_eh_succ_edge
     532                 :             :                                             (gimple_bb (asgn)));
     533                 :           4 :               gsi_split = gsi_start_bb (nbb);
     534                 :             : 
     535                 :           4 :               if (dump_file)
     536                 :           0 :                 fprintf (dump_file,
     537                 :             :                          "Splitting non-EH edge from block %i into %i"
     538                 :             :                          " after a throwing compare\n",
     539                 :           0 :                          gimple_bb (asgn)->index, nbb->index);
     540                 :             :             }
     541                 :             : 
     542                 :          71 :           gassign *asgnck = gimple_build_assign (rhs, cop, op1, op2);
     543                 :          71 :           gimple_set_location (asgnck, loc);
     544                 :          71 :           gsi_insert_before (&gsi_split, asgnck, GSI_SAME_STMT);
     545                 :             : 
     546                 :             :           /* We wish to insert a cond_expr after the compare, so arrange
     547                 :             :              for it to be at the end of a block if it isn't, and for it
     548                 :             :              to have a single successor in case there's more than
     549                 :             :              one, as in PR104975.  */
     550                 :          71 :           if (!gsi_end_p (gsi_split)
     551                 :          75 :               || !single_succ_p (gsi_bb (gsi_split)))
     552                 :             :             {
     553                 :          67 :               if (!gsi_end_p (gsi_split))
     554                 :          67 :                 gsi_prev (&gsi_split);
     555                 :             :               else
     556                 :           0 :                 gsi_split = gsi_last_bb (gsi_bb (gsi_split));
     557                 :          67 :               basic_block obb = gsi_bb (gsi_split);
     558                 :          67 :               basic_block nbb = split_block (obb, gsi_stmt (gsi_split))->dest;
     559                 :          67 :               gsi_next (&gsi_split);
     560                 :          67 :               gcc_checking_assert (gsi_end_p (gsi_split));
     561                 :             : 
     562                 :          67 :               single_succ_edge (bb)->goto_locus = loc;
     563                 :             : 
     564                 :          67 :               if (dump_file)
     565                 :          42 :                 fprintf (dump_file,
     566                 :             :                          "Splitting block %i into %i"
     567                 :             :                          " before the conditional trap branch\n",
     568                 :             :                          obb->index, nbb->index);
     569                 :             :             }
     570                 :             : 
     571                 :             :           /* If the check assignment must end a basic block, we can't
     572                 :             :              insert the conditional branch in the same block, so split
     573                 :             :              the block again, and prepare to insert the conditional
     574                 :             :              branch in the new block.
     575                 :             : 
     576                 :             :              Also assign an EH region to the compare.  Even though it's
     577                 :             :              unlikely that the hardening compare will throw after the
     578                 :             :              original compare didn't, the compiler won't even know that
     579                 :             :              it's the same compare operands, so add the EH edge anyway.  */
     580                 :          71 :           if (throwing_compare_p)
     581                 :             :             {
     582                 :           4 :               add_stmt_to_eh_lp (asgnck, lookup_stmt_eh_lp (asgn));
     583                 :           4 :               edge eh = make_eh_edge (asgnck);
     584                 :             :               /* This compare looks like it could raise an exception,
     585                 :             :                  but it's dominated by the original compare, that
     586                 :             :                  would raise an exception first, so the EH edge from
     587                 :             :                  this one is never really taken.  */
     588                 :           4 :               eh->probability = profile_probability::never ();
     589                 :           4 :               if (eh->dest->count.initialized_p ())
     590                 :           0 :                 eh->dest->count += eh->count ();
     591                 :             :               else
     592                 :           4 :                 eh->dest->count = eh->count ();
     593                 :             : 
     594                 :           4 :               edge ckeh;
     595                 :           4 :               basic_block nbb = split_edge (non_eh_succ_edge
     596                 :             :                                             (gimple_bb (asgnck), &ckeh));
     597                 :           4 :               gcc_checking_assert (eh == ckeh);
     598                 :           4 :               gsi_split = gsi_start_bb (nbb);
     599                 :             : 
     600                 :           4 :               if (dump_file)
     601                 :           0 :                 fprintf (dump_file,
     602                 :             :                          "Splitting non-EH edge from block %i into %i after"
     603                 :             :                          " the newly-inserted reversed throwing compare\n",
     604                 :           0 :                          gimple_bb (asgnck)->index, nbb->index);
     605                 :             : 
     606                 :           4 :               if (!gimple_seq_empty_p (phi_nodes (ckeh->dest)))
     607                 :             :                 {
     608                 :           0 :                   edge aseh;
     609                 :           0 :                   non_eh_succ_edge (gimple_bb (asgn), &aseh);
     610                 :             : 
     611                 :           0 :                   gcc_checking_assert (aseh->dest == ckeh->dest);
     612                 :             : 
     613                 :           0 :                   for (gphi_iterator psi = gsi_start_phis (ckeh->dest);
     614                 :           0 :                        !gsi_end_p (psi); gsi_next (&psi))
     615                 :             :                     {
     616                 :           0 :                       gphi *phi = psi.phi ();
     617                 :           0 :                       add_phi_arg (phi, PHI_ARG_DEF_FROM_EDGE (phi, aseh), ckeh,
     618                 :             :                                    gimple_phi_arg_location_from_edge (phi, aseh));
     619                 :             :                     }
     620                 :             : 
     621                 :           0 :                   if (dump_file)
     622                 :           0 :                     fprintf (dump_file,
     623                 :             :                              "Copying PHI args in EH block %i from %i to %i\n",
     624                 :           0 :                              aseh->dest->index, aseh->src->index,
     625                 :           0 :                              ckeh->src->index);
     626                 :             :                 }
     627                 :             :             }
     628                 :             : 
     629                 :         142 :           gcc_checking_assert (single_succ_p (gsi_bb (gsi_split)));
     630                 :             : 
     631                 :          71 :           insert_check_and_trap (loc, &gsi_split, EDGE_TRUE_VALUE,
     632                 :             :                                  EQ_EXPR, lhs, rhs);
     633                 :             :         }
     634                 :             :     }
     635                 :             : 
     636                 :          53 :   return 0;
     637                 :          53 : }
     638                 :             : 
     639                 :             : /* Instantiate a hardcmp pass.  */
     640                 :             : 
     641                 :             : gimple_opt_pass *
     642                 :      281914 : make_pass_harden_compares (gcc::context *ctxt)
     643                 :             : {
     644                 :      281914 :   return new pass_harden_compares (ctxt);
     645                 :             : }
        

Generated by: LCOV version 2.1-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.