From patchwork Wed Oct 8 19:18:15 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilya Enkovich X-Patchwork-Id: 397699 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3C672140085 for ; Thu, 9 Oct 2014 06:18:33 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=fxfUCta3GsdoQXFII0KO+vOFq3qfT4rhMHd+dW7+F5mZqDnurb v8+no1O1zPRBb8fHJE3RdpamFIqHwgedQge/mDBuGC4c/i4Qa8DHeHTCqYDR4WhP PszcQaV7aBhI18GqK6gm7MKaX5lu+oKYDPQB8bpKJQVBy5jcWUoG0UdQ4= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=BthDQueNq5CrMwRfNCLn3OP2zx4=; b=XqQQXHDUWjyzDvmLD9f3 l8zk5wzl51RQSviRUe/P/E43h4kd7mGiwX+4FTdVgz7hOY6qIrZY+dYkPZhB4fZB DmfIYUUln0haONZFC8IreK/gj1A/f1TCSXX3OuWL3qNH5HCSbapl4QJMKd1MXRNT QMKESy3CcmUAWbrU+RC7Jt8= Received: (qmail 17102 invoked by alias); 8 Oct 2014 19:18:26 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 17092 invoked by uid 89); 8 Oct 2014 19:18:25 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pa0-f45.google.com Received: from mail-pa0-f45.google.com (HELO mail-pa0-f45.google.com) (209.85.220.45) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Wed, 08 Oct 2014 19:18:23 +0000 Received: by mail-pa0-f45.google.com with SMTP id rd3so9582596pab.4 for ; Wed, 08 Oct 2014 12:18:22 -0700 (PDT) X-Received: by 10.70.51.136 with SMTP id k8mr13667014pdo.132.1412795901955; Wed, 08 Oct 2014 12:18:21 -0700 (PDT) Received: from msticlxl57.ims.intel.com ([192.55.55.41]) by mx.google.com with ESMTPSA id ri9sm744287pbc.5.2014.10.08.12.18.19 for (version=TLSv1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 08 Oct 2014 12:18:21 -0700 (PDT) Date: Wed, 8 Oct 2014 23:18:15 +0400 From: Ilya Enkovich To: gcc-patches@gcc.gnu.org Cc: Jeff Law Subject: [PATCH, Pointer Bounds Checker 14/x] Passes [12/n] Optimize string functions Message-ID: <20141008191815.GL13454@msticlxl57.ims.intel.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Hi, This patch introduces simple optimization of string function calls using variants with no checks and/or bounds copy when possible. Thanks, Ilya --- 2014-10-08 Ilya Enkovich * tree-chkp.c (check_infos): New. (chkp_get_nobnd_fndecl): New. (chkp_get_nochk_fndecl): New. (chkp_optimize_string_function_calls): New. (chkp_opt_init): New. (chkp_opt_fini): New. (chkp_opt_execute): New. (chkp_opt_gate): New. (pass_data_chkp_opt): New. (pass_chkp_opt): New. (make_pass_chkp_opt): New. diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index aae9681..5230d14 100644 --- a/gcc/tree-chkp.c +++ b/gcc/tree-chkp.c @@ -399,6 +399,8 @@ static void chkp_parse_array_and_component_ref (tree node, tree *ptr, #define chkp_extract_upper_fndecl \ (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER)) +static vec check_infos = vNULL; + static GTY (()) tree chkp_uintptr_type; static GTY (()) tree chkp_zero_bounds_var; @@ -4567,8 +4569,315 @@ chkp_fill_check_info (gimple stmt, struct check_info *ci) ci->stmt = stmt; } +/* Return fast version of string function FNCODE. */ +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. */ +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. */ +void +chkp_optimize_string_function_calls (void) +{ + basic_block bb; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Searching for replacable 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; + + gimple_call_set_fndecl (stmt, fndecl); + + /* 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 runtiome 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. */ + j = i; + gsi_prev (&j); + fall = split_block (bb, gsi_stmt (j)); + bb = fall->src; + + /* Add size check. */ + j = gsi_last_bb (bb); + if (gsi_end_p (j)) + gsi_insert_before (&j, check, GSI_SAME_STMT); + else + gsi_insert_after (&j, check, GSI_SAME_STMT); + + /* Create basic block for checks. */ + check_bb = create_empty_bb (bb); + make_edge (bb, check_bb, EDGE_TRUE_VALUE); + make_single_succ_edge (check_bb, fall->dest, EDGE_FALLTHRU); + + /* Fix edge for splitted bb. */ + fall->flags = EDGE_FALSE_VALUE; + fall->count = bb->count; + fall->probability = REG_BR_PROB_BASE; + + /* Update dominance info. */ + if (dom_info_available_p (CDI_DOMINATORS)) + { + set_immediate_dominator (CDI_DOMINATORS, check_bb, bb); + set_immediate_dominator (CDI_DOMINATORS, fall->dest, bb); + } + + /* Update loop info. */ + if (current_loops) + add_bb_to_loop (check_bb, bb->loop_father); + + /* 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. */ +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. */ +void +chkp_opt_fini (void) +{ + chkp_fix_cfg (); + + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); +} + +/* Checker optimization pass function. */ +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. */ +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 */ +}; + const pass_data pass_data_chkp = { GIMPLE_PASS, /* type */ @@ -4608,6 +4917,31 @@ public: }; // class pass_chkp +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 * @@ -4616,4 +4950,10 @@ make_pass_chkp (gcc::context *ctxt) return new pass_chkp (ctxt); } +gimple_opt_pass * +make_pass_chkp_opt (gcc::context *ctxt) +{ + return new pass_chkp_opt (ctxt); +} + #include "gt-tree-chkp.h"