@@ -8165,6 +8165,44 @@ make_pass_split_crit_edges (gcc::context *ctxt)
}
+/* Insert COND expression which is GIMPLE_COND after STMT
+ in basic block BB with appropriate basic block split
+ and creation of a new conditionally executed basic block.
+ Return created basic block. */
+basic_block
+insert_cond_bb (basic_block bb, gimple stmt, gimple cond)
+{
+ edge fall = split_block (bb, stmt);
+ gimple_stmt_iterator iter = gsi_last_bb (bb);
+ basic_block new_bb;
+
+ /* Insert cond statement. */
+ gcc_assert (gimple_code (cond) == GIMPLE_COND);
+ if (gsi_end_p (iter))
+ gsi_insert_before (&iter, cond, GSI_CONTINUE_LINKING);
+ else
+ gsi_insert_after (&iter, cond, GSI_CONTINUE_LINKING);
+
+ /* Create conditionally executed block. */
+ new_bb = create_empty_bb (bb);
+ make_edge (bb, new_bb, EDGE_TRUE_VALUE);
+ make_single_succ_edge (new_bb, fall->dest, EDGE_FALLTHRU);
+
+ /* Fix edge for split bb. */
+ fall->flags = EDGE_FALSE_VALUE;
+
+ /* Update dominance info. */
+ if (dom_info_available_p (CDI_DOMINATORS))
+ {
+ set_immediate_dominator (CDI_DOMINATORS, new_bb, bb);
+ set_immediate_dominator (CDI_DOMINATORS, fall->dest, bb);
+ }
+
+ /* Update loop info. */
+ if (current_loops)
+ add_bb_to_loop (new_bb, bb->loop_father);
+}
+
/* Build a ternary operation and gimplify it. Emit code before GSI.
Return the gimple_val holding the result. */
@@ -95,5 +95,6 @@ extern tree gimplify_build1 (gimple_stmt_iterator *, enum tree_code,
extern void extract_true_false_edges_from_block (basic_block, edge *, edge *);
extern unsigned int execute_fixup_cfg (void);
extern unsigned int split_critical_edges (void);
+extern basic_block insert_cond_bb (basic_block, gimple, gimple);
#endif /* _TREE_CFG_H */
@@ -111,6 +111,8 @@ static void chkp_collect_value (tree ssa_name, address_t &res);
#define chkp_checku_fndecl \
(targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU))
+static vec<struct bb_checks, va_heap, vl_ptr> check_infos = vNULL;
+
/* Comparator for pol_item structures I1 and I2 to be used
to find items with equal var. Also used for polynomial
sorting. */
@@ -461,3 +463,329 @@ chkp_fill_check_info (gimple stmt, struct check_info *ci)
: CHECK_UPPER_BOUND);
ci->stmt = stmt;
}
+
+/* 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;
+
+ switch (fncode)
+ {
+ case BUILT_IN_MEMCPY:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND);
+
+ case BUILT_IN_MEMPCPY:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND);
+
+ case BUILT_IN_MEMMOVE:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND);
+
+ case BUILT_IN_MEMSET:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND);
+
+ case BUILT_IN_CHKP_MEMCPY_NOCHK:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMPCPY_NOCHK:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMMOVE_NOCHK:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMSET_NOCHK:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
+
+ default:
+ return NULL_TREE;
+ }
+}
+
+
+/* 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;
+
+ switch (fncode)
+ {
+ case BUILT_IN_MEMCPY:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOCHK);
+
+ case BUILT_IN_MEMPCPY:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOCHK);
+
+ case BUILT_IN_MEMMOVE:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOCHK);
+
+ case BUILT_IN_MEMSET:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOCHK);
+
+ case BUILT_IN_CHKP_MEMCPY_NOBND:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMPCPY_NOBND:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMMOVE_NOBND:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMMOVE_NOBND_NOCHK);
+
+ case BUILT_IN_CHKP_MEMSET_NOBND:
+ return builtin_decl_implicit (BUILT_IN_CHKP_MEMSET_NOBND_NOCHK);
+
+ default:
+ return NULL_TREE;
+ }
+}
+
+/* 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 (gimple_code (stmt) != GIMPLE_CALL
+ || !gimple_call_with_bounds_p (stmt))
+ continue;
+
+ fndecl = gimple_call_fndecl (stmt);
+
+ if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+ continue;
+
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMCPY
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMPCPY
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMMOVE
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_MEMSET)
+ {
+ 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;
+ tree size = gimple_call_arg (stmt, is_memset ? 3 : 4);
+ tree fndecl_nochk;
+ gimple_stmt_iterator j;
+ basic_block check_bb;
+ edge fall;
+ 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);
+
+ /* 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);
+
+ }
+ }
+ }
+}
+
+/* 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 ();
+}
+
+/* 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_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);
+}