2014-12-12 Richard Biener <rguenther@suse.de>
[official-gcc.git] / gcc / tree-chkp-opt.c
blob92e0694f09b7f7c629d4881f4521e9c38bd8b0c4
1 /* Pointer Bounds Checker optimization pass.
2 Copyright (C) 2014 Free Software Foundation, Inc.
3 Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree-core.h"
25 #include "tree.h"
26 #include "target.h"
27 #include "tree-cfg.h"
28 #include "tree-pass.h"
29 #include "is-a.h"
30 #include "cfgloop.h"
31 #include "stringpool.h"
32 #include "tree-ssa-alias.h"
33 #include "tree-ssanames.h"
34 #include "tree-ssa-operands.h"
35 #include "tree-ssa-address.h"
36 #include "tree-ssa.h"
37 #include "predict.h"
38 #include "dominance.h"
39 #include "cfg.h"
40 #include "basic-block.h"
41 #include "tree-ssa-loop-niter.h"
42 #include "gimple-expr.h"
43 #include "gimple.h"
44 #include "tree-phinodes.h"
45 #include "gimple-ssa.h"
46 #include "ssa-iterators.h"
47 #include "gimple-pretty-print.h"
48 #include "gimple-iterator.h"
49 #include "gimplify.h"
50 #include "gimplify-me.h"
51 #include "expr.h"
52 #include "tree-chkp.h"
53 #include "ipa-chkp.h"
54 #include "diagnostic.h"
56 enum check_type
58 CHECK_LOWER_BOUND,
59 CHECK_UPPER_BOUND
62 struct pol_item
64 tree cst;
65 tree var;
68 struct address_t
70 vec<struct pol_item> pol;
73 /* Structure to hold check informtation. */
74 struct check_info
76 /* Type of the check. */
77 check_type type;
78 /* Address used for the check. */
79 address_t addr;
80 /* Bounds used for the check. */
81 tree bounds;
82 /* Check statement. Can be NULL for removed checks. */
83 gimple stmt;
86 /* Structure to hold checks information for BB. */
87 struct bb_checks
89 vec<struct check_info, va_heap, vl_ptr> checks;
92 static void chkp_collect_value (tree ssa_name, address_t &res);
94 #define chkp_bndmk_fndecl \
95 (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK))
96 #define chkp_intersect_fndecl \
97 (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT))
98 #define chkp_checkl_fndecl \
99 (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL))
100 #define chkp_checku_fndecl \
101 (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU))
103 static vec<struct bb_checks, va_heap, vl_ptr> check_infos = vNULL;
105 /* Comparator for pol_item structures I1 and I2 to be used
106 to find items with equal var. Also used for polynomial
107 sorting. */
108 static int
109 chkp_pol_item_compare (const void *i1, const void *i2)
111 const struct pol_item *p1 = (const struct pol_item *)i1;
112 const struct pol_item *p2 = (const struct pol_item *)i2;
114 if (p1->var == p2->var)
115 return 0;
116 else if (p1->var > p2->var)
117 return 1;
118 else
119 return -1;
122 /* Find polynomial item in ADDR with var equal to VAR
123 and return its index. Return -1 if item was not
124 found. */
125 static int
126 chkp_pol_find (address_t &addr, tree var)
128 int left = 0;
129 int right = addr.pol.length () - 1;
130 int n;
132 while (right >= left)
134 n = (left + right) / 2;
136 if (addr.pol[n].var == var
137 || (var && addr.pol[n].var
138 && TREE_CODE (var) == ADDR_EXPR
139 && TREE_CODE (addr.pol[n].var) == ADDR_EXPR
140 && TREE_OPERAND (var, 0) == TREE_OPERAND (addr.pol[n].var, 0)))
141 return n;
142 else if (addr.pol[n].var > var)
143 right = n - 1;
144 else
145 left = n + 1;
148 return -1;
151 /* Return constant CST extended to size type. */
152 static tree
153 chkp_extend_const (tree cst)
155 if (TYPE_PRECISION (TREE_TYPE (cst)) < TYPE_PRECISION (size_type_node))
156 return build_int_cst_type (size_type_node, tree_to_shwi (cst));
158 return cst;
161 /* Add polynomial item CST * VAR to ADDR. */
162 static void
163 chkp_add_addr_item (address_t &addr, tree cst, tree var)
165 int n = chkp_pol_find (addr, var);
167 cst = chkp_extend_const (cst);
169 if (n < 0)
171 struct pol_item item;
172 item.cst = cst;
173 item.var = var;
175 addr.pol.safe_push (item);
176 addr.pol.qsort (&chkp_pol_item_compare);
178 else
180 addr.pol[n].cst = fold_build2 (PLUS_EXPR, TREE_TYPE (addr.pol[n].cst),
181 addr.pol[n].cst, cst);
182 if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST
183 && integer_zerop (addr.pol[n].cst))
184 addr.pol.ordered_remove (n);
188 /* Subtract polynomial item CST * VAR from ADDR. */
189 static void
190 chkp_sub_addr_item (address_t &addr, tree cst, tree var)
192 int n = chkp_pol_find (addr, var);
194 cst = chkp_extend_const (cst);
196 if (n < 0)
198 struct pol_item item;
199 item.cst = fold_build2 (MINUS_EXPR, TREE_TYPE (cst),
200 integer_zero_node, cst);
201 item.var = var;
203 addr.pol.safe_push (item);
204 addr.pol.qsort (&chkp_pol_item_compare);
206 else
208 addr.pol[n].cst = fold_build2 (MINUS_EXPR, TREE_TYPE (addr.pol[n].cst),
209 addr.pol[n].cst, cst);
210 if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST
211 && integer_zerop (addr.pol[n].cst))
212 addr.pol.ordered_remove (n);
216 /* Add address DELTA to ADDR. */
217 static void
218 chkp_add_addr_addr (address_t &addr, address_t &delta)
220 unsigned int i;
221 for (i = 0; i < delta.pol.length (); i++)
222 chkp_add_addr_item (addr, delta.pol[i].cst, delta.pol[i].var);
225 /* Subtract address DELTA from ADDR. */
226 static void
227 chkp_sub_addr_addr (address_t &addr, address_t &delta)
229 unsigned int i;
230 for (i = 0; i < delta.pol.length (); i++)
231 chkp_sub_addr_item (addr, delta.pol[i].cst, delta.pol[i].var);
234 /* Mutiply address ADDR by integer constant MULT. */
235 static void
236 chkp_mult_addr (address_t &addr, tree mult)
238 unsigned int i;
239 for (i = 0; i < addr.pol.length (); i++)
240 addr.pol[i].cst = fold_build2 (MULT_EXPR, TREE_TYPE (addr.pol[i].cst),
241 addr.pol[i].cst, mult);
244 /* Return 1 if we may prove ADDR has a constant value with
245 determined sign, which is put into *SIGN. Otherwise
246 return 0. */
247 static bool
248 chkp_is_constant_addr (const address_t &addr, int *sign)
250 *sign = 0;
252 if (addr.pol.length () == 0)
253 return true;
254 else if (addr.pol.length () > 1)
255 return false;
256 else if (addr.pol[0].var)
257 return false;
258 else if (integer_zerop (addr.pol[0].cst))
259 *sign = 0;
260 else if (tree_int_cst_sign_bit (addr.pol[0].cst))
261 *sign = -1;
262 else
263 *sign = 1;
265 return true;
268 /* Dump ADDR into dump_file. */
269 static void
270 chkp_print_addr (const address_t &addr)
272 unsigned int n = 0;
273 for (n = 0; n < addr.pol.length (); n++)
275 if (n > 0)
276 fprintf (dump_file, " + ");
278 if (addr.pol[n].var == NULL_TREE)
279 print_generic_expr (dump_file, addr.pol[n].cst, 0);
280 else
282 if (TREE_CODE (addr.pol[n].cst) != INTEGER_CST
283 || !integer_onep (addr.pol[n].cst))
285 print_generic_expr (dump_file, addr.pol[n].cst, 0);
286 fprintf (dump_file, " * ");
288 print_generic_expr (dump_file, addr.pol[n].var, 0);
293 /* Compute value of PTR and put it into address RES.
294 PTR has to be ADDR_EXPR. */
295 static void
296 chkp_collect_addr_value (tree ptr, address_t &res)
298 tree obj = TREE_OPERAND (ptr, 0);
299 address_t addr;
301 switch (TREE_CODE (obj))
303 case INDIRECT_REF:
304 chkp_collect_value (TREE_OPERAND (obj, 0), res);
305 break;
307 case MEM_REF:
308 chkp_collect_value (TREE_OPERAND (obj, 0), res);
309 addr.pol.create (0);
310 chkp_collect_value (TREE_OPERAND (obj, 1), addr);
311 chkp_add_addr_addr (res, addr);
312 addr.pol.release ();
313 break;
315 case ARRAY_REF:
316 chkp_collect_value (build_fold_addr_expr (TREE_OPERAND (obj, 0)), res);
317 addr.pol.create (0);
318 chkp_collect_value (TREE_OPERAND (obj, 1), addr);
319 chkp_mult_addr (addr, array_ref_element_size (obj));
320 chkp_add_addr_addr (res, addr);
321 addr.pol.release ();
322 break;
324 case COMPONENT_REF:
326 tree str = TREE_OPERAND (obj, 0);
327 tree field = TREE_OPERAND (obj, 1);
328 chkp_collect_value (build_fold_addr_expr (str), res);
329 addr.pol.create (0);
330 chkp_collect_value (component_ref_field_offset (obj), addr);
331 chkp_add_addr_addr (res, addr);
332 addr.pol.release ();
333 if (DECL_FIELD_BIT_OFFSET (field))
335 addr.pol.create (0);
336 chkp_collect_value (fold_build2 (TRUNC_DIV_EXPR, size_type_node,
337 DECL_FIELD_BIT_OFFSET (field),
338 size_int (BITS_PER_UNIT)),
339 addr);
340 chkp_add_addr_addr (res, addr);
341 addr.pol.release ();
344 break;
346 default:
347 chkp_add_addr_item (res, integer_one_node, ptr);
348 break;
352 /* Compute value of PTR and put it into address RES. */
353 static void
354 chkp_collect_value (tree ptr, address_t &res)
356 gimple def_stmt;
357 enum gimple_code code;
358 enum tree_code rhs_code;
359 address_t addr;
360 tree rhs1;
362 if (TREE_CODE (ptr) == INTEGER_CST)
364 chkp_add_addr_item (res, ptr, NULL);
365 return;
367 else if (TREE_CODE (ptr) == ADDR_EXPR)
369 chkp_collect_addr_value (ptr, res);
370 return;
372 else if (TREE_CODE (ptr) != SSA_NAME)
374 chkp_add_addr_item (res, integer_one_node, ptr);
375 return;
378 /* Now we handle the case when polynomial is computed
379 for SSA NAME. */
380 def_stmt = SSA_NAME_DEF_STMT (ptr);
381 code = gimple_code (def_stmt);
383 /* Currently we do not walk through statements other
384 than assignment. */
385 if (code != GIMPLE_ASSIGN)
387 chkp_add_addr_item (res, integer_one_node, ptr);
388 return;
391 rhs_code = gimple_assign_rhs_code (def_stmt);
392 rhs1 = gimple_assign_rhs1 (def_stmt);
394 switch (rhs_code)
396 case SSA_NAME:
397 case INTEGER_CST:
398 case ADDR_EXPR:
399 chkp_collect_value (rhs1, res);
400 break;
402 case PLUS_EXPR:
403 case POINTER_PLUS_EXPR:
404 chkp_collect_value (rhs1, res);
405 addr.pol.create (0);
406 chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr);
407 chkp_add_addr_addr (res, addr);
408 addr.pol.release ();
409 break;
411 case MINUS_EXPR:
412 chkp_collect_value (rhs1, res);
413 addr.pol.create (0);
414 chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr);
415 chkp_sub_addr_addr (res, addr);
416 addr.pol.release ();
417 break;
419 case MULT_EXPR:
420 if (TREE_CODE (rhs1) == SSA_NAME
421 && TREE_CODE (gimple_assign_rhs2 (def_stmt)) == INTEGER_CST)
423 chkp_collect_value (rhs1, res);
424 chkp_mult_addr (res, gimple_assign_rhs2 (def_stmt));
426 else if (TREE_CODE (gimple_assign_rhs2 (def_stmt)) == SSA_NAME
427 && TREE_CODE (rhs1) == INTEGER_CST)
429 chkp_collect_value (gimple_assign_rhs2 (def_stmt), res);
430 chkp_mult_addr (res, rhs1);
432 else
433 chkp_add_addr_item (res, integer_one_node, ptr);
434 break;
436 default:
437 chkp_add_addr_item (res, integer_one_node, ptr);
438 break;
442 /* Fill check_info structure *CI with information about
443 check STMT. */
444 static void
445 chkp_fill_check_info (gimple stmt, struct check_info *ci)
447 ci->addr.pol.create (0);
448 ci->bounds = gimple_call_arg (stmt, 1);
449 chkp_collect_value (gimple_call_arg (stmt, 0), ci->addr);
450 ci->type = (gimple_call_fndecl (stmt) == chkp_checkl_fndecl
451 ? CHECK_LOWER_BOUND
452 : CHECK_UPPER_BOUND);
453 ci->stmt = stmt;
456 /* Release structures holding check information
457 for current function. */
458 static void
459 chkp_release_check_info (void)
461 unsigned int n, m;
463 if (check_infos.exists ())
465 for (n = 0; n < check_infos.length (); n++)
467 for (m = 0; m < check_infos[n].checks.length (); m++)
468 if (check_infos[n].checks[m].addr.pol.exists ())
469 check_infos[n].checks[m].addr.pol.release ();
470 check_infos[n].checks.release ();
472 check_infos.release ();
476 /* Create structures to hold check information
477 for current function. */
478 static void
479 chkp_init_check_info (void)
481 struct bb_checks empty_bbc;
482 int n;
484 empty_bbc.checks = vNULL;
486 chkp_release_check_info ();
488 check_infos.create (last_basic_block_for_fn (cfun));
489 for (n = 0; n < last_basic_block_for_fn (cfun); n++)
491 check_infos.safe_push (empty_bbc);
492 check_infos.last ().checks.create (0);
496 /* Find all checks in current function and store info about them
497 in check_infos. */
498 static void
499 chkp_gather_checks_info (void)
501 basic_block bb;
502 gimple_stmt_iterator i;
504 if (dump_file && (dump_flags & TDF_DETAILS))
505 fprintf (dump_file, "Gathering information about checks...\n");
507 chkp_init_check_info ();
509 FOR_EACH_BB_FN (bb, cfun)
511 struct bb_checks *bbc = &check_infos[bb->index];
513 if (dump_file && (dump_flags & TDF_DETAILS))
514 fprintf (dump_file, "Searching checks in BB%d...\n", bb->index);
516 for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
518 gimple stmt = gsi_stmt (i);
520 if (gimple_code (stmt) != GIMPLE_CALL)
521 continue;
523 if (gimple_call_fndecl (stmt) == chkp_checkl_fndecl
524 || gimple_call_fndecl (stmt) == chkp_checku_fndecl)
526 struct check_info ci;
528 chkp_fill_check_info (stmt, &ci);
529 bbc->checks.safe_push (ci);
531 if (dump_file && (dump_flags & TDF_DETAILS))
533 fprintf (dump_file, "Adding check information:\n");
534 fprintf (dump_file, " bounds: ");
535 print_generic_expr (dump_file, ci.bounds, 0);
536 fprintf (dump_file, "\n address: ");
537 chkp_print_addr (ci.addr);
538 fprintf (dump_file, "\n check: ");
539 print_gimple_stmt (dump_file, stmt, 0, 0);
546 /* Return 1 if check CI against BOUNDS always pass,
547 -1 if check CI against BOUNDS always fails and
548 0 if we cannot compute check result. */
549 static int
550 chkp_get_check_result (struct check_info *ci, tree bounds)
552 gimple bnd_def;
553 address_t bound_val;
554 int sign, res = 0;
556 if (dump_file && (dump_flags & TDF_DETAILS))
558 fprintf (dump_file, "Trying to compute result of the check\n");
559 fprintf (dump_file, " check: ");
560 print_gimple_stmt (dump_file, ci->stmt, 0, 0);
561 fprintf (dump_file, " address: ");
562 chkp_print_addr (ci->addr);
563 fprintf (dump_file, "\n bounds: ");
564 print_generic_expr (dump_file, bounds, 0);
565 fprintf (dump_file, "\n");
568 if (TREE_CODE (bounds) != SSA_NAME)
570 if (dump_file && (dump_flags & TDF_DETAILS))
571 fprintf (dump_file, " result: bounds tree code is not ssa_name\n");
572 return 0;
575 bnd_def = SSA_NAME_DEF_STMT (bounds);
576 /* Currently we handle cases when bounds are result of bndmk
577 or loaded static bounds var. */
578 if (gimple_code (bnd_def) == GIMPLE_CALL
579 && gimple_call_fndecl (bnd_def) == chkp_bndmk_fndecl)
581 bound_val.pol.create (0);
582 chkp_collect_value (gimple_call_arg (bnd_def, 0), bound_val);
583 if (ci->type == CHECK_UPPER_BOUND)
585 address_t size_val;
586 size_val.pol.create (0);
587 chkp_collect_value (gimple_call_arg (bnd_def, 1), size_val);
588 chkp_add_addr_addr (bound_val, size_val);
589 size_val.pol.release ();
590 chkp_add_addr_item (bound_val, integer_minus_one_node, NULL);
593 else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
594 && gimple_assign_rhs1 (bnd_def) == chkp_get_zero_bounds_var ())
596 if (dump_file && (dump_flags & TDF_DETAILS))
597 fprintf (dump_file, " result: always pass with zero bounds\n");
598 return 1;
600 else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
601 && gimple_assign_rhs1 (bnd_def) == chkp_get_none_bounds_var ())
603 if (dump_file && (dump_flags & TDF_DETAILS))
604 fprintf (dump_file, " result: always fails with none bounds\n");
605 return -1;
607 else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
608 && TREE_CODE (gimple_assign_rhs1 (bnd_def)) == VAR_DECL)
610 tree bnd_var = gimple_assign_rhs1 (bnd_def);
611 tree var;
612 tree size;
614 if (!DECL_INITIAL (bnd_var)
615 || DECL_INITIAL (bnd_var) == error_mark_node)
617 if (dump_file && (dump_flags & TDF_DETAILS))
618 fprintf (dump_file, " result: cannot compute bounds\n");
619 return 0;
622 gcc_assert (TREE_CODE (DECL_INITIAL (bnd_var)) == ADDR_EXPR);
623 var = TREE_OPERAND (DECL_INITIAL (bnd_var), 0);
625 bound_val.pol.create (0);
626 chkp_collect_value (DECL_INITIAL (bnd_var), bound_val);
627 if (ci->type == CHECK_UPPER_BOUND)
629 if (TREE_CODE (var) == VAR_DECL)
631 if (DECL_SIZE (var)
632 && !chkp_variable_size_type (TREE_TYPE (var)))
633 size = DECL_SIZE_UNIT (var);
634 else
636 if (dump_file && (dump_flags & TDF_DETAILS))
637 fprintf (dump_file, " result: cannot compute bounds\n");
638 return 0;
641 else
643 gcc_assert (TREE_CODE (var) == STRING_CST);
644 size = build_int_cst (size_type_node,
645 TREE_STRING_LENGTH (var));
648 address_t size_val;
649 size_val.pol.create (0);
650 chkp_collect_value (size, size_val);
651 chkp_add_addr_addr (bound_val, size_val);
652 size_val.pol.release ();
653 chkp_add_addr_item (bound_val, integer_minus_one_node, NULL);
656 else
658 if (dump_file && (dump_flags & TDF_DETAILS))
659 fprintf (dump_file, " result: cannot compute bounds\n");
660 return 0;
663 if (dump_file && (dump_flags & TDF_DETAILS))
665 fprintf (dump_file, " bound value: ");
666 chkp_print_addr (bound_val);
667 fprintf (dump_file, "\n");
670 chkp_sub_addr_addr (bound_val, ci->addr);
672 if (!chkp_is_constant_addr (bound_val, &sign))
674 if (dump_file && (dump_flags & TDF_DETAILS))
675 fprintf (dump_file, " result: cannot compute result\n");
677 res = 0;
679 else if (sign == 0
680 || (ci->type == CHECK_UPPER_BOUND && sign > 0)
681 || (ci->type == CHECK_LOWER_BOUND && sign < 0))
683 if (dump_file && (dump_flags & TDF_DETAILS))
684 fprintf (dump_file, " result: always pass\n");
686 res = 1;
688 else
690 if (dump_file && (dump_flags & TDF_DETAILS))
691 fprintf (dump_file, " result: always fail\n");
693 res = -1;
696 bound_val.pol.release ();
698 return res;
701 /* Try to compare bounds value and address value
702 used in the check CI. If we can prove that check
703 always pass then remove it. */
704 static void
705 chkp_remove_check_if_pass (struct check_info *ci)
707 int result = 0;
709 if (dump_file && (dump_flags & TDF_DETAILS))
711 fprintf (dump_file, "Trying to remove check: ");
712 print_gimple_stmt (dump_file, ci->stmt, 0, 0);
715 result = chkp_get_check_result (ci, ci->bounds);
717 if (result == 1)
719 gimple_stmt_iterator i = gsi_for_stmt (ci->stmt);
721 if (dump_file && (dump_flags & TDF_DETAILS))
722 fprintf (dump_file, " action: delete check (always pass)\n");
724 gsi_remove (&i, true);
725 unlink_stmt_vdef (ci->stmt);
726 release_defs (ci->stmt);
727 ci->stmt = NULL;
729 else if (result == -1)
731 if (dump_file && (dump_flags & TDF_DETAILS))
732 fprintf (dump_file, " action: keep check (always fail)\n");
733 warning_at (gimple_location (ci->stmt), OPT_Wchkp,
734 "memory access check always fail");
736 else if (result == 0)
738 if (dump_file && (dump_flags & TDF_DETAILS))
739 fprintf (dump_file, " action: keep check (cannot compute result)\n");
743 /* For bounds used in CI check if bounds are produced by
744 intersection and we may use outer bounds instead. If
745 transformation is possible then fix check statement and
746 recompute its info. */
747 static void
748 chkp_use_outer_bounds_if_possible (struct check_info *ci)
750 gimple bnd_def;
751 tree bnd1, bnd2, bnd_res = NULL;
752 int check_res1, check_res2;
754 if (TREE_CODE (ci->bounds) != SSA_NAME)
755 return;
757 bnd_def = SSA_NAME_DEF_STMT (ci->bounds);
758 if (gimple_code (bnd_def) != GIMPLE_CALL
759 || gimple_call_fndecl (bnd_def) != chkp_intersect_fndecl)
760 return;
762 if (dump_file && (dump_flags & TDF_DETAILS))
764 fprintf (dump_file, "Check if bounds intersection is redundant: \n");
765 fprintf (dump_file, " check: ");
766 print_gimple_stmt (dump_file, ci->stmt, 0, 0);
767 fprintf (dump_file, " intersection: ");
768 print_gimple_stmt (dump_file, bnd_def, 0, 0);
769 fprintf (dump_file, "\n");
772 bnd1 = gimple_call_arg (bnd_def, 0);
773 bnd2 = gimple_call_arg (bnd_def, 1);
775 check_res1 = chkp_get_check_result (ci, bnd1);
776 check_res2 = chkp_get_check_result (ci, bnd2);
777 if (check_res1 == 1)
778 bnd_res = bnd2;
779 else if (check_res1 == -1)
780 bnd_res = bnd1;
781 else if (check_res2 == 1)
782 bnd_res = bnd1;
783 else if (check_res2 == -1)
784 bnd_res = bnd2;
786 if (bnd_res)
788 if (dump_file && (dump_flags & TDF_DETAILS))
790 fprintf (dump_file, " action: use ");
791 print_generic_expr (dump_file, bnd2, 0);
792 fprintf (dump_file, " instead of ");
793 print_generic_expr (dump_file, ci->bounds, 0);
794 fprintf (dump_file, "\n");
797 ci->bounds = bnd_res;
798 gimple_call_set_arg (ci->stmt, 1, bnd_res);
799 update_stmt (ci->stmt);
800 chkp_fill_check_info (ci->stmt, ci);
804 /* Try to find checks whose bounds were produced by intersection
805 which does not affect check result. In such check outer bounds
806 are used instead. It allows to remove excess intersections
807 and helps to compare checks. */
808 static void
809 chkp_remove_excess_intersections (void)
811 basic_block bb;
813 if (dump_file && (dump_flags & TDF_DETAILS))
814 fprintf (dump_file, "Searching for redundant bounds intersections...\n");
816 FOR_EACH_BB_FN (bb, cfun)
818 struct bb_checks *bbc = &check_infos[bb->index];
819 unsigned int no;
821 /* Iterate through all found checks in BB. */
822 for (no = 0; no < bbc->checks.length (); no++)
823 if (bbc->checks[no].stmt)
824 chkp_use_outer_bounds_if_possible (&bbc->checks[no]);
828 /* Try to remove all checks which are known to alwyas pass. */
829 static void
830 chkp_remove_constant_checks (void)
832 basic_block bb;
834 if (dump_file && (dump_flags & TDF_DETAILS))
835 fprintf (dump_file, "Searching for redundant checks...\n");
837 FOR_EACH_BB_FN (bb, cfun)
839 struct bb_checks *bbc = &check_infos[bb->index];
840 unsigned int no;
842 /* Iterate through all found checks in BB. */
843 for (no = 0; no < bbc->checks.length (); no++)
844 if (bbc->checks[no].stmt)
845 chkp_remove_check_if_pass (&bbc->checks[no]);
849 /* Return fast version of string function FNCODE. */
850 static tree
851 chkp_get_nobnd_fndecl (enum built_in_function fncode)
853 /* Check if we are allowed to use fast string functions. */
854 if (!flag_chkp_use_fast_string_functions)
855 return NULL_TREE;
857 tree fndecl = NULL_TREE;
859 switch (fncode)
861 case BUILT_IN_MEMCPY_CHKP:
862 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND);
863 break;
865 case BUILT_IN_MEMPCPY_CHKP:
866 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND);
867 break;
869 case BUILT_IN_MEMMOVE_CHKP:
870 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND);
871 break;
873 case BUILT_IN_MEMSET_CHKP:
874 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND);
875 break;
877 case BUILT_IN_CHKP_MEMCPY_NOCHK_CHKP:
878 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
879 break;
881 case BUILT_IN_CHKP_MEMPCPY_NOCHK_CHKP:
882 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
883 break;
885 case BUILT_IN_CHKP_MEMMOVE_NOCHK_CHKP:
886 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
887 break;
889 case BUILT_IN_CHKP_MEMSET_NOCHK_CHKP:
890 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
891 break;
893 default:
894 break;
897 if (fndecl)
898 fndecl = chkp_maybe_clone_builtin_fndecl (fndecl);
900 return fndecl;
904 /* Return no-check version of string function FNCODE. */
905 static tree
906 chkp_get_nochk_fndecl (enum built_in_function fncode)
908 /* Check if we are allowed to use fast string functions. */
909 if (!flag_chkp_use_nochk_string_functions)
910 return NULL_TREE;
912 tree fndecl = NULL_TREE;
914 switch (fncode)
916 case BUILT_IN_MEMCPY_CHKP:
917 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOCHK);
918 break;
920 case BUILT_IN_MEMPCPY_CHKP:
921 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOCHK);
922 break;
924 case BUILT_IN_MEMMOVE_CHKP:
925 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOCHK);
926 break;
928 case BUILT_IN_MEMSET_CHKP:
929 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOCHK);
930 break;
932 case BUILT_IN_CHKP_MEMCPY_NOBND_CHKP:
933 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
934 break;
936 case BUILT_IN_CHKP_MEMPCPY_NOBND_CHKP:
937 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
938 break;
940 case BUILT_IN_CHKP_MEMMOVE_NOBND_CHKP:
941 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
942 break;
944 case BUILT_IN_CHKP_MEMSET_NOBND_CHKP:
945 fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
946 break;
948 default:
949 break;
952 if (fndecl)
953 fndecl = chkp_maybe_clone_builtin_fndecl (fndecl);
955 return fndecl;
958 /* Find memcpy, mempcpy, memmove and memset calls, perform
959 checks before call and then call no_chk version of
960 functions. We do it on O2 to enable inlining of these
961 functions during expand.
963 Also try to find memcpy, mempcpy, memmove and memset calls
964 which are known to not write pointers to memory and use
965 faster function versions for them. */
966 static void
967 chkp_optimize_string_function_calls (void)
969 basic_block bb;
971 if (dump_file && (dump_flags & TDF_DETAILS))
972 fprintf (dump_file, "Searching for replaceable string function calls...\n");
974 FOR_EACH_BB_FN (bb, cfun)
976 gimple_stmt_iterator i;
978 for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
980 gimple stmt = gsi_stmt (i);
981 tree fndecl;
983 if (gimple_code (stmt) != GIMPLE_CALL
984 || !gimple_call_with_bounds_p (stmt))
985 continue;
987 fndecl = gimple_call_fndecl (stmt);
989 if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
990 continue;
992 if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMCPY_CHKP
993 || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY_CHKP
994 || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMMOVE_CHKP
995 || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET_CHKP)
997 tree dst = gimple_call_arg (stmt, 0);
998 tree dst_bnd = gimple_call_arg (stmt, 1);
999 bool is_memset = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET_CHKP;
1000 tree size = gimple_call_arg (stmt, is_memset ? 3 : 4);
1001 tree fndecl_nochk;
1002 gimple_stmt_iterator j;
1003 basic_block check_bb;
1004 address_t size_val;
1005 int sign;
1006 bool known;
1008 /* We may replace call with corresponding __chkp_*_nobnd
1009 call in case destination pointer base type is not
1010 void or pointer. */
1011 if (POINTER_TYPE_P (TREE_TYPE (dst))
1012 && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (dst)))
1013 && !chkp_type_has_pointer (TREE_TYPE (TREE_TYPE (dst))))
1015 tree fndecl_nobnd
1016 = chkp_get_nobnd_fndecl (DECL_FUNCTION_CODE (fndecl));
1018 if (fndecl_nobnd)
1019 fndecl = fndecl_nobnd;
1022 fndecl_nochk = chkp_get_nochk_fndecl (DECL_FUNCTION_CODE (fndecl));
1024 if (fndecl_nochk)
1025 fndecl = fndecl_nochk;
1027 if (fndecl != gimple_call_fndecl (stmt))
1029 if (dump_file && (dump_flags & TDF_DETAILS))
1031 fprintf (dump_file, "Replacing call: ");
1032 print_gimple_stmt (dump_file, stmt, 0,
1033 TDF_VOPS|TDF_MEMSYMS);
1036 gimple_call_set_fndecl (stmt, fndecl);
1038 if (dump_file && (dump_flags & TDF_DETAILS))
1040 fprintf (dump_file, "With a new call: ");
1041 print_gimple_stmt (dump_file, stmt, 0,
1042 TDF_VOPS|TDF_MEMSYMS);
1046 /* If there is no nochk version of function then
1047 do nothing. Otherwise insert checks before
1048 the call. */
1049 if (!fndecl_nochk)
1050 continue;
1052 /* If size passed to call is known and > 0
1053 then we may insert checks unconditionally. */
1054 size_val.pol.create (0);
1055 chkp_collect_value (size, size_val);
1056 known = chkp_is_constant_addr (size_val, &sign);
1057 size_val.pol.release ();
1059 /* If we are not sure size is not zero then we have
1060 to perform runtime check for size and perform
1061 checks only when size is not zero. */
1062 if (!known)
1064 gimple check = gimple_build_cond (NE_EXPR,
1065 size,
1066 size_zero_node,
1067 NULL_TREE,
1068 NULL_TREE);
1070 /* Split block before string function call. */
1071 gsi_prev (&i);
1072 check_bb = insert_cond_bb (bb, gsi_stmt (i), check);
1074 /* Set position for checks. */
1075 j = gsi_last_bb (check_bb);
1077 /* The block was splitted and therefore we
1078 need to set iterator to its end. */
1079 i = gsi_last_bb (bb);
1081 /* If size is known to be zero then no checks
1082 should be performed. */
1083 else if (!sign)
1084 continue;
1085 else
1086 j = i;
1088 size = size_binop (MINUS_EXPR, size, size_one_node);
1089 if (!is_memset)
1091 tree src = gimple_call_arg (stmt, 2);
1092 tree src_bnd = gimple_call_arg (stmt, 3);
1094 chkp_check_mem_access (src, fold_build_pointer_plus (src, size),
1095 src_bnd, j, gimple_location (stmt),
1096 integer_zero_node);
1099 chkp_check_mem_access (dst, fold_build_pointer_plus (dst, size),
1100 dst_bnd, j, gimple_location (stmt),
1101 integer_one_node);
1108 /* Intrumentation pass inserts most of bounds creation code
1109 in the header of the function. We want to move bounds
1110 creation closer to bounds usage to reduce bounds lifetime.
1111 We also try to avoid bounds creation code on paths where
1112 bounds are not used. */
1113 static void
1114 chkp_reduce_bounds_lifetime (void)
1116 basic_block bb = FALLTHRU_EDGE (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest;
1117 gimple_stmt_iterator i;
1119 for (i = gsi_start_bb (bb); !gsi_end_p (i); )
1121 gimple dom_use, use_stmt, stmt = gsi_stmt (i);
1122 basic_block dom_bb;
1123 ssa_op_iter iter;
1124 imm_use_iterator use_iter;
1125 use_operand_p use_p;
1126 tree op;
1127 bool want_move = false;
1128 bool deps = false;
1130 if (gimple_code (stmt) == GIMPLE_CALL
1131 && gimple_call_fndecl (stmt) == chkp_bndmk_fndecl)
1132 want_move = true;
1134 if (gimple_code (stmt) == GIMPLE_ASSIGN
1135 && POINTER_BOUNDS_P (gimple_assign_lhs (stmt))
1136 && gimple_assign_rhs_code (stmt) == VAR_DECL)
1137 want_move = true;
1139 if (!want_move)
1141 gsi_next (&i);
1142 continue;
1145 /* Check we do not increase other values lifetime. */
1146 FOR_EACH_PHI_OR_STMT_USE (use_p, stmt, iter, SSA_OP_USE)
1148 op = USE_FROM_PTR (use_p);
1150 if (TREE_CODE (op) == SSA_NAME
1151 && gimple_code (SSA_NAME_DEF_STMT (op)) != GIMPLE_NOP)
1153 deps = true;
1154 break;
1158 if (deps)
1160 gsi_next (&i);
1161 continue;
1164 /* Check all usages of bounds. */
1165 if (gimple_code (stmt) == GIMPLE_CALL)
1166 op = gimple_call_lhs (stmt);
1167 else
1169 gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
1170 op = gimple_assign_lhs (stmt);
1173 dom_use = NULL;
1174 dom_bb = NULL;
1176 FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, op)
1178 if (is_gimple_debug (use_stmt))
1179 continue;
1181 if (dom_bb &&
1182 dominated_by_p (CDI_DOMINATORS,
1183 dom_bb, gimple_bb (use_stmt)))
1185 dom_use = use_stmt;
1186 dom_bb = NULL;
1188 else if (dom_bb)
1189 dom_bb = nearest_common_dominator (CDI_DOMINATORS, dom_bb,
1190 gimple_bb (use_stmt));
1191 else if (!dom_use)
1192 dom_use = use_stmt;
1193 else if (stmt_dominates_stmt_p (use_stmt, dom_use))
1194 dom_use = use_stmt;
1195 else if (!stmt_dominates_stmt_p (dom_use, use_stmt)
1196 /* If dom_use and use_stmt are PHI nodes in one BB
1197 then it is OK to keep any of them as dom_use.
1198 stmt_dominates_stmt_p returns 0 for such
1199 combination, so check it here manually. */
1200 && (gimple_code (dom_use) != GIMPLE_PHI
1201 || gimple_code (use_stmt) != GIMPLE_PHI
1202 || gimple_bb (use_stmt) != gimple_bb (dom_use))
1205 dom_bb = nearest_common_dominator (CDI_DOMINATORS,
1206 gimple_bb (use_stmt),
1207 gimple_bb (dom_use));
1208 dom_use = NULL;
1212 /* In case there is a single use, just move bounds
1213 creation to the use. */
1214 if (dom_use || dom_bb)
1216 if (dump_file && (dump_flags & TDF_DETAILS))
1218 fprintf (dump_file, "Moving creation of ");
1219 print_generic_expr (dump_file, op, 0);
1220 fprintf (dump_file, " down to its use.\n");
1223 if (dom_use && gimple_code (dom_use) == GIMPLE_PHI)
1225 dom_bb = get_immediate_dominator (CDI_DOMINATORS,
1226 gimple_bb (dom_use));
1227 dom_use = NULL;
1230 if (dom_bb == bb
1231 || (dom_use && gimple_bb (dom_use) == bb))
1233 if (dump_file && (dump_flags & TDF_DETAILS))
1234 fprintf (dump_file, "Cannot move statement bacause there is no "
1235 "suitable dominator block other than entry block.\n");
1237 gsi_next (&i);
1239 else
1241 if (dom_bb)
1243 gimple_stmt_iterator last = gsi_last_bb (dom_bb);
1244 if (!gsi_end_p (last) && stmt_ends_bb_p (gsi_stmt (last)))
1245 gsi_move_before (&i, &last);
1246 else
1247 gsi_move_after (&i, &last);
1249 else
1251 gimple_stmt_iterator gsi = gsi_for_stmt (dom_use);
1252 gsi_move_before (&i, &gsi);
1255 update_stmt (stmt);
1258 else
1259 gsi_next (&i);
1263 /* Initilize checker optimization pass. */
1264 static void
1265 chkp_opt_init (void)
1267 check_infos.create (0);
1269 calculate_dominance_info (CDI_DOMINATORS);
1270 calculate_dominance_info (CDI_POST_DOMINATORS);
1272 /* With LTO constant bounds vars may be not initialized by now.
1273 Get constant bounds vars to handle their assignments during
1274 optimizations. */
1275 chkp_get_zero_bounds_var ();
1276 chkp_get_none_bounds_var ();
1279 /* Finalise checker optimization pass. */
1280 static void
1281 chkp_opt_fini (void)
1283 chkp_fix_cfg ();
1286 /* Checker optimization pass function. */
1287 static unsigned int
1288 chkp_opt_execute (void)
1290 chkp_opt_init();
1292 /* This optimization may introduce new checks
1293 and thus we put it before checks search. */
1294 chkp_optimize_string_function_calls ();
1296 chkp_gather_checks_info ();
1298 chkp_remove_excess_intersections ();
1300 chkp_remove_constant_checks ();
1302 chkp_reduce_bounds_lifetime ();
1304 chkp_release_check_info ();
1306 chkp_opt_fini ();
1308 return 0;
1311 /* Pass gate. */
1312 static bool
1313 chkp_opt_gate (void)
1315 return chkp_function_instrumented_p (cfun->decl)
1316 && (flag_chkp_optimize > 0
1317 || (flag_chkp_optimize == -1 && optimize > 0));
1320 namespace {
1322 const pass_data pass_data_chkp_opt =
1324 GIMPLE_PASS, /* type */
1325 "chkpopt", /* name */
1326 OPTGROUP_NONE, /* optinfo_flags */
1327 TV_NONE, /* tv_id */
1328 PROP_ssa | PROP_cfg, /* properties_required */
1329 0, /* properties_provided */
1330 0, /* properties_destroyed */
1331 0, /* todo_flags_start */
1332 TODO_verify_il
1333 | TODO_update_ssa /* todo_flags_finish */
1336 class pass_chkp_opt : public gimple_opt_pass
1338 public:
1339 pass_chkp_opt (gcc::context *ctxt)
1340 : gimple_opt_pass (pass_data_chkp_opt, ctxt)
1343 /* opt_pass methods: */
1344 virtual opt_pass * clone ()
1346 return new pass_chkp_opt (m_ctxt);
1349 virtual bool gate (function *)
1351 return chkp_opt_gate ();
1354 virtual unsigned int execute (function *)
1356 return chkp_opt_execute ();
1359 }; // class pass_chkp_opt
1361 } // anon namespace
1363 gimple_opt_pass *
1364 make_pass_chkp_opt (gcc::context *ctxt)
1366 return new pass_chkp_opt (ctxt);