On Thu, Nov 21, 2013 at 10:03 AM, Michael Matz <m...@suse.de> wrote: > 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). >
Can you use block/scope information to address this ? References from enclosing scopes can be considered possible aliases. David > 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<tree> 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),