summaryrefslogtreecommitdiff
path: root/gcc/tree-chkp-opt.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-chkp-opt.c')
-rw-r--r--gcc/tree-chkp-opt.c1355
1 files changed, 0 insertions, 1355 deletions
diff --git a/gcc/tree-chkp-opt.c b/gcc/tree-chkp-opt.c
deleted file mode 100644
index 5a005a6a7b6..00000000000
--- a/gcc/tree-chkp-opt.c
+++ /dev/null
@@ -1,1355 +0,0 @@
-/* Pointer Bounds Checker optimization pass.
- Copyright (C) 2014-2018 Free Software Foundation, Inc.
- Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 3, or (at your option) any later
-version.
-
-GCC is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3. If not see
-<http://www.gnu.org/licenses/>. */
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "backend.h"
-#include "target.h"
-#include "rtl.h"
-#include "tree.h"
-#include "gimple.h"
-#include "tree-pass.h"
-#include "ssa.h"
-#include "gimple-pretty-print.h"
-#include "diagnostic.h"
-#include "fold-const.h"
-#include "tree-cfg.h"
-#include "tree-ssa-loop-niter.h"
-#include "gimple-iterator.h"
-#include "tree-chkp.h"
-#include "ipa-chkp.h"
-
-enum check_type
-{
- CHECK_LOWER_BOUND,
- CHECK_UPPER_BOUND
-};
-
-struct pol_item
-{
- tree cst;
- tree var;
-};
-
-struct address_t
-{
- vec<struct pol_item> pol;
-};
-
-/* Structure to hold check informtation. */
-struct check_info
-{
- /* Type of the check. */
- check_type type;
- /* Address used for the check. */
- address_t addr;
- /* Bounds used for the check. */
- tree bounds;
- /* Check statement. Can be NULL for removed checks. */
- gimple *stmt;
-};
-
-/* Structure to hold checks information for BB. */
-struct bb_checks
-{
- vec<struct check_info, va_heap, vl_ptr> checks;
-};
-
-static void chkp_collect_value (tree ssa_name, address_t &res);
-
-#define chkp_bndmk_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK))
-#define chkp_intersect_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT))
-#define chkp_checkl_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL))
-#define chkp_checku_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU))
-
-static vec<struct bb_checks, va_heap, vl_ptr> check_infos;
-
-/* Comparator for pol_item structures I1 and I2 to be used
- to find items with equal var. Also used for polynomial
- sorting. */
-static int
-chkp_pol_item_compare (const void *i1, const void *i2)
-{
- const struct pol_item *p1 = (const struct pol_item *)i1;
- const struct pol_item *p2 = (const struct pol_item *)i2;
-
- if (p1->var == p2->var)
- return 0;
- else if (p1->var > p2->var)
- return 1;
- else
- return -1;
-}
-
-/* Find polynomial item in ADDR with var equal to VAR
- and return its index. Return -1 if item was not
- found. */
-static int
-chkp_pol_find (address_t &addr, tree var)
-{
- int left = 0;
- int right = addr.pol.length () - 1;
- int n;
-
- while (right >= left)
- {
- n = (left + right) / 2;
-
- if (addr.pol[n].var == var
- || (var && addr.pol[n].var
- && TREE_CODE (var) == ADDR_EXPR
- && TREE_CODE (addr.pol[n].var) == ADDR_EXPR
- && TREE_OPERAND (var, 0) == TREE_OPERAND (addr.pol[n].var, 0)))
- return n;
- else if (addr.pol[n].var > var)
- right = n - 1;
- else
- left = n + 1;
- }
-
- return -1;
-}
-
-/* Return constant CST extended to size type. */
-static tree
-chkp_extend_const (tree cst)
-{
- if (TYPE_PRECISION (TREE_TYPE (cst)) < TYPE_PRECISION (size_type_node))
- return build_int_cst_type (size_type_node, tree_to_shwi (cst));
-
- return cst;
-}
-
-/* Add polynomial item CST * VAR to ADDR. */
-static void
-chkp_add_addr_item (address_t &addr, tree cst, tree var)
-{
- int n = chkp_pol_find (addr, var);
-
- cst = chkp_extend_const (cst);
-
- if (n < 0)
- {
- struct pol_item item;
- item.cst = cst;
- item.var = var;
-
- addr.pol.safe_push (item);
- addr.pol.qsort (&chkp_pol_item_compare);
- }
- else
- {
- addr.pol[n].cst = fold_build2 (PLUS_EXPR, TREE_TYPE (addr.pol[n].cst),
- addr.pol[n].cst, cst);
- if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST
- && integer_zerop (addr.pol[n].cst))
- addr.pol.ordered_remove (n);
- }
-}
-
-/* Subtract polynomial item CST * VAR from ADDR. */
-static void
-chkp_sub_addr_item (address_t &addr, tree cst, tree var)
-{
- int n = chkp_pol_find (addr, var);
-
- cst = chkp_extend_const (cst);
-
- if (n < 0)
- {
- struct pol_item item;
- item.cst = fold_build2 (MINUS_EXPR, TREE_TYPE (cst),
- integer_zero_node, cst);
- item.var = var;
-
- addr.pol.safe_push (item);
- addr.pol.qsort (&chkp_pol_item_compare);
- }
- else
- {
- addr.pol[n].cst = fold_build2 (MINUS_EXPR, TREE_TYPE (addr.pol[n].cst),
- addr.pol[n].cst, cst);
- if (TREE_CODE (addr.pol[n].cst) == INTEGER_CST
- && integer_zerop (addr.pol[n].cst))
- addr.pol.ordered_remove (n);
- }
-}
-
-/* Add address DELTA to ADDR. */
-static void
-chkp_add_addr_addr (address_t &addr, address_t &delta)
-{
- unsigned int i;
- for (i = 0; i < delta.pol.length (); i++)
- chkp_add_addr_item (addr, delta.pol[i].cst, delta.pol[i].var);
-}
-
-/* Subtract address DELTA from ADDR. */
-static void
-chkp_sub_addr_addr (address_t &addr, address_t &delta)
-{
- unsigned int i;
- for (i = 0; i < delta.pol.length (); i++)
- chkp_sub_addr_item (addr, delta.pol[i].cst, delta.pol[i].var);
-}
-
-/* Mutiply address ADDR by integer constant MULT. */
-static void
-chkp_mult_addr (address_t &addr, tree mult)
-{
- unsigned int i;
- for (i = 0; i < addr.pol.length (); i++)
- addr.pol[i].cst = fold_build2 (MULT_EXPR, TREE_TYPE (addr.pol[i].cst),
- addr.pol[i].cst, mult);
-}
-
-/* Return 1 if we may prove ADDR has a constant value with
- determined sign, which is put into *SIGN. Otherwise
- return 0. */
-static bool
-chkp_is_constant_addr (const address_t &addr, int *sign)
-{
- *sign = 0;
-
- if (addr.pol.length () == 0)
- return true;
- else if (addr.pol.length () > 1)
- return false;
- else if (addr.pol[0].var)
- return false;
- else if (TREE_CODE (addr.pol[0].cst) != INTEGER_CST)
- return false;
- else if (integer_zerop (addr.pol[0].cst))
- *sign = 0;
- else if (tree_int_cst_sign_bit (addr.pol[0].cst))
- *sign = -1;
- else
- *sign = 1;
-
- return true;
-}
-
-/* Dump ADDR into dump_file. */
-static void
-chkp_print_addr (const address_t &addr)
-{
- unsigned int n = 0;
- for (n = 0; n < addr.pol.length (); n++)
- {
- if (n > 0)
- fprintf (dump_file, " + ");
-
- if (addr.pol[n].var == NULL_TREE)
- print_generic_expr (dump_file, addr.pol[n].cst);
- else
- {
- if (TREE_CODE (addr.pol[n].cst) != INTEGER_CST
- || !integer_onep (addr.pol[n].cst))
- {
- print_generic_expr (dump_file, addr.pol[n].cst);
- fprintf (dump_file, " * ");
- }
- print_generic_expr (dump_file, addr.pol[n].var);
- }
- }
-}
-
-/* Compute value of PTR and put it into address RES.
- PTR has to be ADDR_EXPR. */
-static void
-chkp_collect_addr_value (tree ptr, address_t &res)
-{
- tree obj = TREE_OPERAND (ptr, 0);
- address_t addr;
-
- switch (TREE_CODE (obj))
- {
- case INDIRECT_REF:
- chkp_collect_value (TREE_OPERAND (obj, 0), res);
- break;
-
- case MEM_REF:
- chkp_collect_value (TREE_OPERAND (obj, 0), res);
- addr.pol.create (0);
- chkp_collect_value (TREE_OPERAND (obj, 1), addr);
- chkp_add_addr_addr (res, addr);
- addr.pol.release ();
- break;
-
- case ARRAY_REF:
- chkp_collect_value (build_fold_addr_expr (TREE_OPERAND (obj, 0)), res);
- addr.pol.create (0);
- chkp_collect_value (TREE_OPERAND (obj, 1), addr);
- chkp_mult_addr (addr, array_ref_element_size (obj));
- chkp_add_addr_addr (res, addr);
- addr.pol.release ();
- break;
-
- case COMPONENT_REF:
- {
- tree str = TREE_OPERAND (obj, 0);
- tree field = TREE_OPERAND (obj, 1);
- chkp_collect_value (build_fold_addr_expr (str), res);
- addr.pol.create (0);
- chkp_collect_value (component_ref_field_offset (obj), addr);
- chkp_add_addr_addr (res, addr);
- addr.pol.release ();
- if (DECL_FIELD_BIT_OFFSET (field))
- {
- addr.pol.create (0);
- chkp_collect_value (fold_build2 (TRUNC_DIV_EXPR, size_type_node,
- DECL_FIELD_BIT_OFFSET (field),
- size_int (BITS_PER_UNIT)),
- addr);
- chkp_add_addr_addr (res, addr);
- addr.pol.release ();
- }
- }
- break;
-
- default:
- chkp_add_addr_item (res, integer_one_node, ptr);
- break;
- }
-}
-
-/* Compute value of PTR and put it into address RES. */
-static void
-chkp_collect_value (tree ptr, address_t &res)
-{
- gimple *def_stmt;
- enum gimple_code code;
- enum tree_code rhs_code;
- address_t addr;
- tree rhs1;
-
- if (TREE_CODE (ptr) == INTEGER_CST)
- {
- chkp_add_addr_item (res, ptr, NULL);
- return;
- }
- else if (TREE_CODE (ptr) == ADDR_EXPR)
- {
- chkp_collect_addr_value (ptr, res);
- return;
- }
- else if (TREE_CODE (ptr) != SSA_NAME)
- {
- chkp_add_addr_item (res, integer_one_node, ptr);
- return;
- }
-
- /* Now we handle the case when polynomial is computed
- for SSA NAME. */
- def_stmt = SSA_NAME_DEF_STMT (ptr);
- code = gimple_code (def_stmt);
-
- /* Currently we do not walk through statements other
- than assignment. */
- if (code != GIMPLE_ASSIGN)
- {
- chkp_add_addr_item (res, integer_one_node, ptr);
- return;
- }
-
- rhs_code = gimple_assign_rhs_code (def_stmt);
- rhs1 = gimple_assign_rhs1 (def_stmt);
-
- switch (rhs_code)
- {
- case SSA_NAME:
- case INTEGER_CST:
- case ADDR_EXPR:
- chkp_collect_value (rhs1, res);
- break;
-
- case PLUS_EXPR:
- case POINTER_PLUS_EXPR:
- chkp_collect_value (rhs1, res);
- addr.pol.create (0);
- chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr);
- chkp_add_addr_addr (res, addr);
- addr.pol.release ();
- break;
-
- case MINUS_EXPR:
- chkp_collect_value (rhs1, res);
- addr.pol.create (0);
- chkp_collect_value (gimple_assign_rhs2 (def_stmt), addr);
- chkp_sub_addr_addr (res, addr);
- addr.pol.release ();
- break;
-
- case MULT_EXPR:
- if (TREE_CODE (rhs1) == SSA_NAME
- && TREE_CODE (gimple_assign_rhs2 (def_stmt)) == INTEGER_CST)
- {
- chkp_collect_value (rhs1, res);
- chkp_mult_addr (res, gimple_assign_rhs2 (def_stmt));
- }
- else if (TREE_CODE (gimple_assign_rhs2 (def_stmt)) == SSA_NAME
- && TREE_CODE (rhs1) == INTEGER_CST)
- {
- chkp_collect_value (gimple_assign_rhs2 (def_stmt), res);
- chkp_mult_addr (res, rhs1);
- }
- else
- chkp_add_addr_item (res, integer_one_node, ptr);
- break;
-
- default:
- chkp_add_addr_item (res, integer_one_node, ptr);
- break;
- }
-}
-
-/* Fill check_info structure *CI with information about
- check STMT. */
-static void
-chkp_fill_check_info (gimple *stmt, struct check_info *ci)
-{
- ci->addr.pol.create (0);
- ci->bounds = gimple_call_arg (stmt, 1);
- chkp_collect_value (gimple_call_arg (stmt, 0), ci->addr);
- ci->type = (gimple_call_fndecl (stmt) == chkp_checkl_fndecl
- ? CHECK_LOWER_BOUND
- : CHECK_UPPER_BOUND);
- ci->stmt = stmt;
-}
-
-/* Release structures holding check information
- for current function. */
-static void
-chkp_release_check_info (void)
-{
- unsigned int n, m;
-
- if (check_infos.exists ())
- {
- for (n = 0; n < check_infos.length (); n++)
- {
- for (m = 0; m < check_infos[n].checks.length (); m++)
- if (check_infos[n].checks[m].addr.pol.exists ())
- check_infos[n].checks[m].addr.pol.release ();
- check_infos[n].checks.release ();
- }
- check_infos.release ();
- }
-}
-
-/* Create structures to hold check information
- for current function. */
-static void
-chkp_init_check_info (void)
-{
- struct bb_checks empty_bbc;
- int n;
-
- empty_bbc.checks = vNULL;
-
- chkp_release_check_info ();
-
- check_infos.create (last_basic_block_for_fn (cfun));
- for (n = 0; n < last_basic_block_for_fn (cfun); n++)
- {
- check_infos.safe_push (empty_bbc);
- check_infos.last ().checks.create (0);
- }
-}
-
-/* Find all checks in current function and store info about them
- in check_infos. */
-static void
-chkp_gather_checks_info (void)
-{
- basic_block bb;
- gimple_stmt_iterator i;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Gathering information about checks...\n");
-
- chkp_init_check_info ();
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- struct bb_checks *bbc = &check_infos[bb->index];
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Searching checks in BB%d...\n", bb->index);
-
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- {
- gimple *stmt = gsi_stmt (i);
-
- if (gimple_code (stmt) != GIMPLE_CALL)
- continue;
-
- if (gimple_call_fndecl (stmt) == chkp_checkl_fndecl
- || gimple_call_fndecl (stmt) == chkp_checku_fndecl)
- {
- struct check_info ci;
-
- chkp_fill_check_info (stmt, &ci);
- bbc->checks.safe_push (ci);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Adding check information:\n");
- fprintf (dump_file, " bounds: ");
- print_generic_expr (dump_file, ci.bounds);
- fprintf (dump_file, "\n address: ");
- chkp_print_addr (ci.addr);
- fprintf (dump_file, "\n check: ");
- print_gimple_stmt (dump_file, stmt, 0);
- }
- }
- }
- }
-}
-
-/* Return 1 if check CI against BOUNDS always pass,
- -1 if check CI against BOUNDS always fails and
- 0 if we cannot compute check result. */
-static int
-chkp_get_check_result (struct check_info *ci, tree bounds)
-{
- gimple *bnd_def;
- address_t bound_val;
- int sign, res = 0;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Trying to compute result of the check\n");
- fprintf (dump_file, " check: ");
- print_gimple_stmt (dump_file, ci->stmt, 0);
- fprintf (dump_file, " address: ");
- chkp_print_addr (ci->addr);
- fprintf (dump_file, "\n bounds: ");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, "\n");
- }
-
- if (TREE_CODE (bounds) != SSA_NAME)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: bounds tree code is not ssa_name\n");
- return 0;
- }
-
- bnd_def = SSA_NAME_DEF_STMT (bounds);
- /* Currently we handle cases when bounds are result of bndmk
- or loaded static bounds var. */
- if (gimple_code (bnd_def) == GIMPLE_CALL
- && gimple_call_fndecl (bnd_def) == chkp_bndmk_fndecl)
- {
- bound_val.pol.create (0);
- chkp_collect_value (gimple_call_arg (bnd_def, 0), bound_val);
- if (ci->type == CHECK_UPPER_BOUND)
- {
- address_t size_val;
- size_val.pol.create (0);
- chkp_collect_value (gimple_call_arg (bnd_def, 1), size_val);
- chkp_add_addr_addr (bound_val, size_val);
- size_val.pol.release ();
- chkp_add_addr_item (bound_val, integer_minus_one_node, NULL);
- }
- }
- else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
- && gimple_assign_rhs1 (bnd_def) == chkp_get_zero_bounds_var ())
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: always pass with zero bounds\n");
- return 1;
- }
- else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
- && gimple_assign_rhs1 (bnd_def) == chkp_get_none_bounds_var ())
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: always fails with none bounds\n");
- return -1;
- }
- else if (gimple_code (bnd_def) == GIMPLE_ASSIGN
- && TREE_CODE (gimple_assign_rhs1 (bnd_def)) == VAR_DECL)
- {
- tree bnd_var = gimple_assign_rhs1 (bnd_def);
- tree var;
- tree size;
-
- if (!DECL_INITIAL (bnd_var)
- || DECL_INITIAL (bnd_var) == error_mark_node)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: cannot compute bounds\n");
- return 0;
- }
-
- gcc_assert (TREE_CODE (DECL_INITIAL (bnd_var)) == ADDR_EXPR);
- var = TREE_OPERAND (DECL_INITIAL (bnd_var), 0);
-
- bound_val.pol.create (0);
- chkp_collect_value (DECL_INITIAL (bnd_var), bound_val);
- if (ci->type == CHECK_UPPER_BOUND)
- {
- if (VAR_P (var))
- {
- if (DECL_SIZE (var)
- && !chkp_variable_size_type (TREE_TYPE (var)))
- size = DECL_SIZE_UNIT (var);
- else
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: cannot compute bounds\n");
- return 0;
- }
- }
- else
- {
- gcc_assert (TREE_CODE (var) == STRING_CST);
- size = build_int_cst (size_type_node,
- TREE_STRING_LENGTH (var));
- }
-
- address_t size_val;
- size_val.pol.create (0);
- chkp_collect_value (size, size_val);
- chkp_add_addr_addr (bound_val, size_val);
- size_val.pol.release ();
- chkp_add_addr_item (bound_val, integer_minus_one_node, NULL);
- }
- }
- else
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: cannot compute bounds\n");
- return 0;
- }
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, " bound value: ");
- chkp_print_addr (bound_val);
- fprintf (dump_file, "\n");
- }
-
- chkp_sub_addr_addr (bound_val, ci->addr);
-
- if (!chkp_is_constant_addr (bound_val, &sign))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: cannot compute result\n");
-
- res = 0;
- }
- else if (sign == 0
- || (ci->type == CHECK_UPPER_BOUND && sign > 0)
- || (ci->type == CHECK_LOWER_BOUND && sign < 0))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: always pass\n");
-
- res = 1;
- }
- else
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " result: always fail\n");
-
- res = -1;
- }
-
- bound_val.pol.release ();
-
- return res;
-}
-
-/* Try to compare bounds value and address value
- used in the check CI. If we can prove that check
- always pass then remove it. */
-static void
-chkp_remove_check_if_pass (struct check_info *ci)
-{
- int result = 0;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Trying to remove check: ");
- print_gimple_stmt (dump_file, ci->stmt, 0);
- }
-
- result = chkp_get_check_result (ci, ci->bounds);
-
- if (result == 1)
- {
- gimple_stmt_iterator i = gsi_for_stmt (ci->stmt);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " action: delete check (always pass)\n");
-
- gsi_remove (&i, true);
- unlink_stmt_vdef (ci->stmt);
- release_defs (ci->stmt);
- ci->stmt = NULL;
- }
- else if (result == -1)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " action: keep check (always fail)\n");
- warning_at (gimple_location (ci->stmt), OPT_Wchkp,
- "memory access check always fail");
- }
- else if (result == 0)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " action: keep check (cannot compute result)\n");
- }
-}
-
-/* For bounds used in CI check if bounds are produced by
- intersection and we may use outer bounds instead. If
- transformation is possible then fix check statement and
- recompute its info. */
-static void
-chkp_use_outer_bounds_if_possible (struct check_info *ci)
-{
- gimple *bnd_def;
- tree bnd1, bnd2, bnd_res = NULL;
- int check_res1, check_res2;
-
- if (TREE_CODE (ci->bounds) != SSA_NAME)
- return;
-
- bnd_def = SSA_NAME_DEF_STMT (ci->bounds);
- if (gimple_code (bnd_def) != GIMPLE_CALL
- || gimple_call_fndecl (bnd_def) != chkp_intersect_fndecl)
- return;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Check if bounds intersection is redundant: \n");
- fprintf (dump_file, " check: ");
- print_gimple_stmt (dump_file, ci->stmt, 0);
- fprintf (dump_file, " intersection: ");
- print_gimple_stmt (dump_file, bnd_def, 0);
- fprintf (dump_file, "\n");
- }
-
- bnd1 = gimple_call_arg (bnd_def, 0);
- bnd2 = gimple_call_arg (bnd_def, 1);
-
- check_res1 = chkp_get_check_result (ci, bnd1);
- check_res2 = chkp_get_check_result (ci, bnd2);
- if (check_res1 == 1)
- bnd_res = bnd2;
- else if (check_res1 == -1)
- bnd_res = bnd1;
- else if (check_res2 == 1)
- bnd_res = bnd1;
- else if (check_res2 == -1)
- bnd_res = bnd2;
-
- if (bnd_res)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, " action: use ");
- print_generic_expr (dump_file, bnd2);
- fprintf (dump_file, " instead of ");
- print_generic_expr (dump_file, ci->bounds);
- fprintf (dump_file, "\n");
- }
-
- ci->bounds = bnd_res;
- gimple_call_set_arg (ci->stmt, 1, bnd_res);
- update_stmt (ci->stmt);
- chkp_fill_check_info (ci->stmt, ci);
- }
-}
-
-/* Try to find checks whose bounds were produced by intersection
- which does not affect check result. In such check outer bounds
- are used instead. It allows to remove excess intersections
- and helps to compare checks. */
-static void
-chkp_remove_excess_intersections (void)
-{
- basic_block bb;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Searching for redundant bounds intersections...\n");
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- struct bb_checks *bbc = &check_infos[bb->index];
- unsigned int no;
-
- /* Iterate through all found checks in BB. */
- for (no = 0; no < bbc->checks.length (); no++)
- if (bbc->checks[no].stmt)
- chkp_use_outer_bounds_if_possible (&bbc->checks[no]);
- }
-}
-
-/* Try to remove all checks which are known to alwyas pass. */
-static void
-chkp_remove_constant_checks (void)
-{
- basic_block bb;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Searching for redundant checks...\n");
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- struct bb_checks *bbc = &check_infos[bb->index];
- unsigned int no;
-
- /* Iterate through all found checks in BB. */
- for (no = 0; no < bbc->checks.length (); no++)
- if (bbc->checks[no].stmt)
- chkp_remove_check_if_pass (&bbc->checks[no]);
- }
-}
-
-/* Return fast version of string function FNCODE. */
-static tree
-chkp_get_nobnd_fndecl (enum built_in_function fncode)
-{
- /* Check if we are allowed to use fast string functions. */
- if (!flag_chkp_use_fast_string_functions)
- return NULL_TREE;
-
- tree fndecl = NULL_TREE;
-
- switch (fncode)
- {
- case BUILT_IN_MEMCPY_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND);
- break;
-
- case BUILT_IN_MEMPCPY_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND);
- break;
-
- case BUILT_IN_MEMMOVE_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND);
- break;
-
- case BUILT_IN_MEMSET_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND);
- break;
-
- case BUILT_IN_CHKP_MEMCPY_NOCHK_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMPCPY_NOCHK_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMMOVE_NOCHK_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMSET_NOCHK_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
- break;
-
- default:
- break;
- }
-
- if (fndecl)
- fndecl = chkp_maybe_clone_builtin_fndecl (fndecl);
-
- return fndecl;
-}
-
-
-/* Return no-check version of string function FNCODE. */
-static tree
-chkp_get_nochk_fndecl (enum built_in_function fncode)
-{
- /* Check if we are allowed to use fast string functions. */
- if (!flag_chkp_use_nochk_string_functions)
- return NULL_TREE;
-
- tree fndecl = NULL_TREE;
-
- switch (fncode)
- {
- case BUILT_IN_MEMCPY_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOCHK);
- break;
-
- case BUILT_IN_MEMPCPY_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOCHK);
- break;
-
- case BUILT_IN_MEMMOVE_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOCHK);
- break;
-
- case BUILT_IN_MEMSET_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMCPY_NOBND_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMPCPY_NOBND_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMMOVE_NOBND_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
- break;
-
- case BUILT_IN_CHKP_MEMSET_NOBND_CHKP:
- fndecl = builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
- break;
-
- default:
- break;
- }
-
- if (fndecl)
- fndecl = chkp_maybe_clone_builtin_fndecl (fndecl);
-
- return fndecl;
-}
-
-/* Find memcpy, mempcpy, memmove and memset calls, perform
- checks before call and then call no_chk version of
- functions. We do it on O2 to enable inlining of these
- functions during expand.
-
- Also try to find memcpy, mempcpy, memmove and memset calls
- which are known to not write pointers to memory and use
- faster function versions for them. */
-static void
-chkp_optimize_string_function_calls (void)
-{
- basic_block bb;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Searching for replaceable string function calls...\n");
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- gimple_stmt_iterator i;
-
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- {
- gimple *stmt = gsi_stmt (i);
- tree fndecl;
-
- if (!is_gimple_call (stmt)
- || !gimple_call_with_bounds_p (stmt)
- || !gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
- continue;
-
- fndecl = gimple_call_fndecl (stmt);
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMCPY_CHKP
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY_CHKP
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMMOVE_CHKP
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET_CHKP)
- {
- tree dst = gimple_call_arg (stmt, 0);
- tree dst_bnd = gimple_call_arg (stmt, 1);
- bool is_memset = DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET_CHKP;
- tree size = gimple_call_arg (stmt, is_memset ? 3 : 4);
- tree fndecl_nochk;
- gimple_stmt_iterator j;
- basic_block check_bb;
- address_t size_val;
- int sign;
- bool known;
-
- /* We may replace call with corresponding __chkp_*_nobnd
- call in case destination pointer base type is not
- void or pointer. */
- if (POINTER_TYPE_P (TREE_TYPE (dst))
- && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (dst)))
- && !chkp_type_has_pointer (TREE_TYPE (TREE_TYPE (dst))))
- {
- tree fndecl_nobnd
- = chkp_get_nobnd_fndecl (DECL_FUNCTION_CODE (fndecl));
-
- if (fndecl_nobnd)
- fndecl = fndecl_nobnd;
- }
-
- fndecl_nochk = chkp_get_nochk_fndecl (DECL_FUNCTION_CODE (fndecl));
-
- if (fndecl_nochk)
- fndecl = fndecl_nochk;
-
- if (fndecl != gimple_call_fndecl (stmt))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Replacing call: ");
- print_gimple_stmt (dump_file, stmt, 0,
- TDF_VOPS|TDF_MEMSYMS);
- }
-
- gimple_call_set_fndecl (stmt, fndecl);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "With a new call: ");
- print_gimple_stmt (dump_file, stmt, 0,
- TDF_VOPS|TDF_MEMSYMS);
- }
- }
-
- /* If there is no nochk version of function then
- do nothing. Otherwise insert checks before
- the call. */
- if (!fndecl_nochk)
- continue;
-
- /* If size passed to call is known and > 0
- then we may insert checks unconditionally. */
- size_val.pol.create (0);
- chkp_collect_value (size, size_val);
- known = chkp_is_constant_addr (size_val, &sign);
- size_val.pol.release ();
-
- /* If we are not sure size is not zero then we have
- to perform runtime check for size and perform
- checks only when size is not zero. */
- if (!known)
- {
- gimple *check = gimple_build_cond (NE_EXPR,
- size,
- size_zero_node,
- NULL_TREE,
- NULL_TREE);
-
- /* Split block before string function call. */
- gsi_prev (&i);
- check_bb = insert_cond_bb (bb, gsi_stmt (i), check,
- profile_probability::likely ());
-
- /* Set position for checks. */
- j = gsi_last_bb (check_bb);
-
- /* The block was splitted and therefore we
- need to set iterator to its end. */
- i = gsi_last_bb (bb);
- }
- /* If size is known to be zero then no checks
- should be performed. */
- else if (!sign)
- continue;
- else
- j = i;
-
- size = size_binop (MINUS_EXPR, size, size_one_node);
- if (!is_memset)
- {
- tree src = gimple_call_arg (stmt, 2);
- tree src_bnd = gimple_call_arg (stmt, 3);
-
- chkp_check_mem_access (src, fold_build_pointer_plus (src, size),
- src_bnd, j, gimple_location (stmt),
- integer_zero_node);
- }
-
- chkp_check_mem_access (dst, fold_build_pointer_plus (dst, size),
- dst_bnd, j, gimple_location (stmt),
- integer_one_node);
-
- }
- }
- }
-}
-
-/* Intrumentation pass inserts most of bounds creation code
- in the header of the function. We want to move bounds
- creation closer to bounds usage to reduce bounds lifetime.
- We also try to avoid bounds creation code on paths where
- bounds are not used. */
-static void
-chkp_reduce_bounds_lifetime (void)
-{
- basic_block bb = FALLTHRU_EDGE (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest;
- gimple_stmt_iterator i;
-
- for (i = gsi_start_bb (bb); !gsi_end_p (i); )
- {
- gimple *dom_use, *use_stmt, *stmt = gsi_stmt (i);
- basic_block dom_bb;
- ssa_op_iter iter;
- imm_use_iterator use_iter;
- use_operand_p use_p;
- tree op;
- bool want_move = false;
- bool deps = false;
-
- if (gimple_code (stmt) == GIMPLE_CALL
- && gimple_call_fndecl (stmt) == chkp_bndmk_fndecl)
- want_move = true;
-
- if (gimple_code (stmt) == GIMPLE_ASSIGN
- && POINTER_BOUNDS_P (gimple_assign_lhs (stmt))
- && gimple_assign_rhs_code (stmt) == VAR_DECL)
- want_move = true;
-
- if (!want_move)
- {
- gsi_next (&i);
- continue;
- }
-
- /* Check we do not increase other values lifetime. */
- FOR_EACH_PHI_OR_STMT_USE (use_p, stmt, iter, SSA_OP_USE)
- {
- op = USE_FROM_PTR (use_p);
-
- if (TREE_CODE (op) == SSA_NAME
- && gimple_code (SSA_NAME_DEF_STMT (op)) != GIMPLE_NOP)
- {
- deps = true;
- break;
- }
- }
-
- if (deps)
- {
- gsi_next (&i);
- continue;
- }
-
- /* Check all usages of bounds. */
- if (gimple_code (stmt) == GIMPLE_CALL)
- op = gimple_call_lhs (stmt);
- else
- {
- gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
- op = gimple_assign_lhs (stmt);
- }
-
- dom_use = NULL;
- dom_bb = NULL;
-
- FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, op)
- {
- if (is_gimple_debug (use_stmt))
- continue;
-
- if (dom_bb &&
- dominated_by_p (CDI_DOMINATORS,
- dom_bb, gimple_bb (use_stmt)))
- {
- dom_use = use_stmt;
- dom_bb = NULL;
- }
- else if (dom_bb)
- dom_bb = nearest_common_dominator (CDI_DOMINATORS, dom_bb,
- gimple_bb (use_stmt));
- else if (!dom_use)
- dom_use = use_stmt;
- else if (stmt_dominates_stmt_p (use_stmt, dom_use))
- dom_use = use_stmt;
- else if (!stmt_dominates_stmt_p (dom_use, use_stmt)
- /* If dom_use and use_stmt are PHI nodes in one BB
- then it is OK to keep any of them as dom_use.
- stmt_dominates_stmt_p returns 0 for such
- combination, so check it here manually. */
- && (gimple_code (dom_use) != GIMPLE_PHI
- || gimple_code (use_stmt) != GIMPLE_PHI
- || gimple_bb (use_stmt) != gimple_bb (dom_use))
- )
- {
- dom_bb = nearest_common_dominator (CDI_DOMINATORS,
- gimple_bb (use_stmt),
- gimple_bb (dom_use));
- dom_use = NULL;
- }
- }
-
- /* In case there is a single use, just move bounds
- creation to the use. */
- if (dom_use || dom_bb)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Moving creation of ");
- print_generic_expr (dump_file, op);
- fprintf (dump_file, " down to its use.\n");
- }
-
- if (dom_use && gimple_code (dom_use) == GIMPLE_PHI)
- {
- dom_bb = get_immediate_dominator (CDI_DOMINATORS,
- gimple_bb (dom_use));
- dom_use = NULL;
- }
-
- if (dom_bb == bb
- || (dom_use && gimple_bb (dom_use) == bb))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Cannot move statement bacause there is no "
- "suitable dominator block other than entry block.\n");
-
- gsi_next (&i);
- }
- else
- {
- if (dom_bb)
- {
- gimple_stmt_iterator last = gsi_last_bb (dom_bb);
- if (!gsi_end_p (last) && stmt_ends_bb_p (gsi_stmt (last)))
- gsi_move_before (&i, &last);
- else
- gsi_move_after (&i, &last);
- }
- else
- {
- gimple_stmt_iterator gsi = gsi_for_stmt (dom_use);
- gsi_move_before (&i, &gsi);
- }
-
- gimple_set_vdef (stmt, NULL_TREE);
- gimple_set_vuse (stmt, NULL_TREE);
- update_stmt (stmt);
- }
- }
- else
- gsi_next (&i);
- }
-}
-
-/* Initilize checker optimization pass. */
-static void
-chkp_opt_init (void)
-{
- check_infos.create (0);
-
- calculate_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_POST_DOMINATORS);
-
- /* With LTO constant bounds vars may be not initialized by now.
- Get constant bounds vars to handle their assignments during
- optimizations. */
- chkp_get_zero_bounds_var ();
- chkp_get_none_bounds_var ();
-}
-
-/* Finalise checker optimization pass. */
-static void
-chkp_opt_fini (void)
-{
- chkp_fix_cfg ();
-
- free_dominance_info (CDI_POST_DOMINATORS);
-}
-
-/* Checker optimization pass function. */
-static unsigned int
-chkp_opt_execute (void)
-{
- chkp_opt_init();
-
- /* This optimization may introduce new checks
- and thus we put it before checks search. */
- chkp_optimize_string_function_calls ();
-
- chkp_gather_checks_info ();
-
- chkp_remove_excess_intersections ();
-
- chkp_remove_constant_checks ();
-
- chkp_reduce_bounds_lifetime ();
-
- chkp_release_check_info ();
-
- chkp_opt_fini ();
-
- return 0;
-}
-
-/* Pass gate. */
-static bool
-chkp_opt_gate (void)
-{
- return chkp_function_instrumented_p (cfun->decl)
- && (flag_chkp_optimize > 0
- || (flag_chkp_optimize == -1 && optimize > 0));
-}
-
-namespace {
-
-const pass_data pass_data_chkp_opt =
-{
- GIMPLE_PASS, /* type */
- "chkpopt", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_NONE, /* tv_id */
- PROP_ssa | PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_il
- | TODO_update_ssa /* todo_flags_finish */
-};
-
-class pass_chkp_opt : public gimple_opt_pass
-{
-public:
- pass_chkp_opt (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_chkp_opt, ctxt)
- {}
-
- /* opt_pass methods: */
- virtual opt_pass * clone ()
- {
- return new pass_chkp_opt (m_ctxt);
- }
-
- virtual bool gate (function *)
- {
- return chkp_opt_gate ();
- }
-
- virtual unsigned int execute (function *)
- {
- return chkp_opt_execute ();
- }
-
-}; // class pass_chkp_opt
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_chkp_opt (gcc::context *ctxt)
-{
- return new pass_chkp_opt (ctxt);
-}