From patchwork Thu Nov 21 18:03:43 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Matz X-Patchwork-Id: 293210 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)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9467E2C00C6 for ; Fri, 22 Nov 2013 05:04:05 +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=fL2ldWG09rHrih4Vd+2rIJH5ry2s6PJuLZJm2SUoPJwW0QC8Rq QopUu9bJFykXM8VI89u9eUeClqziNBBxYxCppR6N/Zn34DKlCY4E4WOoE8OkqKCG /RD+YzAXHMdarNLi1QN8WwmFHvdn8YFfizezgZbH8NT00zrY0kF+64pzc= 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=E1UUmNuCfjX54PA8sTUJGyZlocI=; b=Lb1SRVsYUBem2sE8Jwgm ml969h83Jcqa6NcctKwAzc0ib0iGu6XVrioq7PiZfl++kFdwpKYRyTr8Bf21Rxhi X+FwCRdwRksryWIXhGaZNls0LYVVzWRIuDnD5CtXLTyBIPcPK0vPO3ehAg6/s7oY W2NWdEkOaN/xift+8iShlqo= Received: (qmail 2589 invoked by alias); 21 Nov 2013 18:03:55 -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 2577 invoked by uid 89); 21 Nov 2013 18:03:55 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.2 required=5.0 tests=AWL, BAYES_99, RDNS_NONE autolearn=no version=3.3.2 X-HELO: mx2.suse.de Received: from Unknown (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 21 Nov 2013 18:03:52 +0000 Received: from relay1.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 128EFA7B68 for ; Thu, 21 Nov 2013 19:03:44 +0100 (CET) Date: Thu, 21 Nov 2013 19:03:43 +0100 (CET) From: Michael Matz To: gcc-patches@gcc.gnu.org Cc: Richard Biener Subject: Overhaul middle-end handling of restrict Message-ID: User-Agent: Alpine 2.00 (LNX 1167 2008-08-23) MIME-Version: 1.0 X-IsSubscribed: yes Hello, after much pondering about the issue we came up with this design to handle restrict more generally. Without a completely different way of representing conflicts (or non-conflicts) of memory references we're bound to somehow encode the necessary information into the points-to set (or in any case information related to pointer ssa names). This in turn means that the location sensitive nature of restrict needs to be made explicit in the IL, i.e. we basically need some starting point when a pointer becomes restrict (ADD_RESTRICT), and also an ending point (DEL_RESTRICT), which must act as barrier for other memory accesses (if it wouldn't some transformations could move references from outside restrict regions into the restrict region making them disasmbiguable with the restrict references, introducing a bug). We also need to use our points-to solver to propagate restrict tags conservatively, postprocessing the information to remove too conservative estimates afterwards per SSA name. And if we want to use restrict based disambiguation we also need to transfer the barriers into RTL barriers (I'm using an asm with an explicit MEM to refer to the pointer in question, so not all memory is clobbered). There's some potential for improvement here by removing useless ADD_RESTRICT / DEL_RESTRICT pairs. There's another improvement when enlargening the set of SSA names to be considered for postprocessin. Right now only the result of ADD_RESTRICT assigns are handled, that can be improved to also process SSA names that trivial depend on such (i.e. are offsetted and themself restrict typed). That facility is used to implement restrict parameters to functions, replacing the current ad-hoc way in the points-to solver. Other uses should be done under control of the frontends, as only those know the semantics for real. I have a patch that more aggressively creates ADD_RESTRICT/DEL_RESTRICT pairs (basically whenever there's an assignment from non-restrict pointers to a restrict pointer, on the grounds that this "invents" a new restrict set), but that breaks C programs that we don't want to break (I consider them invalid, but there's disagreement). Some older incarnations of this were bootstrapped, but this specific patch is only now in regstrapping on x86_64-linux. Okay for trunk if that passes? Ciao, Michael. --------------------------- * tree.def (ADD_RESTRICT): New tree code. * cfgexpand.c (expand_debug_expr): Handle it. * expr.c (expand_pointer_clobber): New function. (expand_expr_real_2): Use it to handle ADD_RESTRICT. * expr.h (expand_pointer_clobber): Declare. * function.c (gimplify_parameters): Return a second gimple_seq, handle restrict parameters. * function.h (gimplify_parameters): Adjust. * gimple-pretty-print.c (dump_binary_rhs): Handle ADD_RESTRICT. * gimplify.c (gimplify_body): Append second gimple_seq, adjust call to gimplify_parameters. * internal-fn.def (DEL_RESTRICT): New internal function code. * internal-fn.c (expand_DEL_RESTRICT): New function. * tree-cfg.c (verify_gimple_assign_binary): Check ADD_RESTRICT. * tree-inline.c (estimate_operator_cost): Handle ADD_RESTRICT. * tree-pretty-print.c (dump_generic_node): Ditto. * tree-ssa-dce.c (propagate_necessity): DEL_RESTRICT calls are only clobbers. * tree-ssa-structalias.c (build_fake_var_decl_uid): New static function. (build_fake_var_decl): Rewrite in terms of above. (make_heapvar): Take uid parameter. (make_constraint_from_restrict_uid): New. (make_constraint_from_restrict): Use above. (make_constraint_from_global_restrict): Explicitely set global flag. (handle_lhs_call): Adjust call to make_heapvar. (find_func_aliases_for_internal_call): New. (find_func_aliases_for_call): Use it. (find_func_aliases): Handle ADD_RESTRICT. (intra_create_variable_infos): Remove any explicit handling of restrict parameters. (set_uids_in_ptset): Update instead of overwrite vars_contains_escaped_heap flag. (find_what_var_points_to_1): Renamed from ... (find_what_var_points_to): ... this, which is now wrapper postprocessing points-to flags. (compute_points_to_sets): Ignore DEL_RESTRICT calls. Index: cfgexpand.c =================================================================== --- cfgexpand.c (revision 205123) +++ cfgexpand.c (working copy) @@ -3785,6 +3785,7 @@ expand_debug_expr (tree exp) /* Fall through. */ adjust_mode: + case ADD_RESTRICT: case PAREN_EXPR: case NOP_EXPR: case CONVERT_EXPR: Index: expr.c =================================================================== --- expr.c (revision 205123) +++ expr.c (working copy) @@ -7988,6 +7988,41 @@ expand_cond_expr_using_cmove (tree treeo return NULL_RTX; } +/* Given a memory pointer PTR, expand an RTL asm statement + that merely clobbers memory reachable from that pointer + via arbitrary offsets. */ + +void +expand_pointer_clobber (tree ptr, location_t loc) +{ + tree ref = build_simple_mem_ref (ptr); + rtx op = expand_expr (ref, NULL_RTX, BLKmode, EXPAND_MEMORY); + rtx body; + + op = validize_mem (op); + + /* There's no way to construct a tree expression doing exactly what + we need (representing a reference to arbitrarily memory reachable + from a given pointer, i.e. with arbitrary offsets. Hence + construct that by massaging the MEM attributes. */ + clear_mem_offset (op); + clear_mem_size (op); + PUT_MODE (op, BLKmode); + + rtvec argvec = rtvec_alloc (0); + rtvec constraintvec = rtvec_alloc (0); + rtvec labelvec = rtvec_alloc (0); + + body = gen_rtx_ASM_OPERANDS (BLKmode, + ggc_strdup (""), + ggc_strdup ("=m"), 0, argvec, constraintvec, + labelvec, loc); + + MEM_VOLATILE_P (body) = 1; + + emit_insn (gen_rtx_SET (VOIDmode, op, body)); +} + rtx expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode, enum expand_modifier modifier) @@ -8047,6 +8082,13 @@ expand_expr_real_2 (sepops ops, rtx targ switch (code) { + case ADD_RESTRICT: + /* We can handle add_restrict like a conversion, treeop1 will be + ignored. But do create a barrier to contain the restrict + section. */ + expand_pointer_clobber (treeop0, loc); + + /* FALLTHRU. */ case NON_LVALUE_EXPR: case PAREN_EXPR: CASE_CONVERT: Index: expr.h =================================================================== --- expr.h (revision 205123) +++ expr.h (working copy) @@ -719,6 +719,8 @@ extern rtx extract_low_bits (enum machin extern rtx expand_mult (enum machine_mode, rtx, rtx, rtx, int); extern rtx expand_mult_highpart_adjust (enum machine_mode, rtx, rtx, rtx, rtx, int); +extern void expand_pointer_clobber (tree, location_t); + extern rtx assemble_static_space (unsigned HOST_WIDE_INT); extern int safe_from_p (const_rtx, tree, int); extern bool split_comparison (enum rtx_code, enum machine_mode, Index: function.c =================================================================== --- function.c (revision 205123) +++ function.c (working copy) @@ -3595,10 +3595,11 @@ gimplify_parm_type (tree *tp, int *walk_ /* Gimplify the parameter list for current_function_decl. This involves evaluating SAVE_EXPRs of variable sized parameters and generating code to implement callee-copies reference parameters. Returns a sequence of - statements to add to the beginning of the function. */ + statements to add to the beginning of the function and another + sequence to add to the end of the function (via *STMT_AFTER). */ gimple_seq -gimplify_parameters (void) +gimplify_parameters (gimple_seq *stmts_after) { struct assign_parm_data_all all; tree parm; @@ -3606,6 +3607,7 @@ gimplify_parameters (void) vec fnargs; unsigned i; + *stmts_after = NULL; assign_parms_initialize_all (&all); fnargs = assign_parms_augmented_arg_list (&all); @@ -3690,6 +3692,17 @@ gimplify_parameters (void) DECL_HAS_VALUE_EXPR_P (parm) = 1; } } + if (POINTER_TYPE_P (TREE_TYPE (parm)) + && TYPE_RESTRICT (TREE_TYPE (parm)) + && !DECL_HAS_VALUE_EXPR_P (parm)) + { + tree t = fold_build2 (ADD_RESTRICT, TREE_TYPE (parm), parm, + build_int_cst (integer_type_node, + allocate_decl_uid ())); + gimplify_assign (parm, t, &stmts); + gimple g = gimple_build_call_internal (IFN_DEL_RESTRICT, 1, parm); + gimple_seq_add_stmt (stmts_after, g); + } } fnargs.release (); Index: function.h =================================================================== --- function.h (revision 205123) +++ function.h (working copy) @@ -841,6 +841,6 @@ extern void preserve_temp_slots (rtx); extern int aggregate_value_p (const_tree, const_tree); extern void push_function_context (void); extern void pop_function_context (void); -extern gimple_seq gimplify_parameters (void); +extern gimple_seq gimplify_parameters (gimple_seq *); #endif /* GCC_FUNCTION_H */ Index: gimple-pretty-print.c =================================================================== --- gimple-pretty-print.c (revision 205123) +++ gimple-pretty-print.c (working copy) @@ -348,6 +348,7 @@ dump_binary_rhs (pretty_printer *buffer, case COMPLEX_EXPR: case MIN_EXPR: case MAX_EXPR: + case ADD_RESTRICT: case VEC_WIDEN_MULT_HI_EXPR: case VEC_WIDEN_MULT_LO_EXPR: case VEC_WIDEN_MULT_EVEN_EXPR: Index: gimplify.c =================================================================== --- gimplify.c (revision 205123) +++ gimplify.c (working copy) @@ -1005,8 +1005,9 @@ gimplify_bind_expr (tree *expr_p, gimple && !DECL_HARD_REGISTER (t) && !TREE_THIS_VOLATILE (t) && !DECL_HAS_VALUE_EXPR_P (t) - /* Only care for variables that have to be in memory. Others - will be rewritten into SSA names, hence moved to the top-level. */ + /* Only care for variables that have to be in memory. + Others will be rewritten into SSA names, hence moved + to the top-level. */ && !is_gimple_reg (t) && flag_stack_reuse != SR_NONE) { @@ -8356,7 +8357,7 @@ gimple gimplify_body (tree fndecl, bool do_parms) { location_t saved_location = input_location; - gimple_seq parm_stmts, seq; + gimple_seq parm_stmts, parm_stmts_after, seq; gimple outer_bind; struct gimplify_ctx gctx; struct cgraph_node *cgn; @@ -8393,7 +8394,8 @@ gimplify_body (tree fndecl, bool do_parm /* Resolve callee-copies. This has to be done before processing the body so that DECL_VALUE_EXPR gets processed correctly. */ - parm_stmts = do_parms ? gimplify_parameters () : NULL; + parm_stmts_after = NULL; + parm_stmts = do_parms ? gimplify_parameters (&parm_stmts_after) : NULL; /* Gimplify the function's body. */ seq = NULL; @@ -8432,6 +8434,10 @@ gimplify_body (tree fndecl, bool do_parm DECL_IGNORED_P (parm) = 0; } } + /* Same for statements that need to come after the body. */ + if (!gimple_seq_empty_p (parm_stmts_after)) + gimplify_seq_add_seq (gimple_bind_body_ptr (outer_bind), + parm_stmts_after); if (nonlocal_vlas) { Index: internal-fn.c =================================================================== --- internal-fn.c (revision 205123) +++ internal-fn.c (working copy) @@ -148,6 +148,16 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE gcc_unreachable (); } +/* Expand the DEL_RESTRICT internal function into a RTL + asm that merely clobbers memory reachable via the pointer. */ + +static void +expand_DEL_RESTRICT (gimple stmt) +{ + tree ptr = gimple_call_arg (stmt, 0); + expand_pointer_clobber (ptr, gimple_location (stmt)); +} + /* Routines to expand each internal function, indexed by function number. Each routine has the prototype: Index: internal-fn.def =================================================================== --- internal-fn.def (revision 205123) +++ internal-fn.def (working copy) @@ -45,3 +45,4 @@ DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (DEL_RESTRICT, /*ECF_LEAF |*/ ECF_NOTHROW) Index: tree-cfg.c =================================================================== --- tree-cfg.c (revision 205123) +++ tree-cfg.c (working copy) @@ -3627,6 +3627,21 @@ verify_gimple_assign_binary (gimple stmt return false; } + case ADD_RESTRICT: + { + if (!POINTER_TYPE_P (rhs1_type) + || !useless_type_conversion_p (lhs_type, rhs1_type) + || !useless_type_conversion_p (integer_type_node, rhs2_type)) + { + error ("Invalid use of add_restrict"); + debug_generic_stmt (lhs_type); + debug_generic_stmt (rhs1_type); + debug_generic_stmt (rhs2_type); + return true; + } + return false; + } + case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: case TRUTH_AND_EXPR: Index: tree-inline.c =================================================================== --- tree-inline.c (revision 205123) +++ tree-inline.c (working copy) @@ -3580,6 +3580,7 @@ estimate_operator_cost (enum tree_code c case COMPLEX_EXPR: case PAREN_EXPR: case VIEW_CONVERT_EXPR: + case ADD_RESTRICT: return 0; /* Assign cost of 1 to usual operations. Index: tree-pretty-print.c =================================================================== --- tree-pretty-print.c (revision 205123) +++ tree-pretty-print.c (working copy) @@ -1927,6 +1927,14 @@ dump_generic_node (pretty_printer *buffe pp_greater (buffer); break; + case ADD_RESTRICT: + pp_string (buffer, "ADD_RESTRICT <"); + dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); + pp_string (buffer, ", "); + dump_generic_node (buffer, TREE_OPERAND (node, 1), spc, flags, false); + pp_character (buffer, '>'); + break; + case ABS_EXPR: pp_string (buffer, "ABS_EXPR <"); dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false); Index: tree-ssa-dce.c =================================================================== --- tree-ssa-dce.c (revision 205123) +++ tree-ssa-dce.c (working copy) @@ -828,6 +828,10 @@ propagate_necessity (bool aggressive) || DECL_FUNCTION_CODE (callee) == BUILT_IN_ASSUME_ALIGNED)) continue; + if (gimple_call_internal_p (stmt) + && gimple_call_internal_fn (stmt) == IFN_DEL_RESTRICT) + continue; + /* Calls implicitly load from memory, their arguments in addition may explicitly perform memory loads. */ mark_all_reaching_defs_necessary (stmt); Index: tree-ssa-structalias.c =================================================================== --- tree-ssa-structalias.c (revision 205123) +++ tree-ssa-structalias.c (working copy) @@ -3741,31 +3741,43 @@ make_transitive_closure_constraints (var /* Temporary storage for fake var decls. */ struct obstack fake_var_decl_obstack; -/* Build a fake VAR_DECL acting as referrer to a DECL_UID. */ +/* Build a fake VAR_DECL with DECL_UID being UID (which probably has + to be ultimately allocated by allocate_decl_uid()). */ static tree -build_fake_var_decl (tree type) +build_fake_var_decl_uid (tree type, int uid) { tree decl = (tree) XOBNEW (&fake_var_decl_obstack, struct tree_var_decl); memset (decl, 0, sizeof (struct tree_var_decl)); TREE_SET_CODE (decl, VAR_DECL); TREE_TYPE (decl) = type; - DECL_UID (decl) = allocate_decl_uid (); + DECL_UID (decl) = uid; SET_DECL_PT_UID (decl, -1); layout_decl (decl, 0); return decl; } -/* Create a new artificial heap variable with NAME. - Return the created variable. */ +/* Build a fake VAR_DECL acting as referrer to a DECL_UID. */ + +static tree +build_fake_var_decl (tree type) +{ + return build_fake_var_decl_uid (type, allocate_decl_uid ()); +} + +/* Create a new artificial heap variable with NAME and UID. + If UID is -1 allocate a new one. Return the created variable. */ static varinfo_t -make_heapvar (const char *name) +make_heapvar (const char *name, int uid) { varinfo_t vi; tree heapvar; - heapvar = build_fake_var_decl (ptr_type_node); + if (uid == -1) + heapvar = build_fake_var_decl (ptr_type_node); + else + heapvar = build_fake_var_decl_uid (ptr_type_node, uid); DECL_EXTERNAL (heapvar) = 1; vi = new_var_info (heapvar, name); @@ -3781,22 +3793,34 @@ make_heapvar (const char *name) return vi; } -/* Create a new artificial heap variable with NAME and make a +/* Create a new artificial heap variable with NAME and UID and make a constraint from it to LHS. Set flags according to a tag used for tracking restrict pointers. */ static varinfo_t -make_constraint_from_restrict (varinfo_t lhs, const char *name) +make_constraint_from_restrict_uid (varinfo_t lhs, const char *name, int uid) { - varinfo_t vi = make_heapvar (name); - vi->is_global_var = 1; - vi->may_have_pointers = 1; + varinfo_t vi; + vi = make_heapvar (name, uid); make_constraint_from (lhs, vi->id); + vi->is_global_var = 0; + /*vi->is_special_var = 1;*/ + vi->may_have_pointers = 1; return vi; } /* Create a new artificial heap variable with NAME and make a constraint from it to LHS. Set flags according to a tag used + for tracking restrict pointers. */ + +static varinfo_t +make_constraint_from_restrict (varinfo_t lhs, const char *name) +{ + return make_constraint_from_restrict_uid (lhs, name, -1); +} + +/* Create a new artificial heap variable with NAME and make a + constraint from it to LHS. Set flags according to a tag used for tracking restrict pointers and make the artificial heap point to global memory. */ @@ -3804,6 +3828,7 @@ static varinfo_t make_constraint_from_global_restrict (varinfo_t lhs, const char *name) { varinfo_t vi = make_constraint_from_restrict (lhs, name); + vi->is_global_var = 1; make_copy_constraint (vi, nonlocal_id); return vi; } @@ -3999,7 +4024,7 @@ handle_lhs_call (gimple stmt, tree lhs, varinfo_t vi; struct constraint_expr tmpc; rhsc.create (0); - vi = make_heapvar ("HEAP"); + vi = make_heapvar ("HEAP", -1); /* We marking allocated storage local, we deal with it becoming global by escaping and setting of vars_contains_escaped_heap. */ DECL_EXTERNAL (vi->decl) = 0; @@ -4490,6 +4515,28 @@ find_func_aliases_for_builtin_call (gimp return false; } +/* Create constraints for the internal call T. Return true if the call + was handled, otherwise false. */ + +static bool +find_func_aliases_for_internal_call (gimple t) +{ + switch (gimple_call_internal_fn (t)) + { + case IFN_DEL_RESTRICT: + { + /* DEL_RESTRICT is a barrier for what its argument points to. */ + tree ptr = gimple_call_arg (t, 0); + make_constraint_to (get_call_clobber_vi (t)->id, ptr); + return true; + } + + default: + /* Fallthru to general call handling. */; + } + return false; +} + /* Create constraints for the call T. */ static void @@ -4505,6 +4552,10 @@ find_func_aliases_for_call (gimple t) && find_func_aliases_for_builtin_call (t)) return; + if (gimple_call_internal_p (t) + && find_func_aliases_for_internal_call (t)) + return; + fi = get_fi_for_callee (t); if (!in_ipa_mode || (fndecl && !fi->is_fn_info)) @@ -4715,6 +4766,16 @@ find_func_aliases (gimple origt) /* Truth value results are not pointer (parts). Or at least very very unreasonable obfuscation of a part. */ ; + else if (gimple_assign_rhs_code (t) == ADD_RESTRICT) + { + /* Add the specified restrict tag and merge from + the incoming pointer. */ + make_constraint_from_restrict_uid (get_vi_for_tree (lhsop), + "RESTRICT_TAG", + TREE_INT_CST_LOW + (gimple_assign_rhs2 (t))); + get_constraint_for_rhs (gimple_assign_rhs1 (t), &rhsc); + } else { /* All other operations are merges. */ @@ -5852,50 +5913,10 @@ intra_create_variable_infos (void) { varinfo_t p = get_vi_for_tree (t); - /* For restrict qualified pointers to objects passed by - reference build a real representative for the pointed-to object. - Treat restrict qualified references the same. */ - if (TYPE_RESTRICT (TREE_TYPE (t)) - && ((DECL_BY_REFERENCE (t) && POINTER_TYPE_P (TREE_TYPE (t))) - || TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE) - && !type_contains_placeholder_p (TREE_TYPE (TREE_TYPE (t)))) - { - struct constraint_expr lhsc, rhsc; - varinfo_t vi; - tree heapvar = build_fake_var_decl (TREE_TYPE (TREE_TYPE (t))); - DECL_EXTERNAL (heapvar) = 1; - vi = create_variable_info_for_1 (heapvar, "PARM_NOALIAS"); - insert_vi_for_tree (heapvar, vi); - lhsc.var = p->id; - lhsc.type = SCALAR; - lhsc.offset = 0; - rhsc.var = vi->id; - rhsc.type = ADDRESSOF; - rhsc.offset = 0; - process_constraint (new_constraint (lhsc, rhsc)); - for (; vi; vi = vi_next (vi)) - if (vi->may_have_pointers) - { - if (vi->only_restrict_pointers) - make_constraint_from_global_restrict (vi, "GLOBAL_RESTRICT"); - else - make_copy_constraint (vi, nonlocal_id); - } - continue; - } - - if (POINTER_TYPE_P (TREE_TYPE (t)) - && TYPE_RESTRICT (TREE_TYPE (t))) - make_constraint_from_global_restrict (p, "PARM_RESTRICT"); - else + for (; p; p = vi_next (p)) { - for (; p; p = vi_next (p)) - { - if (p->only_restrict_pointers) - make_constraint_from_global_restrict (p, "PARM_RESTRICT"); - else if (p->may_have_pointers) - make_constraint_from (p, nonlocal_id); - } + if (p->may_have_pointers) + make_constraint_from (p, nonlocal_id); } } @@ -6022,7 +6043,7 @@ set_uids_in_ptset (bitmap into, bitmap f && bitmap_bit_p (escaped_vi->solution, i))) { pt->vars_contains_escaped = true; - pt->vars_contains_escaped_heap = vi->is_heap_var; + pt->vars_contains_escaped_heap |= vi->is_heap_var; } if (TREE_CODE (vi->decl) == VAR_DECL @@ -6045,10 +6066,10 @@ set_uids_in_ptset (bitmap into, bitmap f } -/* Compute the points-to solution *PT for the variable VI. */ +/* Compute the points-to solution *PT for the variable VI, part one. */ static struct pt_solution -find_what_var_points_to (varinfo_t orig_vi) +find_what_var_points_to_1 (varinfo_t orig_vi) { unsigned int i; bitmap_iterator bi; @@ -6096,7 +6117,7 @@ find_what_var_points_to (varinfo_t orig_ /* Nobody cares. */ ; else if (vi->id == anything_id - || vi->id == integer_id) + || vi->id == integer_id) pt->anything = 1; } } @@ -6126,6 +6147,71 @@ find_what_var_points_to (varinfo_t orig_ return *pt; } +/* Compute the points-to solution *PT for the variable VI, part two. + This modifies the points-to solution to cater for restrict pointers. + + Restrict is modelled as a may-be restrict problem together with + a must-be restrict problem. From the must-be restrict solution we + can later deduce non-aliasing (if the points-to sets don't intersect + the two references can't alias). The may-be restrict problem provides + a sort of conservative cap for that such that a pointer p1 that is even + remotely related to a restrict pointer r2 points to a superset of what + p1 points to. In particular we want to handle this situation: + + 1 int * restrict p1 = ...; + 2 temp = p1; + 3 int * restrict p2; + 4 p2 = temp; + + Suppose that 'temp' is address taken, so (2) is a store and + (4) a load. We want to make sure that p1 and p2 are tagged + the same (remember we're not handling just the C language + definition of restrict). To ensure this we need to look through + memory (and even handle second level effects, like when a restrict + pointer is stored into a global variable). + + That's why we use the points-to solver for the may-be restrict problem + (which handles memory conservatively correct). The must-be restrict + part then is formulated as a post-processing on the conservatively + correct points-to solution. For a set of variables their solution + is pruned. This pruned solution is returned here. */ + +static struct pt_solution +find_what_var_points_to (varinfo_t vi) +{ + struct pt_solution pt; + + pt = find_what_var_points_to_1 (vi); + + if (vi->decl && TREE_CODE (vi->decl) == SSA_NAME) + { + gimple g = SSA_NAME_DEF_STMT (vi->decl); + if (g && is_gimple_assign (g) + && gimple_assign_rhs_code (g) == ADD_RESTRICT) + { + /* Restrict pointers only point to their tags. For now + we leave also the other decls in the points-to set: + changing the set would imply possibly unsharing the bitmap, + and furthermore it's actually good to have precise + decls in there in some cases, e.g. if it's local variables. + + In effect this means removing the nonlocal flags. To + not regard stores via such pointers as inherently useless + we need to set the vars_contains_escaped_heap flag, if the + input contained some nonlocals. */ + if (pt.vars_contains_nonlocal + || pt.nonlocal + || pt.vars_contains_escaped) + pt.vars_contains_escaped_heap = 1; + pt.nonlocal = 0; + pt.vars_contains_escaped = 0; + pt.vars_contains_nonlocal = 0; + } + } + + return pt; +} + /* Given a pointer variable P, fill in its points-to set. */ static void @@ -6866,10 +6952,14 @@ compute_points_to_sets (void) *pt = find_what_var_points_to (vi); /* Escaped (and thus nonlocal) variables are always implicitly used by calls. */ - /* ??? ESCAPED can be empty even though NONLOCAL - always escaped. */ - pt->nonlocal = 1; - pt->escaped = 1; + if (!gimple_call_internal_p (stmt) + || gimple_call_internal_fn (stmt) != IFN_DEL_RESTRICT) + { + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + pt->nonlocal = 1; + pt->escaped = 1; + } } else { @@ -6887,10 +6977,14 @@ compute_points_to_sets (void) *pt = find_what_var_points_to (vi); /* Escaped (and thus nonlocal) variables are always implicitly clobbered by calls. */ - /* ??? ESCAPED can be empty even though NONLOCAL - always escaped. */ - pt->nonlocal = 1; - pt->escaped = 1; + if (!gimple_call_internal_p (stmt) + || gimple_call_internal_fn (stmt) != IFN_DEL_RESTRICT) + { + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + pt->nonlocal = 1; + pt->escaped = 1; + } } else { Index: tree.def =================================================================== --- tree.def (revision 205123) +++ tree.def (working copy) @@ -977,6 +977,8 @@ DEFTREECODE (WITH_SIZE_EXPR, "with_size_ generated by the builtin targetm.vectorize.mask_for_load_builtin_decl. */ DEFTREECODE (REALIGN_LOAD_EXPR, "realign_load", tcc_expression, 3) +DEFTREECODE (ADD_RESTRICT, "add_restrict", tcc_binary, 2) + /* Low-level memory addressing. Operands are BASE (address of static or global variable or register), OFFSET (integer constant), INDEX (register), STEP (integer constant), INDEX2 (register),