middle-end, v4: IFN_ASSUME support [PR106654]
Checks
Commit Message
Hi!
On Mon, Oct 17, 2022 at 06:55:40AM +0000, Richard Biener wrote:
> > That is what I wrote in the patch description as alternative:
> > "with the condition wrapped into a GIMPLE_BIND (I admit the above isn't
> > extra clean but it is just something to hold it from gimplifier until
> > gimple low pass; it reassembles if (condition_never_true) { cond; };
> > an alternative would be introduce GOMP_ASSUME statement that would have
> > the guard var as operand and the GIMPLE_BIND as body, but for the
> > few passes (tree-nested and omp lowering) in between that looked like
> > an overkill to me)"
> > I can certainly implement that easily.
>
> I'd prefer that, it looks possibly less messy.
Ok, introduced GIMPLE_ASSUME for this then.
> Ah, they are all in all_passes :/ Maybe we can add something
> like TODO_discard_function (or a property) that will not discard
> the function but stop compiling it? I wonder if all cleanup
> is properly done for the function - I suppose we want to keep the
> body around for callers indefinitely.
>
> > What I had in the patch was just skip pass_expand
> > and pass_rest_of_compilation, perhaps another possibility
> > to do the former would be to define a gate on pass_expand.
>
> Or some gate in the pass manager, like in override_gate_status
> check fun->properties & PROP_suspended and have some
> pass_suspend_assume add that property for all assume function
> bodies.
>
> In case you like any of the above give it a shot, otherwise what
> you have isn't too bad, I just wondered if there's a nicer way.
Turns out we already had TODO_discard_function, so I've reused it
with the detail that assume function's bodies aren't actually dropped,
and a new pass which returns that and where I'd like to have the
backwards range walk implemented.
> I suppose for now adding noipa is easiest, we'd still inline into
> the body of course.
Ok, done.
On Fri, Oct 14, 2022 at 11:27:07AM +0000, Richard Biener wrote:
> > @@ -237,6 +244,383 @@ lower_omp_directive (gimple_stmt_iterato
> > gsi_next (gsi);
> > }
>
> comment missing
Added.
> > +static tree
> > +create_assumption_fn (location_t loc)
> > +{
> > + tree name = clone_function_name_numbered (current_function_decl, "_assume");
> > + /* For now, will be changed later. */
>
> ?
I've tried error_mark_node as the type, but that didn't work out, get
various ICEs with it even for the short time before it is fixed up.
But changed that to
tree type = build_varargs_function_type_list (boolean_type_node, NULL_TREE);
which is closer to what it ends up later.
> > + DECL_FUNCTION_VERSIONED (decl)
> > + = DECL_FUNCTION_VERSIONED (current_function_decl);
>
> what does it mean to copy DECL_FUNCTION_VERSIONED here?
Dropped.
> > + && !is_gimple_val (vargs[i - sz]))
>
> a few comments might be helpful here
Added some to various places in that function.
> > @@ -1490,7 +1494,14 @@ public:
> >
> > /* opt_pass methods: */
> > opt_pass * clone () final override { return new pass_slp_vectorize (m_ctxt); }
> > - bool gate (function *) final override { return flag_tree_slp_vectorize != 0; }
> > + bool gate (function *fun) final override
> > + {
> > + /* Vectorization makes range analysis of assume functions harder,
> > + not easier. */
>
> Can we split out these kind of considerations from the initial patch?
Dropped for now.
> Reading some of the patch I guessed you wanted to handle nested
> assumes. So - is
>
> [[assume (a == 4 && ([[assume(b == 3)]], b != 2))]]
>
> a thing?
Added 2 tests for nested assumptions (one with a simple assumption
nested in complex one, one with side-effects one nested in complex one).
So far lightly tested, will test fully overnight.
2022-10-17 Jakub Jelinek <jakub@redhat.com>
PR c++/106654
gcc/
* gimple.def (GIMPLE_ASSUME): New statement kind.
* gimple.h (struct gimple_statement_assume): New type.
(is_a_helper <gimple_statement_assume *>::test,
is_a_helper <const gimple_statement_assume *>::test): New.
(gimple_build_assume): Declare.
(gimple_has_substatements): Return true for GIMPLE_ASSUME.
(gimple_assume_guard, gimple_assume_set_guard,
gimple_assume_guard_ptr, gimple_assume_body_ptr, gimple_assume_body):
New inline functions.
* gsstruct.def (GSS_ASSUME): New.
* gimple.cc (gimple_build_assume): New function.
(gimple_copy): Handle GIMPLE_ASSUME.
* gimple-pretty-print.cc (dump_gimple_assume): New function.
(pp_gimple_stmt_1): Handle GIMPLE_ASSUME.
* gimple-walk.cc (walk_gimple_op): Handle GIMPLE_ASSUME.
* omp-low.cc (WALK_SUBSTMTS): Likewise.
(lower_omp_1): Likewise.
* omp-oacc-kernels-decompose.cc (adjust_region_code_walk_stmt_fn):
Likewise.
* tree-cfg.cc (verify_gimple_stmt, verify_gimple_in_seq_2): Likewise.
* function.h (struct function): Add assume_function bitfield.
* gimplify.cc (gimplify_call_expr): If the assumption isn't
simple enough, expand it into GIMPLE_ASSUME wrapped block or
for -O0 drop it.
* gimple-low.cc: Include attribs.h.
(create_assumption_fn): New function.
(struct lower_assumption_data): New type.
(find_assumption_locals_r, assumption_copy_decl,
adjust_assumption_stmt_r, adjust_assumption_stmt_op,
lower_assumption): New functions.
(lower_stmt): Handle GIMPLE_ASSUME.
* tree-ssa-ccp.cc (pass_fold_builtins::execute): Remove
IFN_ASSUME calls.
* lto-streamer-out.cc (output_struct_function_base): Pack
assume_function bit.
* lto-streamer-in.cc (input_struct_function_base): And unpack it.
* cgraphunit.cc (cgraph_node::expand): Don't verify assume_function
has TREE_ASM_WRITTEN set and don't release its body.
(symbol_table::compile): Allow assume functions not to have released
body.
* internal-fn.cc (expand_ASSUME): Remove gcc_unreachable.
* passes.cc (execute_one_pass): For TODO_discard_function don't
release body of assume functions.
* cgraph.cc (cgraph_node::verify_node): Don't verify cgraph nodes
of PROP_assumptions_done functions.
* tree-pass.h (PROP_assumptions_done): Define.
(TODO_discard_function): Adjust comment.
(make_pass_assumptions): Declare.
* passes.def (pass_assumptions): Add.
* tree-vrp.cc (pass_data_assumptions): New variable.
(pass_assumptions): New class.
(make_pass_assumptions): New function.
gcc/cp/
* cp-tree.h (build_assume_call): Declare.
* parser.cc (cp_parser_omp_assumption_clauses): Use build_assume_call.
* cp-gimplify.cc (build_assume_call): New function.
(process_stmt_assume_attribute): Use build_assume_call.
* pt.cc (tsubst_copy_and_build): Likewise.
gcc/testsuite/
* g++.dg/cpp23/attr-assume5.C: New test.
* g++.dg/cpp23/attr-assume6.C: New test.
* g++.dg/cpp23/attr-assume7.C: New test.
Jakub
Comments
On Mon, 17 Oct 2022, Jakub Jelinek wrote:
> Hi!
>
> On Mon, Oct 17, 2022 at 06:55:40AM +0000, Richard Biener wrote:
> > > That is what I wrote in the patch description as alternative:
> > > "with the condition wrapped into a GIMPLE_BIND (I admit the above isn't
> > > extra clean but it is just something to hold it from gimplifier until
> > > gimple low pass; it reassembles if (condition_never_true) { cond; };
> > > an alternative would be introduce GOMP_ASSUME statement that would have
> > > the guard var as operand and the GIMPLE_BIND as body, but for the
> > > few passes (tree-nested and omp lowering) in between that looked like
> > > an overkill to me)"
> > > I can certainly implement that easily.
> >
> > I'd prefer that, it looks possibly less messy.
>
> Ok, introduced GIMPLE_ASSUME for this then.
>
> > Ah, they are all in all_passes :/ Maybe we can add something
> > like TODO_discard_function (or a property) that will not discard
> > the function but stop compiling it? I wonder if all cleanup
> > is properly done for the function - I suppose we want to keep the
> > body around for callers indefinitely.
> >
> > > What I had in the patch was just skip pass_expand
> > > and pass_rest_of_compilation, perhaps another possibility
> > > to do the former would be to define a gate on pass_expand.
> >
> > Or some gate in the pass manager, like in override_gate_status
> > check fun->properties & PROP_suspended and have some
> > pass_suspend_assume add that property for all assume function
> > bodies.
> >
> > In case you like any of the above give it a shot, otherwise what
> > you have isn't too bad, I just wondered if there's a nicer way.
>
> Turns out we already had TODO_discard_function, so I've reused it
> with the detail that assume function's bodies aren't actually dropped,
> and a new pass which returns that and where I'd like to have the
> backwards range walk implemented.
>
> > I suppose for now adding noipa is easiest, we'd still inline into
> > the body of course.
>
> Ok, done.
>
> On Fri, Oct 14, 2022 at 11:27:07AM +0000, Richard Biener wrote:
> > > @@ -237,6 +244,383 @@ lower_omp_directive (gimple_stmt_iterato
> > > gsi_next (gsi);
> > > }
> >
> > comment missing
>
> Added.
>
> > > +static tree
> > > +create_assumption_fn (location_t loc)
> > > +{
> > > + tree name = clone_function_name_numbered (current_function_decl, "_assume");
> > > + /* For now, will be changed later. */
> >
> > ?
>
> I've tried error_mark_node as the type, but that didn't work out, get
> various ICEs with it even for the short time before it is fixed up.
> But changed that to
> tree type = build_varargs_function_type_list (boolean_type_node, NULL_TREE);
> which is closer to what it ends up later.
>
> > > + DECL_FUNCTION_VERSIONED (decl)
> > > + = DECL_FUNCTION_VERSIONED (current_function_decl);
> >
> > what does it mean to copy DECL_FUNCTION_VERSIONED here?
>
> Dropped.
>
> > > + && !is_gimple_val (vargs[i - sz]))
> >
> > a few comments might be helpful here
>
> Added some to various places in that function.
> > > @@ -1490,7 +1494,14 @@ public:
> > >
> > > /* opt_pass methods: */
> > > opt_pass * clone () final override { return new pass_slp_vectorize (m_ctxt); }
> > > - bool gate (function *) final override { return flag_tree_slp_vectorize != 0; }
> > > + bool gate (function *fun) final override
> > > + {
> > > + /* Vectorization makes range analysis of assume functions harder,
> > > + not easier. */
> >
> > Can we split out these kind of considerations from the initial patch?
>
> Dropped for now.
>
> > Reading some of the patch I guessed you wanted to handle nested
> > assumes. So - is
> >
> > [[assume (a == 4 && ([[assume(b == 3)]], b != 2))]]
> >
> > a thing?
>
> Added 2 tests for nested assumptions (one with a simple assumption
> nested in complex one, one with side-effects one nested in complex one).
>
> So far lightly tested, will test fully overnight.
Looks good to me now.
Thanks,
Richard.
> 2022-10-17 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/106654
> gcc/
> * gimple.def (GIMPLE_ASSUME): New statement kind.
> * gimple.h (struct gimple_statement_assume): New type.
> (is_a_helper <gimple_statement_assume *>::test,
> is_a_helper <const gimple_statement_assume *>::test): New.
> (gimple_build_assume): Declare.
> (gimple_has_substatements): Return true for GIMPLE_ASSUME.
> (gimple_assume_guard, gimple_assume_set_guard,
> gimple_assume_guard_ptr, gimple_assume_body_ptr, gimple_assume_body):
> New inline functions.
> * gsstruct.def (GSS_ASSUME): New.
> * gimple.cc (gimple_build_assume): New function.
> (gimple_copy): Handle GIMPLE_ASSUME.
> * gimple-pretty-print.cc (dump_gimple_assume): New function.
> (pp_gimple_stmt_1): Handle GIMPLE_ASSUME.
> * gimple-walk.cc (walk_gimple_op): Handle GIMPLE_ASSUME.
> * omp-low.cc (WALK_SUBSTMTS): Likewise.
> (lower_omp_1): Likewise.
> * omp-oacc-kernels-decompose.cc (adjust_region_code_walk_stmt_fn):
> Likewise.
> * tree-cfg.cc (verify_gimple_stmt, verify_gimple_in_seq_2): Likewise.
> * function.h (struct function): Add assume_function bitfield.
> * gimplify.cc (gimplify_call_expr): If the assumption isn't
> simple enough, expand it into GIMPLE_ASSUME wrapped block or
> for -O0 drop it.
> * gimple-low.cc: Include attribs.h.
> (create_assumption_fn): New function.
> (struct lower_assumption_data): New type.
> (find_assumption_locals_r, assumption_copy_decl,
> adjust_assumption_stmt_r, adjust_assumption_stmt_op,
> lower_assumption): New functions.
> (lower_stmt): Handle GIMPLE_ASSUME.
> * tree-ssa-ccp.cc (pass_fold_builtins::execute): Remove
> IFN_ASSUME calls.
> * lto-streamer-out.cc (output_struct_function_base): Pack
> assume_function bit.
> * lto-streamer-in.cc (input_struct_function_base): And unpack it.
> * cgraphunit.cc (cgraph_node::expand): Don't verify assume_function
> has TREE_ASM_WRITTEN set and don't release its body.
> (symbol_table::compile): Allow assume functions not to have released
> body.
> * internal-fn.cc (expand_ASSUME): Remove gcc_unreachable.
> * passes.cc (execute_one_pass): For TODO_discard_function don't
> release body of assume functions.
> * cgraph.cc (cgraph_node::verify_node): Don't verify cgraph nodes
> of PROP_assumptions_done functions.
> * tree-pass.h (PROP_assumptions_done): Define.
> (TODO_discard_function): Adjust comment.
> (make_pass_assumptions): Declare.
> * passes.def (pass_assumptions): Add.
> * tree-vrp.cc (pass_data_assumptions): New variable.
> (pass_assumptions): New class.
> (make_pass_assumptions): New function.
> gcc/cp/
> * cp-tree.h (build_assume_call): Declare.
> * parser.cc (cp_parser_omp_assumption_clauses): Use build_assume_call.
> * cp-gimplify.cc (build_assume_call): New function.
> (process_stmt_assume_attribute): Use build_assume_call.
> * pt.cc (tsubst_copy_and_build): Likewise.
> gcc/testsuite/
> * g++.dg/cpp23/attr-assume5.C: New test.
> * g++.dg/cpp23/attr-assume6.C: New test.
> * g++.dg/cpp23/attr-assume7.C: New test.
>
> --- gcc/gimple.def.jj 2022-05-30 14:07:01.989306541 +0200
> +++ gcc/gimple.def 2022-10-17 12:47:55.323049825 +0200
> @@ -406,3 +406,8 @@ DEFGSCODE(GIMPLE_PREDICT, "gimple_predic
>
> This tuple should not exist outside of the gimplifier proper. */
> DEFGSCODE(GIMPLE_WITH_CLEANUP_EXPR, "gimple_with_cleanup_expr", GSS_WCE)
> +
> +/* GIMPLE_ASSUME <GUARD, BODY> represents [[assume(cond)]].
> + BODY is the GIMPLE_BIND with the condition which sets GUARD to true
> + (otherwise UB). */
> +DEFGSCODE(GIMPLE_ASSUME, "gimple_assume", GSS_ASSUME)
> --- gcc/gimple.h.jj 2022-09-06 09:19:14.718561564 +0200
> +++ gcc/gimple.h 2022-10-17 14:11:16.142167827 +0200
> @@ -825,6 +825,20 @@ struct GTY((tag("GSS_OMP_ATOMIC_STORE_LA
> stmt->code == GIMPLE_OMP_RETURN. */
> };
>
> +/* Assumptions. */
> +
> +struct GTY((tag("GSS_ASSUME")))
> + gimple_statement_assume : public gimple
> +{
> + /* [ WORD 1-6 ] : base class */
> +
> + /* [ WORD 7 ] */
> + tree guard;
> +
> + /* [ WORD 8 ] */
> + gimple_seq body;
> +};
> +
> /* GIMPLE_TRANSACTION. */
>
> /* Bits to be stored in the GIMPLE_TRANSACTION subcode. */
> @@ -1271,6 +1285,14 @@ is_a_helper <const gswitch *>::test (con
> template <>
> template <>
> inline bool
> +is_a_helper <gimple_statement_assume *>::test (gimple *gs)
> +{
> + return gs->code == GIMPLE_ASSUME;
> +}
> +
> +template <>
> +template <>
> +inline bool
> is_a_helper <gtransaction *>::test (gimple *gs)
> {
> return gs->code == GIMPLE_TRANSACTION;
> @@ -1497,6 +1519,14 @@ is_a_helper <const greturn *>::test (con
> template <>
> template <>
> inline bool
> +is_a_helper <const gimple_statement_assume *>::test (const gimple *gs)
> +{
> + return gs->code == GIMPLE_ASSUME;
> +}
> +
> +template <>
> +template <>
> +inline bool
> is_a_helper <const gtransaction *>::test (const gimple *gs)
> {
> return gs->code == GIMPLE_TRANSACTION;
> @@ -1577,6 +1607,7 @@ gomp_teams *gimple_build_omp_teams (gimp
> gomp_atomic_load *gimple_build_omp_atomic_load (tree, tree,
> enum omp_memory_order);
> gomp_atomic_store *gimple_build_omp_atomic_store (tree, enum omp_memory_order);
> +gimple *gimple_build_assume (tree, gimple_seq);
> gtransaction *gimple_build_transaction (gimple_seq);
> extern void gimple_seq_add_stmt (gimple_seq *, gimple *);
> extern void gimple_seq_add_stmt_without_update (gimple_seq *, gimple *);
> @@ -1835,6 +1866,7 @@ gimple_has_substatements (gimple *g)
> {
> switch (gimple_code (g))
> {
> + case GIMPLE_ASSUME:
> case GIMPLE_BIND:
> case GIMPLE_CATCH:
> case GIMPLE_EH_FILTER:
> @@ -6520,6 +6552,52 @@ gimple_omp_continue_set_control_use (gom
> cont_stmt->control_use = use;
> }
>
> +/* Return the guard associated with the GIMPLE_ASSUME statement GS. */
> +
> +static inline tree
> +gimple_assume_guard (const gimple *gs)
> +{
> + const gimple_statement_assume *assume_stmt
> + = as_a <const gimple_statement_assume *> (gs);
> + return assume_stmt->guard;
> +}
> +
> +/* Set the guard associated with the GIMPLE_ASSUME statement GS. */
> +
> +static inline void
> +gimple_assume_set_guard (gimple *gs, tree guard)
> +{
> + gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
> + assume_stmt->guard = guard;
> +}
> +
> +static inline tree *
> +gimple_assume_guard_ptr (gimple *gs)
> +{
> + gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
> + return &assume_stmt->guard;
> +}
> +
> +/* Return the address of the GIMPLE sequence contained in the GIMPLE_ASSUME
> + statement GS. */
> +
> +static inline gimple_seq *
> +gimple_assume_body_ptr (gimple *gs)
> +{
> + gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
> + return &assume_stmt->body;
> +}
> +
> +/* Return the GIMPLE sequence contained in the GIMPLE_ASSUME statement GS. */
> +
> +static inline gimple_seq
> +gimple_assume_body (const gimple *gs)
> +{
> + const gimple_statement_assume *assume_stmt
> + = as_a <const gimple_statement_assume *> (gs);
> + return assume_stmt->body;
> +}
> +
> /* Return a pointer to the body for the GIMPLE_TRANSACTION statement
> TRANSACTION_STMT. */
>
> --- gcc/gsstruct.def.jj 2022-05-30 14:07:02.054305846 +0200
> +++ gcc/gsstruct.def 2022-10-17 14:12:35.775087795 +0200
> @@ -50,4 +50,5 @@ DEFGSSTRUCT(GSS_OMP_SINGLE_LAYOUT, gimpl
> DEFGSSTRUCT(GSS_OMP_CONTINUE, gomp_continue, false)
> DEFGSSTRUCT(GSS_OMP_ATOMIC_LOAD, gomp_atomic_load, false)
> DEFGSSTRUCT(GSS_OMP_ATOMIC_STORE_LAYOUT, gomp_atomic_store, false)
> +DEFGSSTRUCT(GSS_ASSUME, gimple_statement_assume, false)
> DEFGSSTRUCT(GSS_TRANSACTION, gtransaction, false)
> --- gcc/gimple.cc.jj 2022-09-08 20:22:07.765184796 +0200
> +++ gcc/gimple.cc 2022-10-17 14:13:06.372672818 +0200
> @@ -1290,6 +1290,18 @@ gimple_build_omp_atomic_store (tree val,
> return p;
> }
>
> +/* Build a GIMPLE_ASSUME statement. */
> +
> +gimple *
> +gimple_build_assume (tree guard, gimple_seq body)
> +{
> + gimple_statement_assume *p
> + = as_a <gimple_statement_assume *> (gimple_alloc (GIMPLE_ASSUME, 0));
> + gimple_assume_set_guard (p, guard);
> + *gimple_assume_body_ptr (p) = body;
> + return p;
> +}
> +
> /* Build a GIMPLE_TRANSACTION statement. */
>
> gtransaction *
> @@ -2135,6 +2147,13 @@ gimple_copy (gimple *stmt)
> gimple_omp_masked_set_clauses (copy, t);
> goto copy_omp_body;
>
> + case GIMPLE_ASSUME:
> + new_seq = gimple_seq_copy (gimple_assume_body (stmt));
> + *gimple_assume_body_ptr (copy) = new_seq;
> + gimple_assume_set_guard (copy,
> + unshare_expr (gimple_assume_guard (stmt)));
> + break;
> +
> case GIMPLE_TRANSACTION:
> new_seq = gimple_seq_copy (gimple_transaction_body (
> as_a <gtransaction *> (stmt)));
> --- gcc/gimple-pretty-print.cc.jj 2022-09-29 09:13:31.256642073 +0200
> +++ gcc/gimple-pretty-print.cc 2022-10-17 14:14:37.488437047 +0200
> @@ -2052,6 +2052,31 @@ dump_gimple_omp_return (pretty_printer *
> }
> }
>
> +/* Dump a GIMPLE_ASSUME tuple on the pretty_printer BUFFER. */
> +
> +static void
> +dump_gimple_assume (pretty_printer *buffer, const gimple *gs,
> + int spc, dump_flags_t flags)
> +{
> + if (flags & TDF_RAW)
> + dump_gimple_fmt (buffer, spc, flags,
> + "%G [GUARD=%T] <%+BODY <%S> >",
> + gs, gimple_assume_guard (gs),
> + gimple_assume_body (gs));
> + else
> + {
> + pp_string (buffer, "[[assume (");
> + dump_generic_node (buffer, gimple_assume_guard (gs), spc, flags, false);
> + pp_string (buffer, ")]]");
> + newline_and_indent (buffer, spc + 2);
> + pp_left_brace (buffer);
> + pp_newline (buffer);
> + dump_gimple_seq (buffer, gimple_assume_body (gs), spc + 4, flags);
> + newline_and_indent (buffer, spc + 2);
> + pp_right_brace (buffer);
> + }
> +}
> +
> /* Dump a GIMPLE_TRANSACTION tuple on the pretty_printer BUFFER. */
>
> static void
> @@ -2841,6 +2866,10 @@ pp_gimple_stmt_1 (pretty_printer *buffer
> pp_string (buffer, " predictor.");
> break;
>
> + case GIMPLE_ASSUME:
> + dump_gimple_assume (buffer, gs, spc, flags);
> + break;
> +
> case GIMPLE_TRANSACTION:
> dump_gimple_transaction (buffer, as_a <const gtransaction *> (gs), spc,
> flags);
> --- gcc/gimple-walk.cc.jj 2022-05-30 14:07:01.936307108 +0200
> +++ gcc/gimple-walk.cc 2022-10-17 13:43:10.796032556 +0200
> @@ -485,6 +485,12 @@ walk_gimple_op (gimple *stmt, walk_tree_
> }
> break;
>
> + case GIMPLE_ASSUME:
> + ret = walk_tree (gimple_assume_guard_ptr (stmt), callback_op, wi, pset);
> + if (ret)
> + return ret;
> + break;
> +
> case GIMPLE_TRANSACTION:
> {
> gtransaction *txn = as_a <gtransaction *> (stmt);
> @@ -706,6 +712,13 @@ walk_gimple_stmt (gimple_stmt_iterator *
> if (ret)
> return wi->callback_result;
> break;
> +
> + case GIMPLE_ASSUME:
> + ret = walk_gimple_seq_mod (gimple_assume_body_ptr (stmt),
> + callback_stmt, callback_op, wi);
> + if (ret)
> + return wi->callback_result;
> + break;
>
> case GIMPLE_TRANSACTION:
> ret = walk_gimple_seq_mod (gimple_transaction_body_ptr (
> --- gcc/omp-low.cc.jj 2022-09-26 18:47:27.056348383 +0200
> +++ gcc/omp-low.cc 2022-10-17 13:45:48.271895674 +0200
> @@ -202,6 +202,7 @@ static bool omp_maybe_offloaded_ctx (omp
> case GIMPLE_TRY: \
> case GIMPLE_CATCH: \
> case GIMPLE_EH_FILTER: \
> + case GIMPLE_ASSUME: \
> case GIMPLE_TRANSACTION: \
> /* The sub-statements for these should be walked. */ \
> *handled_ops_p = false; \
> @@ -14413,6 +14414,9 @@ lower_omp_1 (gimple_stmt_iterator *gsi_p
> lower_omp (gimple_try_eval_ptr (stmt), ctx);
> lower_omp (gimple_try_cleanup_ptr (stmt), ctx);
> break;
> + case GIMPLE_ASSUME:
> + lower_omp (gimple_assume_body_ptr (stmt), ctx);
> + break;
> case GIMPLE_TRANSACTION:
> lower_omp (gimple_transaction_body_ptr (as_a <gtransaction *> (stmt)),
> ctx);
> --- gcc/omp-oacc-kernels-decompose.cc.jj 2022-06-28 13:03:30.951689423 +0200
> +++ gcc/omp-oacc-kernels-decompose.cc 2022-10-17 13:47:10.470780243 +0200
> @@ -189,6 +189,7 @@ adjust_region_code_walk_stmt_fn (gimple_
> case GIMPLE_GOTO:
> case GIMPLE_SWITCH:
> case GIMPLE_ASM:
> + case GIMPLE_ASSUME:
> case GIMPLE_TRANSACTION:
> case GIMPLE_RETURN:
> /* Statement that might constitute some looping/control flow pattern. */
> --- gcc/tree-cfg.cc.jj 2022-10-17 13:48:32.782663305 +0200
> +++ gcc/tree-cfg.cc 2022-10-17 14:38:37.473858786 +0200
> @@ -5139,6 +5139,9 @@ verify_gimple_stmt (gimple *stmt)
> how to setup the parallel iteration. */
> return false;
>
> + case GIMPLE_ASSUME:
> + return false;
> +
> case GIMPLE_DEBUG:
> return verify_gimple_debug (stmt);
>
> @@ -5252,6 +5255,10 @@ verify_gimple_in_seq_2 (gimple_seq stmts
> as_a <gcatch *> (stmt)));
> break;
>
> + case GIMPLE_ASSUME:
> + err |= verify_gimple_in_seq_2 (gimple_assume_body (stmt));
> + break;
> +
> case GIMPLE_TRANSACTION:
> err |= verify_gimple_transaction (as_a <gtransaction *> (stmt));
> break;
> --- gcc/function.h.jj 2022-10-13 08:40:38.046532404 +0200
> +++ gcc/function.h 2022-10-17 12:40:45.574886412 +0200
> @@ -438,6 +438,10 @@ struct GTY(()) function {
>
> /* Set if there are any OMP_TARGET regions in the function. */
> unsigned int has_omp_target : 1;
> +
> + /* Set for artificial function created for [[assume (cond)]].
> + These should be GIMPLE optimized, but not expanded to RTL. */
> + unsigned int assume_function : 1;
> };
>
> /* Add the decl D to the local_decls list of FUN. */
> --- gcc/gimplify.cc.jj 2022-10-13 08:40:38.169530712 +0200
> +++ gcc/gimplify.cc 2022-10-17 14:15:05.616055561 +0200
> @@ -3569,7 +3569,33 @@ gimplify_call_expr (tree *expr_p, gimple
> fndecl, 0));
> return GS_OK;
> }
> - /* FIXME: Otherwise expand it specially. */
> + /* If not optimizing, ignore the assumptions. */
> + if (!optimize)
> + {
> + *expr_p = NULL_TREE;
> + return GS_ALL_DONE;
> + }
> + /* Temporarily, until gimple lowering, transform
> + .ASSUME (cond);
> + into:
> + [[assume (guard)]]
> + {
> + guard = cond;
> + }
> + such that gimple lowering can outline the condition into
> + a separate function easily. */
> + tree guard = create_tmp_var (boolean_type_node);
> + *expr_p = build2 (MODIFY_EXPR, void_type_node, guard,
> + CALL_EXPR_ARG (*expr_p, 0));
> + *expr_p = build3 (BIND_EXPR, void_type_node, NULL, *expr_p, NULL);
> + push_gimplify_context ();
> + gimple_seq body = NULL;
> + gimple *g = gimplify_and_return_first (*expr_p, &body);
> + pop_gimplify_context (g);
> + g = gimple_build_assume (guard, body);
> + gimple_set_location (g, loc);
> + gimplify_seq_add_stmt (pre_p, g);
> + *expr_p = NULL_TREE;
> return GS_ALL_DONE;
> }
>
> --- gcc/gimple-low.cc.jj 2022-10-13 08:40:38.091531785 +0200
> +++ gcc/gimple-low.cc 2022-10-17 16:17:42.154113603 +0200
> @@ -33,6 +33,14 @@ along with GCC; see the file COPYING3.
> #include "predict.h"
> #include "gimple-predict.h"
> #include "gimple-fold.h"
> +#include "cgraph.h"
> +#include "tree-ssa.h"
> +#include "value-range.h"
> +#include "stringpool.h"
> +#include "tree-ssanames.h"
> +#include "tree-inline.h"
> +#include "gimple-walk.h"
> +#include "attribs.h"
>
> /* The differences between High GIMPLE and Low GIMPLE are the
> following:
> @@ -237,6 +245,389 @@ lower_omp_directive (gimple_stmt_iterato
> gsi_next (gsi);
> }
>
> +/* Create an artificial FUNCTION_DECL for assumption at LOC. */
> +
> +static tree
> +create_assumption_fn (location_t loc)
> +{
> + tree name = clone_function_name_numbered (current_function_decl, "_assume");
> + /* Temporarily, until we determine all the arguments. */
> + tree type = build_varargs_function_type_list (boolean_type_node, NULL_TREE);
> + tree decl = build_decl (loc, FUNCTION_DECL, name, type);
> + TREE_STATIC (decl) = 1;
> + TREE_USED (decl) = 1;
> + DECL_ARTIFICIAL (decl) = 1;
> + DECL_IGNORED_P (decl) = 1;
> + DECL_NAMELESS (decl) = 1;
> + TREE_PUBLIC (decl) = 0;
> + DECL_UNINLINABLE (decl) = 1;
> + DECL_EXTERNAL (decl) = 0;
> + DECL_CONTEXT (decl) = NULL_TREE;
> + DECL_INITIAL (decl) = make_node (BLOCK);
> + tree attributes = DECL_ATTRIBUTES (current_function_decl);
> + if (lookup_attribute ("noipa", attributes) == NULL)
> + {
> + attributes = tree_cons (get_identifier ("noipa"), NULL, attributes);
> + if (lookup_attribute ("noinline", attributes) == NULL)
> + attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
> + if (lookup_attribute ("noclone", attributes) == NULL)
> + attributes = tree_cons (get_identifier ("noclone"), NULL, attributes);
> + if (lookup_attribute ("no_icf", attributes) == NULL)
> + attributes = tree_cons (get_identifier ("no_icf"), NULL, attributes);
> + }
> + DECL_ATTRIBUTES (decl) = attributes;
> + BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl;
> + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl)
> + = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (current_function_decl);
> + DECL_FUNCTION_SPECIFIC_TARGET (decl)
> + = DECL_FUNCTION_SPECIFIC_TARGET (current_function_decl);
> + tree t = build_decl (DECL_SOURCE_LOCATION (decl),
> + RESULT_DECL, NULL_TREE, boolean_type_node);
> + DECL_ARTIFICIAL (t) = 1;
> + DECL_IGNORED_P (t) = 1;
> + DECL_CONTEXT (t) = decl;
> + DECL_RESULT (decl) = t;
> + push_struct_function (decl);
> + cfun->function_end_locus = loc;
> + init_tree_ssa (cfun);
> + return decl;
> +}
> +
> +struct lower_assumption_data
> +{
> + copy_body_data id;
> + tree return_false_label;
> + tree guard_copy;
> + auto_vec<tree> decls;
> +};
> +
> +/* Helper function for lower_assumptions. Find local vars and labels
> + in the assumption sequence and remove debug stmts. */
> +
> +static tree
> +find_assumption_locals_r (gimple_stmt_iterator *gsi_p, bool *,
> + struct walk_stmt_info *wi)
> +{
> + lower_assumption_data *data = (lower_assumption_data *) wi->info;
> + gimple *stmt = gsi_stmt (*gsi_p);
> + tree lhs = gimple_get_lhs (stmt);
> + if (lhs && TREE_CODE (lhs) == SSA_NAME)
> + {
> + gcc_assert (SSA_NAME_VAR (lhs) == NULL_TREE);
> + data->id.decl_map->put (lhs, NULL_TREE);
> + data->decls.safe_push (lhs);
> + }
> + switch (gimple_code (stmt))
> + {
> + case GIMPLE_BIND:
> + for (tree var = gimple_bind_vars (as_a <gbind *> (stmt));
> + var; var = DECL_CHAIN (var))
> + if (VAR_P (var)
> + && !DECL_EXTERNAL (var)
> + && DECL_CONTEXT (var) == data->id.src_fn)
> + {
> + data->id.decl_map->put (var, var);
> + data->decls.safe_push (var);
> + }
> + break;
> + case GIMPLE_LABEL:
> + {
> + tree label = gimple_label_label (as_a <glabel *> (stmt));
> + data->id.decl_map->put (label, label);
> + break;
> + }
> + case GIMPLE_RETURN:
> + /* If something in assumption tries to return from parent function,
> + if it would be reached in hypothetical evaluation, it would be UB,
> + so transform such returns into return false; */
> + {
> + gimple *g = gimple_build_assign (data->guard_copy, boolean_false_node);
> + gsi_insert_before (gsi_p, g, GSI_SAME_STMT);
> + gimple_return_set_retval (as_a <greturn *> (stmt), data->guard_copy);
> + break;
> + }
> + case GIMPLE_DEBUG:
> + /* As assumptions won't be emitted, debug info stmts in them
> + are useless. */
> + gsi_remove (gsi_p, true);
> + wi->removed_stmt = true;
> + break;
> + default:
> + break;
> + }
> + return NULL_TREE;
> +}
> +
> +/* Create a new PARM_DECL that is indentical in all respect to DECL except that
> + DECL can be either a VAR_DECL, a PARM_DECL or RESULT_DECL. The original
> + DECL must come from ID->src_fn and the copy will be part of ID->dst_fn. */
> +
> +static tree
> +assumption_copy_decl (tree decl, copy_body_data *id)
> +{
> + tree type = TREE_TYPE (decl);
> +
> + if (is_global_var (decl))
> + return decl;
> +
> + gcc_assert (VAR_P (decl)
> + || TREE_CODE (decl) == PARM_DECL
> + || TREE_CODE (decl) == RESULT_DECL);
> + tree copy = build_decl (DECL_SOURCE_LOCATION (decl),
> + PARM_DECL, DECL_NAME (decl), type);
> + if (DECL_PT_UID_SET_P (decl))
> + SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
> + TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
> + TREE_READONLY (copy) = TREE_READONLY (decl);
> + TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
> + DECL_NOT_GIMPLE_REG_P (copy) = DECL_NOT_GIMPLE_REG_P (decl);
> + DECL_BY_REFERENCE (copy) = DECL_BY_REFERENCE (decl);
> + DECL_ARG_TYPE (copy) = type;
> + ((lower_assumption_data *) id)->decls.safe_push (decl);
> + return copy_decl_for_dup_finish (id, decl, copy);
> +}
> +
> +/* Transform gotos out of the assumption into return false. */
> +
> +static tree
> +adjust_assumption_stmt_r (gimple_stmt_iterator *gsi_p, bool *,
> + struct walk_stmt_info *wi)
> +{
> + lower_assumption_data *data = (lower_assumption_data *) wi->info;
> + gimple *stmt = gsi_stmt (*gsi_p);
> + tree lab = NULL_TREE;
> + unsigned int idx = 0;
> + if (gimple_code (stmt) == GIMPLE_GOTO)
> + lab = gimple_goto_dest (stmt);
> + else if (gimple_code (stmt) == GIMPLE_COND)
> + {
> + repeat:
> + if (idx == 0)
> + lab = gimple_cond_true_label (as_a <gcond *> (stmt));
> + else
> + lab = gimple_cond_false_label (as_a <gcond *> (stmt));
> + }
> + else if (gimple_code (stmt) == GIMPLE_LABEL)
> + {
> + tree label = gimple_label_label (as_a <glabel *> (stmt));
> + DECL_CONTEXT (label) = current_function_decl;
> + }
> + if (lab)
> + {
> + if (!data->id.decl_map->get (lab))
> + {
> + if (!data->return_false_label)
> + data->return_false_label
> + = create_artificial_label (UNKNOWN_LOCATION);
> + if (gimple_code (stmt) == GIMPLE_GOTO)
> + gimple_goto_set_dest (as_a <ggoto *> (stmt),
> + data->return_false_label);
> + else if (idx == 0)
> + gimple_cond_set_true_label (as_a <gcond *> (stmt),
> + data->return_false_label);
> + else
> + gimple_cond_set_false_label (as_a <gcond *> (stmt),
> + data->return_false_label);
> + }
> + if (gimple_code (stmt) == GIMPLE_COND && idx == 0)
> + {
> + idx = 1;
> + goto repeat;
> + }
> + }
> + return NULL_TREE;
> +}
> +
> +/* Adjust trees in the assumption body. Called through walk_tree. */
> +
> +static tree
> +adjust_assumption_stmt_op (tree *tp, int *, void *datap)
> +{
> + struct walk_stmt_info *wi = (struct walk_stmt_info *) datap;
> + lower_assumption_data *data = (lower_assumption_data *) wi->info;
> + tree t = *tp;
> + tree *newt;
> + switch (TREE_CODE (t))
> + {
> + case SSA_NAME:
> + newt = data->id.decl_map->get (t);
> + /* There shouldn't be SSA_NAMEs other than ones defined in the
> + assumption's body. */
> + gcc_assert (newt);
> + *tp = *newt;
> + break;
> + case LABEL_DECL:
> + newt = data->id.decl_map->get (t);
> + if (newt)
> + *tp = *newt;
> + break;
> + case VAR_DECL:
> + case PARM_DECL:
> + case RESULT_DECL:
> + *tp = remap_decl (t, &data->id);
> + break;
> + default:
> + break;
> + }
> + return NULL_TREE;
> +}
> +
> +/* Lower assumption.
> + The gimplifier transformed:
> + .ASSUME (cond);
> + into:
> + [[assume (guard)]]
> + {
> + guard = cond;
> + }
> + which we should transform into:
> + .ASSUME (&artificial_fn, args...);
> + where artificial_fn will look like:
> + bool artificial_fn (args...)
> + {
> + guard = cond;
> + return guard;
> + }
> + with any debug stmts in the block removed and jumps out of
> + the block or return stmts replaced with return false; */
> +
> +static void
> +lower_assumption (gimple_stmt_iterator *gsi, struct lower_data *data)
> +{
> + gimple *stmt = gsi_stmt (*gsi);
> + tree guard = gimple_assume_guard (stmt);
> + gimple *bind = gimple_assume_body (stmt);
> + location_t loc = gimple_location (stmt);
> + gcc_assert (gimple_code (bind) == GIMPLE_BIND);
> +
> + lower_assumption_data lad;
> + hash_map<tree, tree> decl_map;
> + memset (&lad.id, 0, sizeof (lad.id));
> + lad.return_false_label = NULL_TREE;
> + lad.id.src_fn = current_function_decl;
> + lad.id.dst_fn = create_assumption_fn (loc);
> + lad.id.src_cfun = DECL_STRUCT_FUNCTION (lad.id.src_fn);
> + lad.id.decl_map = &decl_map;
> + lad.id.copy_decl = assumption_copy_decl;
> + lad.id.transform_call_graph_edges = CB_CGE_DUPLICATE;
> + lad.id.transform_parameter = true;
> + lad.id.do_not_unshare = true;
> + lad.id.do_not_fold = true;
> + cfun->curr_properties = lad.id.src_cfun->curr_properties;
> + lad.guard_copy = create_tmp_var (boolean_type_node);
> + decl_map.put (lad.guard_copy, lad.guard_copy);
> + decl_map.put (guard, lad.guard_copy);
> + cfun->assume_function = 1;
> +
> + /* Find variables, labels and SSA_NAMEs local to the assume GIMPLE_BIND. */
> + gimple_stmt_iterator gsi2 = gsi_start (*gimple_assume_body_ptr (stmt));
> + struct walk_stmt_info wi;
> + memset (&wi, 0, sizeof (wi));
> + wi.info = (void *) &lad;
> + walk_gimple_stmt (&gsi2, find_assumption_locals_r, NULL, &wi);
> + unsigned int sz = lad.decls.length ();
> + for (unsigned i = 0; i < sz; ++i)
> + {
> + tree v = lad.decls[i];
> + tree newv;
> + /* SSA_NAMEs defined in the assume condition should be replaced
> + by new SSA_NAMEs in the artificial function. */
> + if (TREE_CODE (v) == SSA_NAME)
> + {
> + newv = make_ssa_name (remap_type (TREE_TYPE (v), &lad.id));
> + decl_map.put (v, newv);
> + }
> + /* Local vars should have context and type adjusted to the
> + new artificial function. */
> + else if (VAR_P (v))
> + {
> + if (is_global_var (v) && !DECL_ASSEMBLER_NAME_SET_P (v))
> + DECL_ASSEMBLER_NAME (v);
> + TREE_TYPE (v) = remap_type (TREE_TYPE (v), &lad.id);
> + DECL_CONTEXT (v) = current_function_decl;
> + }
> + }
> + /* References to other automatic vars should be replaced by
> + PARM_DECLs to the artificial function. */
> + memset (&wi, 0, sizeof (wi));
> + wi.info = (void *) &lad;
> + walk_gimple_stmt (&gsi2, adjust_assumption_stmt_r,
> + adjust_assumption_stmt_op, &wi);
> +
> + /* At the start prepend guard = false; */
> + gimple_seq body = NULL;
> + gimple *g = gimple_build_assign (lad.guard_copy, boolean_false_node);
> + gimple_seq_add_stmt (&body, g);
> + gimple_seq_add_stmt (&body, bind);
> + /* At the end add return guard; */
> + greturn *gr = gimple_build_return (lad.guard_copy);
> + gimple_seq_add_stmt (&body, gr);
> + /* If there were any jumps to labels outside of the condition,
> + replace them with a jump to
> + return_false_label:
> + guard = false;
> + return guard; */
> + if (lad.return_false_label)
> + {
> + g = gimple_build_label (lad.return_false_label);
> + gimple_seq_add_stmt (&body, g);
> + g = gimple_build_assign (lad.guard_copy, boolean_false_node);
> + gimple_seq_add_stmt (&body, g);
> + gr = gimple_build_return (lad.guard_copy);
> + gimple_seq_add_stmt (&body, gr);
> + }
> + bind = gimple_build_bind (NULL_TREE, body, NULL_TREE);
> + body = NULL;
> + gimple_seq_add_stmt (&body, bind);
> + gimple_set_body (current_function_decl, body);
> + pop_cfun ();
> +
> + tree parms = NULL_TREE;
> + tree parmt = void_list_node;
> + auto_vec<tree, 8> vargs;
> + vargs.safe_grow (1 + (lad.decls.length () - sz), true);
> + /* First argument to IFN_ASSUME will be address of the
> + artificial function. */
> + vargs[0] = build_fold_addr_expr (lad.id.dst_fn);
> + for (unsigned i = lad.decls.length (); i > sz; --i)
> + {
> + tree *v = decl_map.get (lad.decls[i - 1]);
> + gcc_assert (v && TREE_CODE (*v) == PARM_DECL);
> + DECL_CHAIN (*v) = parms;
> + parms = *v;
> + parmt = tree_cons (NULL_TREE, TREE_TYPE (*v), parmt);
> + /* Remaining arguments will be the variables/parameters
> + mentioned in the condition. */
> + vargs[i - sz] = lad.decls[i - 1];
> + /* If they have gimple types, we might need to regimplify
> + them to make the IFN_ASSUME call valid. */
> + if (is_gimple_reg_type (TREE_TYPE (vargs[i - sz]))
> + && !is_gimple_val (vargs[i - sz]))
> + {
> + tree t = make_ssa_name (TREE_TYPE (vargs[i - sz]));
> + g = gimple_build_assign (t, vargs[i - sz]);
> + gsi_insert_before (gsi, g, GSI_SAME_STMT);
> + vargs[i - sz] = t;
> + }
> + }
> + DECL_ARGUMENTS (lad.id.dst_fn) = parms;
> + TREE_TYPE (lad.id.dst_fn) = build_function_type (boolean_type_node, parmt);
> +
> + cgraph_node::add_new_function (lad.id.dst_fn, false);
> +
> + for (unsigned i = 0; i < sz; ++i)
> + {
> + tree v = lad.decls[i];
> + if (TREE_CODE (v) == SSA_NAME)
> + release_ssa_name (v);
> + }
> +
> + data->cannot_fallthru = false;
> + /* Replace GIMPLE_ASSUME statement with IFN_ASSUME call. */
> + gcall *call = gimple_build_call_internal_vec (IFN_ASSUME, vargs);
> + gimple_set_location (call, loc);
> + gsi_replace (gsi, call, true);
> +}
>
> /* Lower statement GSI. DATA is passed through the recursion. We try to
> track the fallthruness of statements and get rid of unreachable return
> @@ -403,6 +794,10 @@ lower_stmt (gimple_stmt_iterator *gsi, s
> data->cannot_fallthru = false;
> return;
>
> + case GIMPLE_ASSUME:
> + lower_assumption (gsi, data);
> + return;
> +
> case GIMPLE_TRANSACTION:
> lower_sequence (gimple_transaction_body_ptr (
> as_a <gtransaction *> (stmt)),
> --- gcc/tree-ssa-ccp.cc.jj 2022-10-13 08:40:38.478526460 +0200
> +++ gcc/tree-ssa-ccp.cc 2022-10-17 12:40:45.696884755 +0200
> @@ -4253,6 +4253,12 @@ pass_fold_builtins::execute (function *f
> }
>
> callee = gimple_call_fndecl (stmt);
> + if (!callee
> + && gimple_call_internal_p (stmt, IFN_ASSUME))
> + {
> + gsi_remove (&i, true);
> + continue;
> + }
> if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
> {
> gsi_next (&i);
> --- gcc/lto-streamer-out.cc.jj 2022-10-13 08:40:38.373527904 +0200
> +++ gcc/lto-streamer-out.cc 2022-10-17 12:40:45.720884429 +0200
> @@ -2278,6 +2278,7 @@ output_struct_function_base (struct outp
> bp_pack_value (&bp, fn->calls_eh_return, 1);
> bp_pack_value (&bp, fn->has_force_vectorize_loops, 1);
> bp_pack_value (&bp, fn->has_simduid_loops, 1);
> + bp_pack_value (&bp, fn->assume_function, 1);
> bp_pack_value (&bp, fn->va_list_fpr_size, 8);
> bp_pack_value (&bp, fn->va_list_gpr_size, 8);
> bp_pack_value (&bp, fn->last_clique, sizeof (short) * 8);
> --- gcc/lto-streamer-in.cc.jj 2022-10-13 08:40:38.371527932 +0200
> +++ gcc/lto-streamer-in.cc 2022-10-17 12:40:45.741884143 +0200
> @@ -1318,6 +1318,7 @@ input_struct_function_base (struct funct
> fn->calls_eh_return = bp_unpack_value (&bp, 1);
> fn->has_force_vectorize_loops = bp_unpack_value (&bp, 1);
> fn->has_simduid_loops = bp_unpack_value (&bp, 1);
> + fn->assume_function = bp_unpack_value (&bp, 1);
> fn->va_list_fpr_size = bp_unpack_value (&bp, 8);
> fn->va_list_gpr_size = bp_unpack_value (&bp, 8);
> fn->last_clique = bp_unpack_value (&bp, sizeof (short) * 8);
> --- gcc/cgraphunit.cc.jj 2022-10-13 08:40:37.868534853 +0200
> +++ gcc/cgraphunit.cc 2022-10-17 16:45:46.995270327 +0200
> @@ -1882,6 +1882,16 @@ cgraph_node::expand (void)
> ggc_collect ();
> timevar_pop (TV_REST_OF_COMPILATION);
>
> + if (DECL_STRUCT_FUNCTION (decl)
> + && DECL_STRUCT_FUNCTION (decl)->assume_function)
> + {
> + /* Assume functions aren't expanded into RTL, on the other side
> + we don't want to release their body. */
> + if (cfun)
> + pop_cfun ();
> + return;
> + }
> +
> /* Make sure that BE didn't give up on compiling. */
> gcc_assert (TREE_ASM_WRITTEN (decl));
> if (cfun)
> @@ -2373,6 +2383,10 @@ symbol_table::compile (void)
> if (node->inlined_to
> || gimple_has_body_p (node->decl))
> {
> + if (DECL_STRUCT_FUNCTION (node->decl)
> + && (DECL_STRUCT_FUNCTION (node->decl)->curr_properties
> + & PROP_assumptions_done) != 0)
> + continue;
> error_found = true;
> node->debug ();
> }
> --- gcc/internal-fn.cc.jj 2022-10-13 08:40:38.218530037 +0200
> +++ gcc/internal-fn.cc 2022-10-17 12:40:45.782883587 +0200
> @@ -4526,5 +4526,4 @@ expand_TRAP (internal_fn, gcall *)
> void
> expand_ASSUME (internal_fn, gcall *)
> {
> - gcc_unreachable ();
> }
> --- gcc/passes.cc.jj 2022-10-13 08:40:38.419527272 +0200
> +++ gcc/passes.cc 2022-10-17 16:38:23.325283213 +0200
> @@ -2660,6 +2660,15 @@ execute_one_pass (opt_pass *pass)
> if (dom_info_available_p (CDI_POST_DOMINATORS))
> free_dominance_info (CDI_POST_DOMINATORS);
>
> + if (cfun->assume_function)
> + {
> + /* For assume functions, don't release body, keep it around. */
> + cfun->curr_properties |= PROP_assumptions_done;
> + pop_cfun ();
> + current_pass = NULL;
> + return true;
> + }
> +
> tree fn = cfun->decl;
> pop_cfun ();
> gcc_assert (!cfun);
> --- gcc/cgraph.cc.jj 2022-06-27 11:18:02.047066621 +0200
> +++ gcc/cgraph.cc 2022-10-17 16:40:24.319643418 +0200
> @@ -3751,7 +3751,9 @@ cgraph_node::verify_node (void)
> && (!DECL_EXTERNAL (decl) || inlined_to)
> && !flag_wpa)
> {
> - if (this_cfun->cfg)
> + if ((this_cfun->curr_properties & PROP_assumptions_done) != 0)
> + ;
> + else if (this_cfun->cfg)
> {
> hash_set<gimple *> stmts;
>
> --- gcc/tree-pass.h.jj 2022-07-26 10:32:24.020267414 +0200
> +++ gcc/tree-pass.h 2022-10-17 16:36:12.800052172 +0200
> @@ -227,6 +227,8 @@ protected:
> #define PROP_rtl_split_insns (1 << 17) /* RTL has insns split. */
> #define PROP_loop_opts_done (1 << 18) /* SSA loop optimizations
> have completed. */
> +#define PROP_assumptions_done (1 << 19) /* Assume function kept
> + around. */
>
> #define PROP_gimple \
> (PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
> @@ -301,7 +303,8 @@ protected:
> /* Rebuild the callgraph edges. */
> #define TODO_rebuild_cgraph_edges (1 << 22)
>
> -/* Release function body and stop pass manager. */
> +/* Release function body (unless assumption function)
> + and stop pass manager. */
> #define TODO_discard_function (1 << 23)
>
> /* Internally used in execute_function_todo(). */
> @@ -465,6 +468,7 @@ extern gimple_opt_pass *make_pass_copy_p
> extern gimple_opt_pass *make_pass_isolate_erroneous_paths (gcc::context *ctxt);
> extern gimple_opt_pass *make_pass_early_vrp (gcc::context *ctxt);
> extern gimple_opt_pass *make_pass_vrp (gcc::context *ctxt);
> +extern gimple_opt_pass *make_pass_assumptions (gcc::context *ctxt);
> extern gimple_opt_pass *make_pass_uncprop (gcc::context *ctxt);
> extern gimple_opt_pass *make_pass_return_slot (gcc::context *ctxt);
> extern gimple_opt_pass *make_pass_reassoc (gcc::context *ctxt);
> --- gcc/passes.def.jj 2022-09-23 09:02:56.876313524 +0200
> +++ gcc/passes.def 2022-10-17 16:11:58.526770578 +0200
> @@ -407,6 +407,7 @@ along with GCC; see the file COPYING3.
> and thus it should be run last. */
> NEXT_PASS (pass_uncprop);
> POP_INSERT_PASSES ()
> + NEXT_PASS (pass_assumptions);
> NEXT_PASS (pass_tm_init);
> PUSH_INSERT_PASSES_WITHIN (pass_tm_init)
> NEXT_PASS (pass_tm_mark);
> --- gcc/timevar.def.jj 2022-09-03 09:35:41.334986488 +0200
> +++ gcc/timevar.def 2022-10-17 16:06:57.336852424 +0200
> @@ -226,6 +226,7 @@ DEFTIMEVAR (TV_TREE_WIDEN_MUL , "
> DEFTIMEVAR (TV_TRANS_MEM , "transactional memory")
> DEFTIMEVAR (TV_TREE_STRLEN , "tree strlen optimization")
> DEFTIMEVAR (TV_TREE_MODREF , "tree modref")
> +DEFTIMEVAR (TV_TREE_ASSUMPTIONS , "tree assumptions")
> DEFTIMEVAR (TV_CGRAPH_VERIFY , "callgraph verifier")
> DEFTIMEVAR (TV_DOM_FRONTIERS , "dominance frontiers")
> DEFTIMEVAR (TV_DOMINANCE , "dominance computation")
> --- gcc/tree-inline.cc.jj 2022-10-07 09:08:43.887133549 +0200
> +++ gcc/tree-inline.cc 2022-10-17 13:50:11.755320277 +0200
> @@ -1736,6 +1736,11 @@ remap_gimple_stmt (gimple *stmt, copy_bo
> (as_a <gomp_critical *> (stmt)));
> break;
>
> + case GIMPLE_ASSUME:
> + s1 = remap_gimple_seq (gimple_assume_body (stmt), id);
> + copy = gimple_build_assume (gimple_assume_guard (stmt), s1);
> + break;
> +
> case GIMPLE_TRANSACTION:
> {
> gtransaction *old_trans_stmt = as_a <gtransaction *> (stmt);
> --- gcc/tree-vrp.cc.jj 2022-09-23 09:02:57.099310450 +0200
> +++ gcc/tree-vrp.cc 2022-10-17 16:37:10.623268518 +0200
> @@ -4441,6 +4441,35 @@ public:
> int my_pass;
> }; // class pass_vrp
>
> +const pass_data pass_data_assumptions =
> +{
> + GIMPLE_PASS, /* type */
> + "assumptions", /* name */
> + OPTGROUP_NONE, /* optinfo_flags */
> + TV_TREE_ASSUMPTIONS, /* tv_id */
> + PROP_ssa, /* properties_required */
> + PROP_assumptions_done, /* properties_provided */
> + 0, /* properties_destroyed */
> + 0, /* todo_flags_start */
> + 0, /* todo_flags_end */
> +};
> +
> +class pass_assumptions : public gimple_opt_pass
> +{
> +public:
> + pass_assumptions (gcc::context *ctxt)
> + : gimple_opt_pass (pass_data_assumptions, ctxt)
> + {}
> +
> + /* opt_pass methods: */
> + bool gate (function *fun) final override { return fun->assume_function; }
> + unsigned int execute (function *) final override
> + {
> + return TODO_discard_function;
> + }
> +
> +}; // class pass_assumptions
> +
> } // anon namespace
>
> gimple_opt_pass *
> @@ -4454,3 +4483,9 @@ make_pass_early_vrp (gcc::context *ctxt)
> {
> return new pass_vrp (ctxt, pass_data_early_vrp);
> }
> +
> +gimple_opt_pass *
> +make_pass_assumptions (gcc::context *ctx)
> +{
> + return new pass_assumptions (ctx);
> +}
> --- gcc/cp/cp-tree.h.jj 2022-10-14 09:35:56.201990233 +0200
> +++ gcc/cp/cp-tree.h 2022-10-17 12:40:45.842882772 +0200
> @@ -8280,6 +8280,7 @@ extern tree predeclare_vla (tree);
> extern void clear_fold_cache (void);
> extern tree lookup_hotness_attribute (tree);
> extern tree process_stmt_hotness_attribute (tree, location_t);
> +extern tree build_assume_call (location_t, tree);
> extern tree process_stmt_assume_attribute (tree, tree, location_t);
> extern bool simple_empty_class_p (tree, tree, tree_code);
> extern tree fold_builtin_source_location (location_t);
> --- gcc/cp/parser.cc.jj 2022-10-14 09:28:28.006164065 +0200
> +++ gcc/cp/parser.cc 2022-10-17 12:40:45.897882025 +0200
> @@ -46012,11 +46012,7 @@ cp_parser_omp_assumption_clauses (cp_par
> if (!type_dependent_expression_p (t))
> t = contextual_conv_bool (t, tf_warning_or_error);
> if (is_assume && !error_operand_p (t))
> - {
> - t = build_call_expr_internal_loc (eloc, IFN_ASSUME,
> - void_type_node, 1, t);
> - finish_expr_stmt (t);
> - }
> + finish_expr_stmt (build_assume_call (eloc, t));
> if (!parens.require_close (parser))
> cp_parser_skip_to_closing_parenthesis (parser,
> /*recovering=*/true,
> --- gcc/cp/cp-gimplify.cc.jj 2022-10-14 09:28:28.154162026 +0200
> +++ gcc/cp/cp-gimplify.cc 2022-10-17 12:40:45.931881563 +0200
> @@ -3101,6 +3101,17 @@ process_stmt_hotness_attribute (tree std
> return std_attrs;
> }
>
> +/* Build IFN_ASSUME internal call for assume condition ARG. */
> +
> +tree
> +build_assume_call (location_t loc, tree arg)
> +{
> + if (!processing_template_decl)
> + arg = fold_build_cleanup_point_expr (TREE_TYPE (arg), arg);
> + return build_call_expr_internal_loc (loc, IFN_ASSUME, void_type_node,
> + 1, arg);
> +}
> +
> /* If [[assume (cond)]] appears on this statement, handle it. */
>
> tree
> @@ -3137,9 +3148,7 @@ process_stmt_assume_attribute (tree std_
> arg = contextual_conv_bool (arg, tf_warning_or_error);
> if (error_operand_p (arg))
> continue;
> - statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
> - void_type_node, 1, arg);
> - finish_expr_stmt (statement);
> + finish_expr_stmt (build_assume_call (attrs_loc, arg));
> }
> }
> return remove_attribute ("gnu", "assume", std_attrs);
> --- gcc/cp/pt.cc.jj 2022-10-14 09:28:28.135162288 +0200
> +++ gcc/cp/pt.cc 2022-10-17 12:40:45.985880829 +0200
> @@ -21140,10 +21140,7 @@ tsubst_copy_and_build (tree t,
> ret = error_mark_node;
> break;
> }
> - ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
> - IFN_ASSUME,
> - void_type_node, 1,
> - arg);
> + ret = build_assume_call (EXPR_LOCATION (t), arg);
> RETURN (ret);
> }
> break;
> --- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj 2022-10-17 12:40:45.985880829 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C 2022-10-17 12:40:45.985880829 +0200
> @@ -0,0 +1,5 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +// { dg-options "-O2" }
> +
> +#include "attr-assume1.C"
> --- gcc/testsuite/g++.dg/cpp23/attr-assume6.C.jj 2022-10-17 12:40:45.986880816 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume6.C 2022-10-17 12:40:45.986880816 +0200
> @@ -0,0 +1,5 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do run { target c++11 } }
> +// { dg-options "-O2" }
> +
> +#include "attr-assume3.C"
> --- gcc/testsuite/g++.dg/cpp23/attr-assume7.C.jj 2022-10-17 12:40:45.986880816 +0200
> +++ gcc/testsuite/g++.dg/cpp23/attr-assume7.C 2022-10-17 17:39:43.992411442 +0200
> @@ -0,0 +1,56 @@
> +// P1774R8 - Portable assumptions
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-O2" }
> +
> +int
> +foo (int x)
> +{
> + [[assume (x == 42)]];
> + return x;
> +}
> +
> +int
> +bar (int x)
> +{
> + [[assume (++x == 43)]];
> + return x;
> +}
> +
> +int
> +baz (int x)
> +{
> + [[assume (({ int z = ++x; static int w; ++w; if (z == 51) return -1; if (z == 53) goto lab1; if (z == 64) throw 1; z == 43; }))]];
> +lab1:
> + return x;
> +}
> +
> +struct S { S (); S (const S &); ~S (); int a, b; int foo (); };
> +
> +int
> +qux ()
> +{
> + S s;
> + [[assume (s.a == 42 && s.b == 43)]];
> + return s.a + s.b;
> +}
> +
> +int
> +S::foo ()
> +{
> + [[assume (a == 42 && b == 43)]];
> + return a + b;
> +}
> +
> +int
> +corge (int x)
> +{
> + [[assume (({ [[assume (x < 42)]]; x > -42; }))]];
> + return x < 42;
> +}
> +
> +int
> +garply (int x)
> +{
> + [[assume (({ [[assume (++x < 43)]]; x > -42; }))]];
> + return x < 42;
> +}
>
>
> Jakub
>
>
On 10/17/22 11:44, Jakub Jelinek via Gcc-patches wrote:
>
> Added 2 tests for nested assumptions (one with a simple assumption
> nested in complex one, one with side-effects one nested in complex one).
>
> So far lightly tested, will test fully overnight.
>
> 2022-10-17 Jakub Jelinek<jakub@redhat.com>
>
OK, new prototype update. I have moved the code to a new "class
assume_query" which is still in gimple-range.h
We don't need all the heavy lifting of a full on ranger, just an
ssa-name table and a gori_compute module. GORI requires a range_query
class, so assume_query derived form that.
patch1 is the same/similar infer processing that looks for assume calls
and adds any ranges as a side effect. I removed the hack and now its
simply returns whetever the global value is, so its no net effect.
patch2 is the assume_query class. When created, it will "process" the
current function up front. It currently only works on function with a
single integral return statement. It should never fail, it'll simpy
return VARYING for anything it cant determine a value for, or if its an
inappropriate function.
When its create, you can then query using
bool assume_range_p (vrange &r, tree name);
the value of any ssa-name in the function. They are all
pre-calculated, so this is simply picking up any value that was saved.
So the good stuff! *Whats now supported?* It begins with the return
statement having a value of [1,1] and begins going back to defs, and
evaluating any operands on that statement.
PHIs are now processed.
- If an argument is symbolic, we try substituting the LHS for the
argument, and go to its def. continuing the process.
- If an argument is a constant, we check if its compatible with the LHS.
- If the intersection is undefined, then that edge cannot be
executed, and it is ignored
- if the is defined, then we examine the predecessor block to see
if the exit condition which took this edge provides any useful into.
And finally, when the def chain terminates and we cant go any further,
it also checks to see if this block has a single predecessor, and if so,
if taking the edge to get here was the result of a condition that
supplies yet more range info.
Caveats. Im not doing a lot of error checking. If I encounter the
definition of an ssa-name a second time, I simply stop. This avoid
infinite loops, and re-doing the same work. Its unclear to me in a
complex case if we might need to do some value merging at merge points,
but at this point, we make no attempt. I'd need to find a case where
this was problematic, and I see none yet.
*What can you expect? *Using a testcase sample attr-assume-7.C (and
patch 3 which hacks VRP to spit out what values it would find) it
provides the following values:
bar()
<bb 2> :
x_3 = x_2(D) + 1;
_4 = x_2(D) <= 41;
return _4;
for an assume function, x_2(D) would have a range of [irange] int [-INF, 41]
for an assume function, _4 would have a range of [irange] bool [1, 1]
baz()
|<bb 2> :
x_4 = x_3(D) + 1;
w.0_7 = w;
w.1_8 = w.0_7 + 1;
w = w.1_8;
if (x_4 == 51)
goto <bb 3>; [INV]
else
goto <bb 4>; [INV]
<bb 3> :
// predicted unlikely by early return (on trees) predictor.
goto <bb 9>; [INV]
<bb 4> :
if (x_4 == 53)
goto <bb 5>; [INV]
else
goto <bb 6>; [INV]
<bb 5> :
// predicted unlikely by goto predictor.
goto <bb 9>; [INV]
<bb 6> :
if (x_4 == 64)
goto <bb 7>; [INV]
else
goto <bb 8>; [INV]
<bb 7> :
_12 = __cxa_allocate_exception (4);
MEM[(int *)_12] = 1;
__cxa_throw (_12, &_ZTIi, 0B);
<bb 8> :
_10 = x_4 == 43;
<bb 9> :
# _1 = PHI <0(3), _10(8), 0(5)>
return _1;
for an assume function, _1 would have a range of [irange] bool [1, 1]
for an assume function, x_3(D) would have a range of [irange] int [42,
42] NONZERO 0x2a
for an assume function, x_4 would have a range of [irange] int [43, 43]
NONZERO 0x2b
for an assume function, _10 would have a range of [irange] bool [1, 1]
qux()
<bb 2> :
_4 = s.a;
if (_4 == 42)
goto <bb 3>; [INV]
else
goto <bb 4>; [INV]
<bb 3> :
_5 = s.b;
if (_5 == 43)
goto <bb 5>; [INV]
else
goto <bb 4>; [INV]
<bb 4> :
<bb 5> :
# iftmp.2_1 = PHI <1(3), 0(4)>
return iftmp.2_1;
for an assume function, iftmp.2_1 would have a range of [irange] bool [1, 1]
for an assume function, _4 would have a range of [irange] int [42, 42]
NONZERO 0x2a
for an assume function, _5 would have a range of [irange] int [43, 43]
NONZERO 0x2b
S::foo()
<bb 2> :
_5 = this_4(D)->a;
if (_5 == 42)
goto <bb 3>; [INV]
else
goto <bb 4>; [INV]
<bb 3> :
_6 = this_4(D)->b;
if (_6 == 43)
goto <bb 5>; [INV]
else
goto <bb 4>; [INV]
<bb 4> :
<bb 5> :
# iftmp.3_1 = PHI <1(3), 0(4)>
return iftmp.3_1;
for an assume function, iftmp.3_1 would have a range of [irange] bool [1, 1]
for an assume function, _5 would have a range of [irange] int [42, 42]
NONZERO 0x2a
for an assume function, _6 would have a range of [irange] int [43, 43]
NONZERO 0x2b
corge()
<bb 2> :
if (x_2(D) <= 41)
goto <bb 4>; [INV]
else
goto <bb 3>; [INV]
<bb 3> :
__builtin_unreachable ();
<bb 4> :
_3 = x_2(D) >= -41;
return _3;
for an assume function, x_2(D) would have a range of [irange] int [-41,
+INF]
for an assume function, _3 would have a range of [irange] bool [1, 1]
---------------------------------------------------------------------------------------------------------
THis seems to provide a reasonable amount of functionality. As you can
see by the qux() and S:foo() cases, if you can figure out how to map
structures and pointers as parameters, we can produce the ranges
"produced" when they are loaded. ie we know _4 is [42,42] and that it
was loaded from s.a:
_4 = s.a;
Presumably what is needed is a pass (which can be anywhere you want)
which invokes an assume_query, and then tries to map the parameter
values to ssa-names.
Anyway, gives you something to experiement with. If you would find a
different interface useful, let me know, or if there are limitations or
other expansions we might need. This seems like something reasonable
for you to start working with?
Let me know what you think. This all bootstraps fine, I could check it
into the code base if it helps.
Andrew
Hi!
On Tue, Oct 18, 2022 at 05:31:58PM -0400, Andrew MacLeod wrote:
> Anyway, gives you something to experiement with. If you would find a
> different interface useful, let me know, or if there are limitations or
> other expansions we might need. This seems like something reasonable for
> you to start working with?
Thanks for working on this.
> + // Look for ASSUME calls, and call query_assume_call for each argument
> + // to determine if there is any inferred range to be had.
> + if (is_a<gcall *> (s) && gimple_call_internal_p (s)
> + && gimple_call_internal_fn (s) == IFN_ASSUME)
> + {
> + tree assume_id = gimple_call_arg (s, 0);
> + for (unsigned i = 1; i < gimple_call_num_args (s); i++)
> + {
> + tree op = gimple_call_arg (s, i);
> + tree type = TREE_TYPE (op);
> + if (gimple_range_ssa_p (op) && Value_Range::supports_type_p (type))
> + {
> + Value_Range assume_range (type);
> + if (query_assume_call (assume_range, assume_id, op))
> + {
> + add_range (op, assume_range);
> + if (dump_file)
> + {
> + print_generic_expr (dump_file, assume_id, TDF_SLIM);
> + fprintf (dump_file, " assume inferred range of ");
> + print_generic_expr (dump_file, op, TDF_SLIM);
> + fprintf (dump_file, " to ");
> + assume_range.dump (dump_file);
> + fputc ('\n', dump_file);
> + }
> + }
Not sure I understand this part. op is whatever we pass as the ith
argument to IFN_ASSUME. I'd expect that at this point one needs to
remap that to the (i-1)th PARM_DECL of assume_id (so e.g. when you
have the above loop you could as well start with DECL_ARGUMENTS and move
that to DECL_CHAIN at the end of every iteration. And then
query ssa_default_def (DECL_STRUCT_FUNCTION (assume_id), parm)
in each case and get global range of what that returns.
> + for (unsigned x= 0; x < gimple_phi_num_args (phi); x++)
for (unsigned x = 0; ...
?
> @@ -4345,6 +4345,30 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p)
> scev_initialize ();
> calculate_dominance_info (CDI_DOMINATORS);
>
> + assume_query *r2 = new assume_query ();
> + for (unsigned i = 0; i < num_ssa_names; i++)
> + {
> + tree name = ssa_name (i);
> + if (!name || !gimple_range_ssa_p (name))
> + continue;
> + tree type = TREE_TYPE (name);
> + if (!Value_Range::supports_type_p (type))
> + continue;
> + Value_Range assume_range (type);
> + if (r2->assume_range_p (assume_range, name))
> + {
> + if (dump_file)
> + {
> + fprintf (dump_file, "for an assume function, ");
> + print_generic_expr (dump_file, name, TDF_SLIM);
> + fprintf (dump_file, " would have a range of ");
> + assume_range.dump (dump_file);
> + fputc ('\n', dump_file);
> + }
> + }
> + }
> + delete r2;
I have expected (but tell me if that isn't possible) this could be something
done in the new pass_assumptions::execute () rather than vrp and you'd
create the assume_query there (i.e. just for assume_functions) and then
query it solely for ssa_default_def of the parameters and save in
SSA_NAME_RANGE_INFO.
But my knowledge about ranger is fairly limited...
Jakub
On 10/19/22 12:06, Jakub Jelinek wrote:
> Hi!
>
> On Tue, Oct 18, 2022 at 05:31:58PM -0400, Andrew MacLeod wrote:
>> Anyway, gives you something to experiement with. If you would find a
>> different interface useful, let me know, or if there are limitations or
>> other expansions we might need. This seems like something reasonable for
>> you to start working with?
> Thanks for working on this.
>
>> + // Look for ASSUME calls, and call query_assume_call for each argument
>> + // to determine if there is any inferred range to be had.
>> + if (is_a<gcall *> (s) && gimple_call_internal_p (s)
>> + && gimple_call_internal_fn (s) == IFN_ASSUME)
>> + {
>> + tree assume_id = gimple_call_arg (s, 0);
>> + for (unsigned i = 1; i < gimple_call_num_args (s); i++)
>> + {
>> + tree op = gimple_call_arg (s, i);
>> + tree type = TREE_TYPE (op);
>> + if (gimple_range_ssa_p (op) && Value_Range::supports_type_p (type))
>> + {
>> + Value_Range assume_range (type);
>> + if (query_assume_call (assume_range, assume_id, op))
>> + {
>> + add_range (op, assume_range);
>> + if (dump_file)
>> + {
>> + print_generic_expr (dump_file, assume_id, TDF_SLIM);
>> + fprintf (dump_file, " assume inferred range of ");
>> + print_generic_expr (dump_file, op, TDF_SLIM);
>> + fprintf (dump_file, " to ");
>> + assume_range.dump (dump_file);
>> + fputc ('\n', dump_file);
>> + }
>> + }
> Not sure I understand this part. op is whatever we pass as the ith
> argument to IFN_ASSUME. I'd expect that at this point one needs to
> remap that to the (i-1)th PARM_DECL of assume_id (so e.g. when you
> have the above loop you could as well start with DECL_ARGUMENTS and move
> that to DECL_CHAIN at the end of every iteration. And then
> query ssa_default_def (DECL_STRUCT_FUNCTION (assume_id), parm)
> in each case and get global range of what that returns.
OK, this is the bit of code I dont know how to write :-)
yes, op is the name of the value within this current function, and yes,
that needs to be mapped to the argument decl in the assume function.
Then we need to query what range was given to that name during the
assume pass. when that is returned, the add_range (op, range) will
inject it as a side effect.
Can you write that loop?
>
>> + for (unsigned x= 0; x < gimple_phi_num_args (phi); x++)
> for (unsigned x = 0; ...
> ?
>
>> @@ -4345,6 +4345,30 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p)
>> scev_initialize ();
>> calculate_dominance_info (CDI_DOMINATORS);
>>
>> + assume_query *r2 = new assume_query ();
>> + for (unsigned i = 0; i < num_ssa_names; i++)
>> + {
>> + tree name = ssa_name (i);
>> + if (!name || !gimple_range_ssa_p (name))
>> + continue;
>> + tree type = TREE_TYPE (name);
>> + if (!Value_Range::supports_type_p (type))
>> + continue;
>> + Value_Range assume_range (type);
>> + if (r2->assume_range_p (assume_range, name))
>> + {
>> + if (dump_file)
>> + {
>> + fprintf (dump_file, "for an assume function, ");
>> + print_generic_expr (dump_file, name, TDF_SLIM);
>> + fprintf (dump_file, " would have a range of ");
>> + assume_range.dump (dump_file);
>> + fputc ('\n', dump_file);
>> + }
>> + }
>> + }
>> + delete r2;
> I have expected (but tell me if that isn't possible) this could be something
> done in the new pass_assumptions::execute () rather than vrp and you'd
> create the assume_query there (i.e. just for assume_functions) and then
> query it solely for ssa_default_def of the parameters and save in
> SSA_NAME_RANGE_INFO.
I just discovered the assumption pass, and I have moved it to there.
I dont know much about managing the parameters, but presumably yes, we'd
only query it for the parameters........... I was showing the query for
every name just to show what its producing.
As for storing it, SSA_NAME_RANGE_INFO is for the current function, that
woud be easy. if we store it there, how do we look up that range from
another functi\on when we have the ASSUME_ID ? That was unclear to me.,
I've attached the replacement version of 0003* which uses the assume
pass, and writes the global values out. It still loops over all names
at the moment
Andrew
>
On 10/19/22 12:06, Jakub Jelinek wrote:
>
> I have expected (but tell me if that isn't possible) this could be something
> done in the new pass_assumptions::execute () rather than vrp and you'd
> create the assume_query there (i.e. just for assume_functions) and then
> query it solely for ssa_default_def of the parameters and save in
> SSA_NAME_RANGE_INFO.
>
Oh, how do I know I'm processing an assume function? presumable we
could gate the pass on that so its not even invoked on non-assume functions?
Andrew
On Wed, Oct 19, 2022 at 12:55:12PM -0400, Andrew MacLeod via Gcc-patches wrote:
> > Not sure I understand this part. op is whatever we pass as the ith
> > argument to IFN_ASSUME. I'd expect that at this point one needs to
> > remap that to the (i-1)th PARM_DECL of assume_id (so e.g. when you
> > have the above loop you could as well start with DECL_ARGUMENTS and move
> > that to DECL_CHAIN at the end of every iteration. And then
> > query ssa_default_def (DECL_STRUCT_FUNCTION (assume_id), parm)
> > in each case and get global range of what that returns.
>
> OK, this is the bit of code I dont know how to write :-)
>
> yes, op is the name of the value within this current function, and yes, that
> needs to be mapped to the argument decl in the assume function. Then we
> need to query what range was given to that name during the assume pass.
> when that is returned, the add_range (op, range) will inject it as a side
> effect.
>
> Can you write that loop?
I meant something like (untested code):
&& gimple_call_internal_fn (s) == IFN_ASSUME)
{
tree assume_id = gimple_call_arg (s, 0);
- for (unsigned i = 1; i < gimple_call_num_args (s); i++)
+ tree parm = DECL_ARGUMENTS (assume_id);
+ struct function *fun = DECL_STRUCT_FUNCTION (assume_id);
+ for (unsigned i = 1;
+ i < gimple_call_num_args (s) && parm;
+ i++, parm = DECL_CHAIN (parm))
{
tree op = gimple_call_arg (s, i);
tree type = TREE_TYPE (op);
+ tree arg = ssa_default_def (fun, parm);
+ if (arg == NULL_TREE)
+ continue;
if (gimple_range_ssa_p (op) && Value_Range::supports_type_p (type))
{
Value_Range assume_range (type);
and querying SSA_NAME_RANGE_INFO of arg rather than op.
>
> >
> > > + for (unsigned x= 0; x < gimple_phi_num_args (phi); x++)
> > for (unsigned x = 0; ...
> > ?
> >
> > > @@ -4345,6 +4345,30 @@ execute_ranger_vrp (struct function *fun, bool warn_array_bounds_p)
> > > scev_initialize ();
> > > calculate_dominance_info (CDI_DOMINATORS);
> > > + assume_query *r2 = new assume_query ();
> > > + for (unsigned i = 0; i < num_ssa_names; i++)
> > > + {
> > > + tree name = ssa_name (i);
> > > + if (!name || !gimple_range_ssa_p (name))
> > > + continue;
> > > + tree type = TREE_TYPE (name);
> > > + if (!Value_Range::supports_type_p (type))
> > > + continue;
> > > + Value_Range assume_range (type);
> > > + if (r2->assume_range_p (assume_range, name))
> > > + {
> > > + if (dump_file)
> > > + {
> > > + fprintf (dump_file, "for an assume function, ");
> > > + print_generic_expr (dump_file, name, TDF_SLIM);
> > > + fprintf (dump_file, " would have a range of ");
> > > + assume_range.dump (dump_file);
> > > + fputc ('\n', dump_file);
> > > + }
> > > + }
> > > + }
> > > + delete r2;
> > I have expected (but tell me if that isn't possible) this could be something
> > done in the new pass_assumptions::execute () rather than vrp and you'd
> > create the assume_query there (i.e. just for assume_functions) and then
> > query it solely for ssa_default_def of the parameters and save in
> > SSA_NAME_RANGE_INFO.
>
> I just discovered the assumption pass, and I have moved it to there.
>
> I dont know much about managing the parameters, but presumably yes, we'd
> only query it for the parameters........... I was showing the query for
> every name just to show what its producing.
Inside of the assume function (cfun->assume_function being true)
one could again walk DECL_ARGUMENTS and for arguments with types
which ranger is able to cope with and they are reg types,
ssa_default_def (cfun, parm) to get the SSA_NAME of the default def (if
any).
> --- a/gcc/tree-vrp.cc
> +++ b/gcc/tree-vrp.cc
> @@ -4465,6 +4465,35 @@ public:
> bool gate (function *fun) final override { return fun->assume_function; }
Regarding your second mail, see above gate, this pass is only run for
assume functions and nothing else.
> unsigned int execute (function *) final override
> {
> + assume_query query;
> + if (dump_file)
> + fprintf (dump_file, "Assumptions :\n--------------\n");
> + for (unsigned i = 0; i < num_ssa_names; i++)
> + {
> + tree name = ssa_name (i);
> + if (!name || !gimple_range_ssa_p (name))
> + continue;
> + tree type = TREE_TYPE (name);
> + if (!Value_Range::supports_type_p (type))
> + continue;
> + Value_Range assume_range (type);
> + if (query.assume_range_p (assume_range, name))
> + {
> + set_range_info (name, assume_range);
> + if (dump_file)
> + {
> + print_generic_expr (dump_file, name, TDF_SLIM);
> + fprintf (dump_file, " -> ");
> + assume_range.dump (dump_file);
> + fputc ('\n', dump_file);
> + }
> + }
> + }
> + if (dump_file)
> + {
> + fputc ('\n', dump_file);
> + gimple_dump_cfg (dump_file, dump_flags);
> + }
> return TODO_discard_function;
> }
Jakub
On Wed, Oct 19, 2022 at 07:39:05PM +0200, Jakub Jelinek via Gcc-patches wrote:
> On Wed, Oct 19, 2022 at 12:55:12PM -0400, Andrew MacLeod via Gcc-patches wrote:
> > > Not sure I understand this part. op is whatever we pass as the ith
> > > argument to IFN_ASSUME. I'd expect that at this point one needs to
> > > remap that to the (i-1)th PARM_DECL of assume_id (so e.g. when you
> > > have the above loop you could as well start with DECL_ARGUMENTS and move
> > > that to DECL_CHAIN at the end of every iteration. And then
> > > query ssa_default_def (DECL_STRUCT_FUNCTION (assume_id), parm)
> > > in each case and get global range of what that returns.
> >
> > OK, this is the bit of code I dont know how to write :-)
> >
> > yes, op is the name of the value within this current function, and yes, that
> > needs to be mapped to the argument decl in the assume function. Then we
> > need to query what range was given to that name during the assume pass.
> > when that is returned, the add_range (op, range) will inject it as a side
> > effect.
> >
> > Can you write that loop?
>
> I meant something like (untested code):
> && gimple_call_internal_fn (s) == IFN_ASSUME)
> {
> tree assume_id = gimple_call_arg (s, 0);
> - for (unsigned i = 1; i < gimple_call_num_args (s); i++)
> + tree parm = DECL_ARGUMENTS (assume_id);
> + struct function *fun = DECL_STRUCT_FUNCTION (assume_id);
> + for (unsigned i = 1;
> + i < gimple_call_num_args (s) && parm;
> + i++, parm = DECL_CHAIN (parm))
> {
> tree op = gimple_call_arg (s, i);
> tree type = TREE_TYPE (op);
> + tree arg = ssa_default_def (fun, parm);
> + if (arg == NULL_TREE)
> + continue;
> if (gimple_range_ssa_p (op) && Value_Range::supports_type_p (type))
> {
> Value_Range assume_range (type);
> and querying SSA_NAME_RANGE_INFO of arg rather than op.
Oops, the tree arg = ... stuff would need to be moved into the if (gimple_range...)
body, it won't work for aggregate PARM_DECLs etc., only PARM_DECLs with
is_gimple_reg_type (TREE_TYPE (arg)).
Jakub
On 10/19/22 13:41, Jakub Jelinek wrote:
> On Wed, Oct 19, 2022 at 07:39:05PM +0200, Jakub Jelinek via Gcc-patches wrote:
>> On Wed, Oct 19, 2022 at 12:55:12PM -0400, Andrew MacLeod via Gcc-patches wrote:
>>>> Not sure I understand this part. op is whatever we pass as the ith
>>>> argument to IFN_ASSUME. I'd expect that at this point one needs to
>>>> remap that to the (i-1)th PARM_DECL of assume_id (so e.g. when you
>>>> have the above loop you could as well start with DECL_ARGUMENTS and move
>>>> that to DECL_CHAIN at the end of every iteration. And then
>>>> query ssa_default_def (DECL_STRUCT_FUNCTION (assume_id), parm)
>>>> in each case and get global range of what that returns.
>>> OK, this is the bit of code I dont know how to write :-)
>>>
>>> yes, op is the name of the value within this current function, and yes, that
>>> needs to be mapped to the argument decl in the assume function. Then we
>>> need to query what range was given to that name during the assume pass.
>>> when that is returned, the add_range (op, range) will inject it as a side
>>> effect.
>>>
>>> Can you write that loop?
>> I meant something like (untested code):
>> && gimple_call_internal_fn (s) == IFN_ASSUME)
>> {
>> tree assume_id = gimple_call_arg (s, 0);
>> - for (unsigned i = 1; i < gimple_call_num_args (s); i++)
>> + tree parm = DECL_ARGUMENTS (assume_id);
>> + struct function *fun = DECL_STRUCT_FUNCTION (assume_id);
>> + for (unsigned i = 1;
>> + i < gimple_call_num_args (s) && parm;
>> + i++, parm = DECL_CHAIN (parm))
>> {
>> tree op = gimple_call_arg (s, i);
>> tree type = TREE_TYPE (op);
>> + tree arg = ssa_default_def (fun, parm);
>> + if (arg == NULL_TREE)
>> + continue;
>> if (gimple_range_ssa_p (op) && Value_Range::supports_type_p (type))
>> {
>> Value_Range assume_range (type);
>> and querying SSA_NAME_RANGE_INFO of arg rather than op.
Thanks, I had actually come to most of those conclusions already, except
for the DECL_STRUCT_FUNCTION bit.. I was stuck on that :-)
So the SSA_NAMEs for default_defs are never disposed of? so I can query
them even though they are not in the current function decl? huh. I did
not know that.
OK, attached is the latest set of patches. not bootstrapped or
anything, but very interesting.
from.assumption pass:
;; Function _Z3bari._assume.0 (_Z3bari._assume.0, funcdef_no=5,
decl_uid=2184, cgraph_uid=7, symbol_order=7)
Assumptions :
--------------
x_1(D) -> [irange] int [42, 42] NONZERO 0x2a
__attribute__((no_icf, noclone, noinline, noipa))
bool _Z3bari._assume.0 (int x)
{
bool _2;
;; basic block 2, loop depth 0
;; pred: ENTRY
_2 = x_1(D) == 42;
return _2;
;; succ: EXIT
}
and in the VRP2 pass, I see:
_Z3bari._assume.0 assume inferred range of x_1(D) (param x) = [irange]
int [42, 42] NONZERO 0x2a
int bar (int x)
{
<bb 2> [local count: 1073741824]:
.ASSUME (_Z3bari._assume.0, x_1(D));
return 42;
}
Huh. lookit that.....
Anyway, let me clean this up a bit more, but so far so good.
I'll try bootstrapping and such
Andrew
@@ -406,3 +406,8 @@ DEFGSCODE(GIMPLE_PREDICT, "gimple_predic
This tuple should not exist outside of the gimplifier proper. */
DEFGSCODE(GIMPLE_WITH_CLEANUP_EXPR, "gimple_with_cleanup_expr", GSS_WCE)
+
+/* GIMPLE_ASSUME <GUARD, BODY> represents [[assume(cond)]].
+ BODY is the GIMPLE_BIND with the condition which sets GUARD to true
+ (otherwise UB). */
+DEFGSCODE(GIMPLE_ASSUME, "gimple_assume", GSS_ASSUME)
@@ -825,6 +825,20 @@ struct GTY((tag("GSS_OMP_ATOMIC_STORE_LA
stmt->code == GIMPLE_OMP_RETURN. */
};
+/* Assumptions. */
+
+struct GTY((tag("GSS_ASSUME")))
+ gimple_statement_assume : public gimple
+{
+ /* [ WORD 1-6 ] : base class */
+
+ /* [ WORD 7 ] */
+ tree guard;
+
+ /* [ WORD 8 ] */
+ gimple_seq body;
+};
+
/* GIMPLE_TRANSACTION. */
/* Bits to be stored in the GIMPLE_TRANSACTION subcode. */
@@ -1271,6 +1285,14 @@ is_a_helper <const gswitch *>::test (con
template <>
template <>
inline bool
+is_a_helper <gimple_statement_assume *>::test (gimple *gs)
+{
+ return gs->code == GIMPLE_ASSUME;
+}
+
+template <>
+template <>
+inline bool
is_a_helper <gtransaction *>::test (gimple *gs)
{
return gs->code == GIMPLE_TRANSACTION;
@@ -1497,6 +1519,14 @@ is_a_helper <const greturn *>::test (con
template <>
template <>
inline bool
+is_a_helper <const gimple_statement_assume *>::test (const gimple *gs)
+{
+ return gs->code == GIMPLE_ASSUME;
+}
+
+template <>
+template <>
+inline bool
is_a_helper <const gtransaction *>::test (const gimple *gs)
{
return gs->code == GIMPLE_TRANSACTION;
@@ -1577,6 +1607,7 @@ gomp_teams *gimple_build_omp_teams (gimp
gomp_atomic_load *gimple_build_omp_atomic_load (tree, tree,
enum omp_memory_order);
gomp_atomic_store *gimple_build_omp_atomic_store (tree, enum omp_memory_order);
+gimple *gimple_build_assume (tree, gimple_seq);
gtransaction *gimple_build_transaction (gimple_seq);
extern void gimple_seq_add_stmt (gimple_seq *, gimple *);
extern void gimple_seq_add_stmt_without_update (gimple_seq *, gimple *);
@@ -1835,6 +1866,7 @@ gimple_has_substatements (gimple *g)
{
switch (gimple_code (g))
{
+ case GIMPLE_ASSUME:
case GIMPLE_BIND:
case GIMPLE_CATCH:
case GIMPLE_EH_FILTER:
@@ -6520,6 +6552,52 @@ gimple_omp_continue_set_control_use (gom
cont_stmt->control_use = use;
}
+/* Return the guard associated with the GIMPLE_ASSUME statement GS. */
+
+static inline tree
+gimple_assume_guard (const gimple *gs)
+{
+ const gimple_statement_assume *assume_stmt
+ = as_a <const gimple_statement_assume *> (gs);
+ return assume_stmt->guard;
+}
+
+/* Set the guard associated with the GIMPLE_ASSUME statement GS. */
+
+static inline void
+gimple_assume_set_guard (gimple *gs, tree guard)
+{
+ gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
+ assume_stmt->guard = guard;
+}
+
+static inline tree *
+gimple_assume_guard_ptr (gimple *gs)
+{
+ gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
+ return &assume_stmt->guard;
+}
+
+/* Return the address of the GIMPLE sequence contained in the GIMPLE_ASSUME
+ statement GS. */
+
+static inline gimple_seq *
+gimple_assume_body_ptr (gimple *gs)
+{
+ gimple_statement_assume *assume_stmt = as_a <gimple_statement_assume *> (gs);
+ return &assume_stmt->body;
+}
+
+/* Return the GIMPLE sequence contained in the GIMPLE_ASSUME statement GS. */
+
+static inline gimple_seq
+gimple_assume_body (const gimple *gs)
+{
+ const gimple_statement_assume *assume_stmt
+ = as_a <const gimple_statement_assume *> (gs);
+ return assume_stmt->body;
+}
+
/* Return a pointer to the body for the GIMPLE_TRANSACTION statement
TRANSACTION_STMT. */
@@ -50,4 +50,5 @@ DEFGSSTRUCT(GSS_OMP_SINGLE_LAYOUT, gimpl
DEFGSSTRUCT(GSS_OMP_CONTINUE, gomp_continue, false)
DEFGSSTRUCT(GSS_OMP_ATOMIC_LOAD, gomp_atomic_load, false)
DEFGSSTRUCT(GSS_OMP_ATOMIC_STORE_LAYOUT, gomp_atomic_store, false)
+DEFGSSTRUCT(GSS_ASSUME, gimple_statement_assume, false)
DEFGSSTRUCT(GSS_TRANSACTION, gtransaction, false)
@@ -1290,6 +1290,18 @@ gimple_build_omp_atomic_store (tree val,
return p;
}
+/* Build a GIMPLE_ASSUME statement. */
+
+gimple *
+gimple_build_assume (tree guard, gimple_seq body)
+{
+ gimple_statement_assume *p
+ = as_a <gimple_statement_assume *> (gimple_alloc (GIMPLE_ASSUME, 0));
+ gimple_assume_set_guard (p, guard);
+ *gimple_assume_body_ptr (p) = body;
+ return p;
+}
+
/* Build a GIMPLE_TRANSACTION statement. */
gtransaction *
@@ -2135,6 +2147,13 @@ gimple_copy (gimple *stmt)
gimple_omp_masked_set_clauses (copy, t);
goto copy_omp_body;
+ case GIMPLE_ASSUME:
+ new_seq = gimple_seq_copy (gimple_assume_body (stmt));
+ *gimple_assume_body_ptr (copy) = new_seq;
+ gimple_assume_set_guard (copy,
+ unshare_expr (gimple_assume_guard (stmt)));
+ break;
+
case GIMPLE_TRANSACTION:
new_seq = gimple_seq_copy (gimple_transaction_body (
as_a <gtransaction *> (stmt)));
@@ -2052,6 +2052,31 @@ dump_gimple_omp_return (pretty_printer *
}
}
+/* Dump a GIMPLE_ASSUME tuple on the pretty_printer BUFFER. */
+
+static void
+dump_gimple_assume (pretty_printer *buffer, const gimple *gs,
+ int spc, dump_flags_t flags)
+{
+ if (flags & TDF_RAW)
+ dump_gimple_fmt (buffer, spc, flags,
+ "%G [GUARD=%T] <%+BODY <%S> >",
+ gs, gimple_assume_guard (gs),
+ gimple_assume_body (gs));
+ else
+ {
+ pp_string (buffer, "[[assume (");
+ dump_generic_node (buffer, gimple_assume_guard (gs), spc, flags, false);
+ pp_string (buffer, ")]]");
+ newline_and_indent (buffer, spc + 2);
+ pp_left_brace (buffer);
+ pp_newline (buffer);
+ dump_gimple_seq (buffer, gimple_assume_body (gs), spc + 4, flags);
+ newline_and_indent (buffer, spc + 2);
+ pp_right_brace (buffer);
+ }
+}
+
/* Dump a GIMPLE_TRANSACTION tuple on the pretty_printer BUFFER. */
static void
@@ -2841,6 +2866,10 @@ pp_gimple_stmt_1 (pretty_printer *buffer
pp_string (buffer, " predictor.");
break;
+ case GIMPLE_ASSUME:
+ dump_gimple_assume (buffer, gs, spc, flags);
+ break;
+
case GIMPLE_TRANSACTION:
dump_gimple_transaction (buffer, as_a <const gtransaction *> (gs), spc,
flags);
@@ -485,6 +485,12 @@ walk_gimple_op (gimple *stmt, walk_tree_
}
break;
+ case GIMPLE_ASSUME:
+ ret = walk_tree (gimple_assume_guard_ptr (stmt), callback_op, wi, pset);
+ if (ret)
+ return ret;
+ break;
+
case GIMPLE_TRANSACTION:
{
gtransaction *txn = as_a <gtransaction *> (stmt);
@@ -706,6 +712,13 @@ walk_gimple_stmt (gimple_stmt_iterator *
if (ret)
return wi->callback_result;
break;
+
+ case GIMPLE_ASSUME:
+ ret = walk_gimple_seq_mod (gimple_assume_body_ptr (stmt),
+ callback_stmt, callback_op, wi);
+ if (ret)
+ return wi->callback_result;
+ break;
case GIMPLE_TRANSACTION:
ret = walk_gimple_seq_mod (gimple_transaction_body_ptr (
@@ -202,6 +202,7 @@ static bool omp_maybe_offloaded_ctx (omp
case GIMPLE_TRY: \
case GIMPLE_CATCH: \
case GIMPLE_EH_FILTER: \
+ case GIMPLE_ASSUME: \
case GIMPLE_TRANSACTION: \
/* The sub-statements for these should be walked. */ \
*handled_ops_p = false; \
@@ -14413,6 +14414,9 @@ lower_omp_1 (gimple_stmt_iterator *gsi_p
lower_omp (gimple_try_eval_ptr (stmt), ctx);
lower_omp (gimple_try_cleanup_ptr (stmt), ctx);
break;
+ case GIMPLE_ASSUME:
+ lower_omp (gimple_assume_body_ptr (stmt), ctx);
+ break;
case GIMPLE_TRANSACTION:
lower_omp (gimple_transaction_body_ptr (as_a <gtransaction *> (stmt)),
ctx);
@@ -189,6 +189,7 @@ adjust_region_code_walk_stmt_fn (gimple_
case GIMPLE_GOTO:
case GIMPLE_SWITCH:
case GIMPLE_ASM:
+ case GIMPLE_ASSUME:
case GIMPLE_TRANSACTION:
case GIMPLE_RETURN:
/* Statement that might constitute some looping/control flow pattern. */
@@ -5139,6 +5139,9 @@ verify_gimple_stmt (gimple *stmt)
how to setup the parallel iteration. */
return false;
+ case GIMPLE_ASSUME:
+ return false;
+
case GIMPLE_DEBUG:
return verify_gimple_debug (stmt);
@@ -5252,6 +5255,10 @@ verify_gimple_in_seq_2 (gimple_seq stmts
as_a <gcatch *> (stmt)));
break;
+ case GIMPLE_ASSUME:
+ err |= verify_gimple_in_seq_2 (gimple_assume_body (stmt));
+ break;
+
case GIMPLE_TRANSACTION:
err |= verify_gimple_transaction (as_a <gtransaction *> (stmt));
break;
@@ -438,6 +438,10 @@ struct GTY(()) function {
/* Set if there are any OMP_TARGET regions in the function. */
unsigned int has_omp_target : 1;
+
+ /* Set for artificial function created for [[assume (cond)]].
+ These should be GIMPLE optimized, but not expanded to RTL. */
+ unsigned int assume_function : 1;
};
/* Add the decl D to the local_decls list of FUN. */
@@ -3569,7 +3569,33 @@ gimplify_call_expr (tree *expr_p, gimple
fndecl, 0));
return GS_OK;
}
- /* FIXME: Otherwise expand it specially. */
+ /* If not optimizing, ignore the assumptions. */
+ if (!optimize)
+ {
+ *expr_p = NULL_TREE;
+ return GS_ALL_DONE;
+ }
+ /* Temporarily, until gimple lowering, transform
+ .ASSUME (cond);
+ into:
+ [[assume (guard)]]
+ {
+ guard = cond;
+ }
+ such that gimple lowering can outline the condition into
+ a separate function easily. */
+ tree guard = create_tmp_var (boolean_type_node);
+ *expr_p = build2 (MODIFY_EXPR, void_type_node, guard,
+ CALL_EXPR_ARG (*expr_p, 0));
+ *expr_p = build3 (BIND_EXPR, void_type_node, NULL, *expr_p, NULL);
+ push_gimplify_context ();
+ gimple_seq body = NULL;
+ gimple *g = gimplify_and_return_first (*expr_p, &body);
+ pop_gimplify_context (g);
+ g = gimple_build_assume (guard, body);
+ gimple_set_location (g, loc);
+ gimplify_seq_add_stmt (pre_p, g);
+ *expr_p = NULL_TREE;
return GS_ALL_DONE;
}
@@ -33,6 +33,14 @@ along with GCC; see the file COPYING3.
#include "predict.h"
#include "gimple-predict.h"
#include "gimple-fold.h"
+#include "cgraph.h"
+#include "tree-ssa.h"
+#include "value-range.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-inline.h"
+#include "gimple-walk.h"
+#include "attribs.h"
/* The differences between High GIMPLE and Low GIMPLE are the
following:
@@ -237,6 +245,389 @@ lower_omp_directive (gimple_stmt_iterato
gsi_next (gsi);
}
+/* Create an artificial FUNCTION_DECL for assumption at LOC. */
+
+static tree
+create_assumption_fn (location_t loc)
+{
+ tree name = clone_function_name_numbered (current_function_decl, "_assume");
+ /* Temporarily, until we determine all the arguments. */
+ tree type = build_varargs_function_type_list (boolean_type_node, NULL_TREE);
+ tree decl = build_decl (loc, FUNCTION_DECL, name, type);
+ TREE_STATIC (decl) = 1;
+ TREE_USED (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ DECL_NAMELESS (decl) = 1;
+ TREE_PUBLIC (decl) = 0;
+ DECL_UNINLINABLE (decl) = 1;
+ DECL_EXTERNAL (decl) = 0;
+ DECL_CONTEXT (decl) = NULL_TREE;
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ tree attributes = DECL_ATTRIBUTES (current_function_decl);
+ if (lookup_attribute ("noipa", attributes) == NULL)
+ {
+ attributes = tree_cons (get_identifier ("noipa"), NULL, attributes);
+ if (lookup_attribute ("noinline", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
+ if (lookup_attribute ("noclone", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("noclone"), NULL, attributes);
+ if (lookup_attribute ("no_icf", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("no_icf"), NULL, attributes);
+ }
+ DECL_ATTRIBUTES (decl) = attributes;
+ BLOCK_SUPERCONTEXT (DECL_INITIAL (decl)) = decl;
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl)
+ = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (current_function_decl);
+ DECL_FUNCTION_SPECIFIC_TARGET (decl)
+ = DECL_FUNCTION_SPECIFIC_TARGET (current_function_decl);
+ tree t = build_decl (DECL_SOURCE_LOCATION (decl),
+ RESULT_DECL, NULL_TREE, boolean_type_node);
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+ DECL_CONTEXT (t) = decl;
+ DECL_RESULT (decl) = t;
+ push_struct_function (decl);
+ cfun->function_end_locus = loc;
+ init_tree_ssa (cfun);
+ return decl;
+}
+
+struct lower_assumption_data
+{
+ copy_body_data id;
+ tree return_false_label;
+ tree guard_copy;
+ auto_vec<tree> decls;
+};
+
+/* Helper function for lower_assumptions. Find local vars and labels
+ in the assumption sequence and remove debug stmts. */
+
+static tree
+find_assumption_locals_r (gimple_stmt_iterator *gsi_p, bool *,
+ struct walk_stmt_info *wi)
+{
+ lower_assumption_data *data = (lower_assumption_data *) wi->info;
+ gimple *stmt = gsi_stmt (*gsi_p);
+ tree lhs = gimple_get_lhs (stmt);
+ if (lhs && TREE_CODE (lhs) == SSA_NAME)
+ {
+ gcc_assert (SSA_NAME_VAR (lhs) == NULL_TREE);
+ data->id.decl_map->put (lhs, NULL_TREE);
+ data->decls.safe_push (lhs);
+ }
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_BIND:
+ for (tree var = gimple_bind_vars (as_a <gbind *> (stmt));
+ var; var = DECL_CHAIN (var))
+ if (VAR_P (var)
+ && !DECL_EXTERNAL (var)
+ && DECL_CONTEXT (var) == data->id.src_fn)
+ {
+ data->id.decl_map->put (var, var);
+ data->decls.safe_push (var);
+ }
+ break;
+ case GIMPLE_LABEL:
+ {
+ tree label = gimple_label_label (as_a <glabel *> (stmt));
+ data->id.decl_map->put (label, label);
+ break;
+ }
+ case GIMPLE_RETURN:
+ /* If something in assumption tries to return from parent function,
+ if it would be reached in hypothetical evaluation, it would be UB,
+ so transform such returns into return false; */
+ {
+ gimple *g = gimple_build_assign (data->guard_copy, boolean_false_node);
+ gsi_insert_before (gsi_p, g, GSI_SAME_STMT);
+ gimple_return_set_retval (as_a <greturn *> (stmt), data->guard_copy);
+ break;
+ }
+ case GIMPLE_DEBUG:
+ /* As assumptions won't be emitted, debug info stmts in them
+ are useless. */
+ gsi_remove (gsi_p, true);
+ wi->removed_stmt = true;
+ break;
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Create a new PARM_DECL that is indentical in all respect to DECL except that
+ DECL can be either a VAR_DECL, a PARM_DECL or RESULT_DECL. The original
+ DECL must come from ID->src_fn and the copy will be part of ID->dst_fn. */
+
+static tree
+assumption_copy_decl (tree decl, copy_body_data *id)
+{
+ tree type = TREE_TYPE (decl);
+
+ if (is_global_var (decl))
+ return decl;
+
+ gcc_assert (VAR_P (decl)
+ || TREE_CODE (decl) == PARM_DECL
+ || TREE_CODE (decl) == RESULT_DECL);
+ tree copy = build_decl (DECL_SOURCE_LOCATION (decl),
+ PARM_DECL, DECL_NAME (decl), type);
+ if (DECL_PT_UID_SET_P (decl))
+ SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
+ TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
+ TREE_READONLY (copy) = TREE_READONLY (decl);
+ TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
+ DECL_NOT_GIMPLE_REG_P (copy) = DECL_NOT_GIMPLE_REG_P (decl);
+ DECL_BY_REFERENCE (copy) = DECL_BY_REFERENCE (decl);
+ DECL_ARG_TYPE (copy) = type;
+ ((lower_assumption_data *) id)->decls.safe_push (decl);
+ return copy_decl_for_dup_finish (id, decl, copy);
+}
+
+/* Transform gotos out of the assumption into return false. */
+
+static tree
+adjust_assumption_stmt_r (gimple_stmt_iterator *gsi_p, bool *,
+ struct walk_stmt_info *wi)
+{
+ lower_assumption_data *data = (lower_assumption_data *) wi->info;
+ gimple *stmt = gsi_stmt (*gsi_p);
+ tree lab = NULL_TREE;
+ unsigned int idx = 0;
+ if (gimple_code (stmt) == GIMPLE_GOTO)
+ lab = gimple_goto_dest (stmt);
+ else if (gimple_code (stmt) == GIMPLE_COND)
+ {
+ repeat:
+ if (idx == 0)
+ lab = gimple_cond_true_label (as_a <gcond *> (stmt));
+ else
+ lab = gimple_cond_false_label (as_a <gcond *> (stmt));
+ }
+ else if (gimple_code (stmt) == GIMPLE_LABEL)
+ {
+ tree label = gimple_label_label (as_a <glabel *> (stmt));
+ DECL_CONTEXT (label) = current_function_decl;
+ }
+ if (lab)
+ {
+ if (!data->id.decl_map->get (lab))
+ {
+ if (!data->return_false_label)
+ data->return_false_label
+ = create_artificial_label (UNKNOWN_LOCATION);
+ if (gimple_code (stmt) == GIMPLE_GOTO)
+ gimple_goto_set_dest (as_a <ggoto *> (stmt),
+ data->return_false_label);
+ else if (idx == 0)
+ gimple_cond_set_true_label (as_a <gcond *> (stmt),
+ data->return_false_label);
+ else
+ gimple_cond_set_false_label (as_a <gcond *> (stmt),
+ data->return_false_label);
+ }
+ if (gimple_code (stmt) == GIMPLE_COND && idx == 0)
+ {
+ idx = 1;
+ goto repeat;
+ }
+ }
+ return NULL_TREE;
+}
+
+/* Adjust trees in the assumption body. Called through walk_tree. */
+
+static tree
+adjust_assumption_stmt_op (tree *tp, int *, void *datap)
+{
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) datap;
+ lower_assumption_data *data = (lower_assumption_data *) wi->info;
+ tree t = *tp;
+ tree *newt;
+ switch (TREE_CODE (t))
+ {
+ case SSA_NAME:
+ newt = data->id.decl_map->get (t);
+ /* There shouldn't be SSA_NAMEs other than ones defined in the
+ assumption's body. */
+ gcc_assert (newt);
+ *tp = *newt;
+ break;
+ case LABEL_DECL:
+ newt = data->id.decl_map->get (t);
+ if (newt)
+ *tp = *newt;
+ break;
+ case VAR_DECL:
+ case PARM_DECL:
+ case RESULT_DECL:
+ *tp = remap_decl (t, &data->id);
+ break;
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Lower assumption.
+ The gimplifier transformed:
+ .ASSUME (cond);
+ into:
+ [[assume (guard)]]
+ {
+ guard = cond;
+ }
+ which we should transform into:
+ .ASSUME (&artificial_fn, args...);
+ where artificial_fn will look like:
+ bool artificial_fn (args...)
+ {
+ guard = cond;
+ return guard;
+ }
+ with any debug stmts in the block removed and jumps out of
+ the block or return stmts replaced with return false; */
+
+static void
+lower_assumption (gimple_stmt_iterator *gsi, struct lower_data *data)
+{
+ gimple *stmt = gsi_stmt (*gsi);
+ tree guard = gimple_assume_guard (stmt);
+ gimple *bind = gimple_assume_body (stmt);
+ location_t loc = gimple_location (stmt);
+ gcc_assert (gimple_code (bind) == GIMPLE_BIND);
+
+ lower_assumption_data lad;
+ hash_map<tree, tree> decl_map;
+ memset (&lad.id, 0, sizeof (lad.id));
+ lad.return_false_label = NULL_TREE;
+ lad.id.src_fn = current_function_decl;
+ lad.id.dst_fn = create_assumption_fn (loc);
+ lad.id.src_cfun = DECL_STRUCT_FUNCTION (lad.id.src_fn);
+ lad.id.decl_map = &decl_map;
+ lad.id.copy_decl = assumption_copy_decl;
+ lad.id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ lad.id.transform_parameter = true;
+ lad.id.do_not_unshare = true;
+ lad.id.do_not_fold = true;
+ cfun->curr_properties = lad.id.src_cfun->curr_properties;
+ lad.guard_copy = create_tmp_var (boolean_type_node);
+ decl_map.put (lad.guard_copy, lad.guard_copy);
+ decl_map.put (guard, lad.guard_copy);
+ cfun->assume_function = 1;
+
+ /* Find variables, labels and SSA_NAMEs local to the assume GIMPLE_BIND. */
+ gimple_stmt_iterator gsi2 = gsi_start (*gimple_assume_body_ptr (stmt));
+ struct walk_stmt_info wi;
+ memset (&wi, 0, sizeof (wi));
+ wi.info = (void *) &lad;
+ walk_gimple_stmt (&gsi2, find_assumption_locals_r, NULL, &wi);
+ unsigned int sz = lad.decls.length ();
+ for (unsigned i = 0; i < sz; ++i)
+ {
+ tree v = lad.decls[i];
+ tree newv;
+ /* SSA_NAMEs defined in the assume condition should be replaced
+ by new SSA_NAMEs in the artificial function. */
+ if (TREE_CODE (v) == SSA_NAME)
+ {
+ newv = make_ssa_name (remap_type (TREE_TYPE (v), &lad.id));
+ decl_map.put (v, newv);
+ }
+ /* Local vars should have context and type adjusted to the
+ new artificial function. */
+ else if (VAR_P (v))
+ {
+ if (is_global_var (v) && !DECL_ASSEMBLER_NAME_SET_P (v))
+ DECL_ASSEMBLER_NAME (v);
+ TREE_TYPE (v) = remap_type (TREE_TYPE (v), &lad.id);
+ DECL_CONTEXT (v) = current_function_decl;
+ }
+ }
+ /* References to other automatic vars should be replaced by
+ PARM_DECLs to the artificial function. */
+ memset (&wi, 0, sizeof (wi));
+ wi.info = (void *) &lad;
+ walk_gimple_stmt (&gsi2, adjust_assumption_stmt_r,
+ adjust_assumption_stmt_op, &wi);
+
+ /* At the start prepend guard = false; */
+ gimple_seq body = NULL;
+ gimple *g = gimple_build_assign (lad.guard_copy, boolean_false_node);
+ gimple_seq_add_stmt (&body, g);
+ gimple_seq_add_stmt (&body, bind);
+ /* At the end add return guard; */
+ greturn *gr = gimple_build_return (lad.guard_copy);
+ gimple_seq_add_stmt (&body, gr);
+ /* If there were any jumps to labels outside of the condition,
+ replace them with a jump to
+ return_false_label:
+ guard = false;
+ return guard; */
+ if (lad.return_false_label)
+ {
+ g = gimple_build_label (lad.return_false_label);
+ gimple_seq_add_stmt (&body, g);
+ g = gimple_build_assign (lad.guard_copy, boolean_false_node);
+ gimple_seq_add_stmt (&body, g);
+ gr = gimple_build_return (lad.guard_copy);
+ gimple_seq_add_stmt (&body, gr);
+ }
+ bind = gimple_build_bind (NULL_TREE, body, NULL_TREE);
+ body = NULL;
+ gimple_seq_add_stmt (&body, bind);
+ gimple_set_body (current_function_decl, body);
+ pop_cfun ();
+
+ tree parms = NULL_TREE;
+ tree parmt = void_list_node;
+ auto_vec<tree, 8> vargs;
+ vargs.safe_grow (1 + (lad.decls.length () - sz), true);
+ /* First argument to IFN_ASSUME will be address of the
+ artificial function. */
+ vargs[0] = build_fold_addr_expr (lad.id.dst_fn);
+ for (unsigned i = lad.decls.length (); i > sz; --i)
+ {
+ tree *v = decl_map.get (lad.decls[i - 1]);
+ gcc_assert (v && TREE_CODE (*v) == PARM_DECL);
+ DECL_CHAIN (*v) = parms;
+ parms = *v;
+ parmt = tree_cons (NULL_TREE, TREE_TYPE (*v), parmt);
+ /* Remaining arguments will be the variables/parameters
+ mentioned in the condition. */
+ vargs[i - sz] = lad.decls[i - 1];
+ /* If they have gimple types, we might need to regimplify
+ them to make the IFN_ASSUME call valid. */
+ if (is_gimple_reg_type (TREE_TYPE (vargs[i - sz]))
+ && !is_gimple_val (vargs[i - sz]))
+ {
+ tree t = make_ssa_name (TREE_TYPE (vargs[i - sz]));
+ g = gimple_build_assign (t, vargs[i - sz]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ vargs[i - sz] = t;
+ }
+ }
+ DECL_ARGUMENTS (lad.id.dst_fn) = parms;
+ TREE_TYPE (lad.id.dst_fn) = build_function_type (boolean_type_node, parmt);
+
+ cgraph_node::add_new_function (lad.id.dst_fn, false);
+
+ for (unsigned i = 0; i < sz; ++i)
+ {
+ tree v = lad.decls[i];
+ if (TREE_CODE (v) == SSA_NAME)
+ release_ssa_name (v);
+ }
+
+ data->cannot_fallthru = false;
+ /* Replace GIMPLE_ASSUME statement with IFN_ASSUME call. */
+ gcall *call = gimple_build_call_internal_vec (IFN_ASSUME, vargs);
+ gimple_set_location (call, loc);
+ gsi_replace (gsi, call, true);
+}
/* Lower statement GSI. DATA is passed through the recursion. We try to
track the fallthruness of statements and get rid of unreachable return
@@ -403,6 +794,10 @@ lower_stmt (gimple_stmt_iterator *gsi, s
data->cannot_fallthru = false;
return;
+ case GIMPLE_ASSUME:
+ lower_assumption (gsi, data);
+ return;
+
case GIMPLE_TRANSACTION:
lower_sequence (gimple_transaction_body_ptr (
as_a <gtransaction *> (stmt)),
@@ -4253,6 +4253,12 @@ pass_fold_builtins::execute (function *f
}
callee = gimple_call_fndecl (stmt);
+ if (!callee
+ && gimple_call_internal_p (stmt, IFN_ASSUME))
+ {
+ gsi_remove (&i, true);
+ continue;
+ }
if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
{
gsi_next (&i);
@@ -2278,6 +2278,7 @@ output_struct_function_base (struct outp
bp_pack_value (&bp, fn->calls_eh_return, 1);
bp_pack_value (&bp, fn->has_force_vectorize_loops, 1);
bp_pack_value (&bp, fn->has_simduid_loops, 1);
+ bp_pack_value (&bp, fn->assume_function, 1);
bp_pack_value (&bp, fn->va_list_fpr_size, 8);
bp_pack_value (&bp, fn->va_list_gpr_size, 8);
bp_pack_value (&bp, fn->last_clique, sizeof (short) * 8);
@@ -1318,6 +1318,7 @@ input_struct_function_base (struct funct
fn->calls_eh_return = bp_unpack_value (&bp, 1);
fn->has_force_vectorize_loops = bp_unpack_value (&bp, 1);
fn->has_simduid_loops = bp_unpack_value (&bp, 1);
+ fn->assume_function = bp_unpack_value (&bp, 1);
fn->va_list_fpr_size = bp_unpack_value (&bp, 8);
fn->va_list_gpr_size = bp_unpack_value (&bp, 8);
fn->last_clique = bp_unpack_value (&bp, sizeof (short) * 8);
@@ -1882,6 +1882,16 @@ cgraph_node::expand (void)
ggc_collect ();
timevar_pop (TV_REST_OF_COMPILATION);
+ if (DECL_STRUCT_FUNCTION (decl)
+ && DECL_STRUCT_FUNCTION (decl)->assume_function)
+ {
+ /* Assume functions aren't expanded into RTL, on the other side
+ we don't want to release their body. */
+ if (cfun)
+ pop_cfun ();
+ return;
+ }
+
/* Make sure that BE didn't give up on compiling. */
gcc_assert (TREE_ASM_WRITTEN (decl));
if (cfun)
@@ -2373,6 +2383,10 @@ symbol_table::compile (void)
if (node->inlined_to
|| gimple_has_body_p (node->decl))
{
+ if (DECL_STRUCT_FUNCTION (node->decl)
+ && (DECL_STRUCT_FUNCTION (node->decl)->curr_properties
+ & PROP_assumptions_done) != 0)
+ continue;
error_found = true;
node->debug ();
}
@@ -4526,5 +4526,4 @@ expand_TRAP (internal_fn, gcall *)
void
expand_ASSUME (internal_fn, gcall *)
{
- gcc_unreachable ();
}
@@ -2660,6 +2660,15 @@ execute_one_pass (opt_pass *pass)
if (dom_info_available_p (CDI_POST_DOMINATORS))
free_dominance_info (CDI_POST_DOMINATORS);
+ if (cfun->assume_function)
+ {
+ /* For assume functions, don't release body, keep it around. */
+ cfun->curr_properties |= PROP_assumptions_done;
+ pop_cfun ();
+ current_pass = NULL;
+ return true;
+ }
+
tree fn = cfun->decl;
pop_cfun ();
gcc_assert (!cfun);
@@ -3751,7 +3751,9 @@ cgraph_node::verify_node (void)
&& (!DECL_EXTERNAL (decl) || inlined_to)
&& !flag_wpa)
{
- if (this_cfun->cfg)
+ if ((this_cfun->curr_properties & PROP_assumptions_done) != 0)
+ ;
+ else if (this_cfun->cfg)
{
hash_set<gimple *> stmts;
@@ -227,6 +227,8 @@ protected:
#define PROP_rtl_split_insns (1 << 17) /* RTL has insns split. */
#define PROP_loop_opts_done (1 << 18) /* SSA loop optimizations
have completed. */
+#define PROP_assumptions_done (1 << 19) /* Assume function kept
+ around. */
#define PROP_gimple \
(PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
@@ -301,7 +303,8 @@ protected:
/* Rebuild the callgraph edges. */
#define TODO_rebuild_cgraph_edges (1 << 22)
-/* Release function body and stop pass manager. */
+/* Release function body (unless assumption function)
+ and stop pass manager. */
#define TODO_discard_function (1 << 23)
/* Internally used in execute_function_todo(). */
@@ -465,6 +468,7 @@ extern gimple_opt_pass *make_pass_copy_p
extern gimple_opt_pass *make_pass_isolate_erroneous_paths (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_vrp (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_vrp (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_assumptions (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_uncprop (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_return_slot (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_reassoc (gcc::context *ctxt);
@@ -407,6 +407,7 @@ along with GCC; see the file COPYING3.
and thus it should be run last. */
NEXT_PASS (pass_uncprop);
POP_INSERT_PASSES ()
+ NEXT_PASS (pass_assumptions);
NEXT_PASS (pass_tm_init);
PUSH_INSERT_PASSES_WITHIN (pass_tm_init)
NEXT_PASS (pass_tm_mark);
@@ -226,6 +226,7 @@ DEFTIMEVAR (TV_TREE_WIDEN_MUL , "
DEFTIMEVAR (TV_TRANS_MEM , "transactional memory")
DEFTIMEVAR (TV_TREE_STRLEN , "tree strlen optimization")
DEFTIMEVAR (TV_TREE_MODREF , "tree modref")
+DEFTIMEVAR (TV_TREE_ASSUMPTIONS , "tree assumptions")
DEFTIMEVAR (TV_CGRAPH_VERIFY , "callgraph verifier")
DEFTIMEVAR (TV_DOM_FRONTIERS , "dominance frontiers")
DEFTIMEVAR (TV_DOMINANCE , "dominance computation")
@@ -1736,6 +1736,11 @@ remap_gimple_stmt (gimple *stmt, copy_bo
(as_a <gomp_critical *> (stmt)));
break;
+ case GIMPLE_ASSUME:
+ s1 = remap_gimple_seq (gimple_assume_body (stmt), id);
+ copy = gimple_build_assume (gimple_assume_guard (stmt), s1);
+ break;
+
case GIMPLE_TRANSACTION:
{
gtransaction *old_trans_stmt = as_a <gtransaction *> (stmt);
@@ -4441,6 +4441,35 @@ public:
int my_pass;
}; // class pass_vrp
+const pass_data pass_data_assumptions =
+{
+ GIMPLE_PASS, /* type */
+ "assumptions", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_TREE_ASSUMPTIONS, /* tv_id */
+ PROP_ssa, /* properties_required */
+ PROP_assumptions_done, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_end */
+};
+
+class pass_assumptions : public gimple_opt_pass
+{
+public:
+ pass_assumptions (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_assumptions, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate (function *fun) final override { return fun->assume_function; }
+ unsigned int execute (function *) final override
+ {
+ return TODO_discard_function;
+ }
+
+}; // class pass_assumptions
+
} // anon namespace
gimple_opt_pass *
@@ -4454,3 +4483,9 @@ make_pass_early_vrp (gcc::context *ctxt)
{
return new pass_vrp (ctxt, pass_data_early_vrp);
}
+
+gimple_opt_pass *
+make_pass_assumptions (gcc::context *ctx)
+{
+ return new pass_assumptions (ctx);
+}
@@ -8280,6 +8280,7 @@ extern tree predeclare_vla (tree);
extern void clear_fold_cache (void);
extern tree lookup_hotness_attribute (tree);
extern tree process_stmt_hotness_attribute (tree, location_t);
+extern tree build_assume_call (location_t, tree);
extern tree process_stmt_assume_attribute (tree, tree, location_t);
extern bool simple_empty_class_p (tree, tree, tree_code);
extern tree fold_builtin_source_location (location_t);
@@ -46012,11 +46012,7 @@ cp_parser_omp_assumption_clauses (cp_par
if (!type_dependent_expression_p (t))
t = contextual_conv_bool (t, tf_warning_or_error);
if (is_assume && !error_operand_p (t))
- {
- t = build_call_expr_internal_loc (eloc, IFN_ASSUME,
- void_type_node, 1, t);
- finish_expr_stmt (t);
- }
+ finish_expr_stmt (build_assume_call (eloc, t));
if (!parens.require_close (parser))
cp_parser_skip_to_closing_parenthesis (parser,
/*recovering=*/true,
@@ -3101,6 +3101,17 @@ process_stmt_hotness_attribute (tree std
return std_attrs;
}
+/* Build IFN_ASSUME internal call for assume condition ARG. */
+
+tree
+build_assume_call (location_t loc, tree arg)
+{
+ if (!processing_template_decl)
+ arg = fold_build_cleanup_point_expr (TREE_TYPE (arg), arg);
+ return build_call_expr_internal_loc (loc, IFN_ASSUME, void_type_node,
+ 1, arg);
+}
+
/* If [[assume (cond)]] appears on this statement, handle it. */
tree
@@ -3137,9 +3148,7 @@ process_stmt_assume_attribute (tree std_
arg = contextual_conv_bool (arg, tf_warning_or_error);
if (error_operand_p (arg))
continue;
- statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
- void_type_node, 1, arg);
- finish_expr_stmt (statement);
+ finish_expr_stmt (build_assume_call (attrs_loc, arg));
}
}
return remove_attribute ("gnu", "assume", std_attrs);
@@ -21140,10 +21140,7 @@ tsubst_copy_and_build (tree t,
ret = error_mark_node;
break;
}
- ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
- IFN_ASSUME,
- void_type_node, 1,
- arg);
+ ret = build_assume_call (EXPR_LOCATION (t), arg);
RETURN (ret);
}
break;
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume1.C"
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume3.C"
@@ -0,0 +1,56 @@
+// P1774R8 - Portable assumptions
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2" }
+
+int
+foo (int x)
+{
+ [[assume (x == 42)]];
+ return x;
+}
+
+int
+bar (int x)
+{
+ [[assume (++x == 43)]];
+ return x;
+}
+
+int
+baz (int x)
+{
+ [[assume (({ int z = ++x; static int w; ++w; if (z == 51) return -1; if (z == 53) goto lab1; if (z == 64) throw 1; z == 43; }))]];
+lab1:
+ return x;
+}
+
+struct S { S (); S (const S &); ~S (); int a, b; int foo (); };
+
+int
+qux ()
+{
+ S s;
+ [[assume (s.a == 42 && s.b == 43)]];
+ return s.a + s.b;
+}
+
+int
+S::foo ()
+{
+ [[assume (a == 42 && b == 43)]];
+ return a + b;
+}
+
+int
+corge (int x)
+{
+ [[assume (({ [[assume (x < 42)]]; x > -42; }))]];
+ return x < 42;
+}
+
+int
+garply (int x)
+{
+ [[assume (({ [[assume (++x < 43)]]; x > -42; }))]];
+ return x < 42;
+}