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