aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2022-10-18 10:31:20 +0200
committerJakub Jelinek <jakub@redhat.com>2022-10-18 10:39:09 +0200
commit4dda30e9910c4f04a6258b053401249e213f29be (patch)
tree4c7ad9e81387b5f56bd856017a40638c32fdb033
parent5ad3cc1ecc3c7c61d5e319f74cb7287fb80944fd (diff)
downloadgcc-4dda30e9910c4f04a6258b053401249e213f29be.zip
gcc-4dda30e9910c4f04a6258b053401249e213f29be.tar.gz
gcc-4dda30e9910c4f04a6258b053401249e213f29be.tar.bz2
middle-end IFN_ASSUME support [PR106654]
My earlier patches gimplify the simplest non-side-effects assumptions into if (cond) ; else __builtin_unreachable (); and throw the rest on the floor. The following patch attempts to do something with the rest too. For -O0, it throws the more complex assumptions on the floor, we don't expect optimizations and the assumptions are there to allow optimizations. Otherwise arranges for the assumptions to be visible in the IL as .ASSUME (_Z2f4i._assume.0, i_1(D)); call where there is an artificial function like: bool _Z2f4i._assume.0 (int i) { bool _2; <bb 2> [local count: 1073741824]: _2 = i_1(D) == 43; return _2; } with the semantics that there is UB unless the assumption function would return true. Aldy, could ranger handle this? If it sees .ASSUME call, walk the body of such function from the edge(s) to exit with the assumption that the function returns true, so above set _2 [true, true] and from there derive that i_1(D) [43, 43] and then map the argument in the assumption function to argument passed to IFN_ASSUME (note, args there are shifted by 1)? During gimplification it actually gimplifies it into [[assume (D.2591)]] { { i = i + 1; D.2591 = i == 44; } } which is a new GIMPLE_ASSUME statement wrapping a GIMPLE_BIND and specifying a boolean_type_node variable which contains the result. The GIMPLE_ASSUME then survives just a couple of passes and is lowered during gimple lowering into an outlined separate function and IFN_ASSUME call. Variables declared inside of the condition (both static and automatic) just change context, automatic variables from the caller are turned into parameters (note, as the code is never executed, I handle this way even non-POD types, we don't need to bother pretending there would be user copy constructors etc. involved). The assume_function artificial functions are then optimized until the new assumptions pass which doesn't do much right now but I'd like to see there the backwards ranger walk and filling up of SSA_NAME_RANGE_INFO for the parameters. There are a few further changes I'd like to do, like ignoring the .ASSUME calls in inlining size estimations (but haven't figured out where it is done), or for LTO arrange for the assume functions to be emitted in all partitions that reference those (usually there will be just one, unless code with the assumption got inlined, versioned etc.). 2022-10-18 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. * timevar.def (TV_TREE_ASSUMPTIONS): New. * tree-inline.cc (remap_gimple_stmt): Handle GIMPLE_ASSUME. * 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.
-rw-r--r--gcc/cgraph.cc4
-rw-r--r--gcc/cgraphunit.cc14
-rw-r--r--gcc/cp/cp-gimplify.cc15
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/parser.cc6
-rw-r--r--gcc/cp/pt.cc5
-rw-r--r--gcc/function.h4
-rw-r--r--gcc/gimple-low.cc395
-rw-r--r--gcc/gimple-pretty-print.cc29
-rw-r--r--gcc/gimple-walk.cc13
-rw-r--r--gcc/gimple.cc19
-rw-r--r--gcc/gimple.def5
-rw-r--r--gcc/gimple.h78
-rw-r--r--gcc/gimplify.cc28
-rw-r--r--gcc/gsstruct.def1
-rw-r--r--gcc/internal-fn.cc1
-rw-r--r--gcc/lto-streamer-in.cc1
-rw-r--r--gcc/lto-streamer-out.cc1
-rw-r--r--gcc/omp-low.cc4
-rw-r--r--gcc/omp-oacc-kernels-decompose.cc1
-rw-r--r--gcc/passes.cc9
-rw-r--r--gcc/passes.def1
-rw-r--r--gcc/testsuite/g++.dg/cpp23/attr-assume5.C5
-rw-r--r--gcc/testsuite/g++.dg/cpp23/attr-assume6.C5
-rw-r--r--gcc/testsuite/g++.dg/cpp23/attr-assume7.C56
-rw-r--r--gcc/timevar.def1
-rw-r--r--gcc/tree-cfg.cc7
-rw-r--r--gcc/tree-inline.cc5
-rw-r--r--gcc/tree-pass.h6
-rw-r--r--gcc/tree-ssa-ccp.cc6
-rw-r--r--gcc/tree-vrp.cc35
31 files changed, 745 insertions, 16 deletions
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index 8d6ed38..0417b05 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -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;
diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
index 7b5be0f..b05d790 100644
--- a/gcc/cgraphunit.cc
+++ b/gcc/cgraphunit.cc
@@ -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 ();
}
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 2608475..28c3398 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -3101,6 +3101,17 @@ process_stmt_hotness_attribute (tree std_attrs, location_t attrs_loc)
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_attrs, tree statement,
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);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e2607f0..60a2510 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9ddfb02..a39c5f0 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -46012,11 +46012,7 @@ cp_parser_omp_assumption_clauses (cp_parser *parser, cp_token *pragma_tok,
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,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e4dca9d..5eddad9 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -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;
diff --git a/gcc/function.h b/gcc/function.h
index 0986137..d7deaeb 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -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. */
diff --git a/gcc/gimple-low.cc b/gcc/gimple-low.cc
index 4cd27dd..512aa9f 100644
--- a/gcc/gimple-low.cc
+++ b/gcc/gimple-low.cc
@@ -33,6 +33,14 @@ along with GCC; see the file COPYING3. If not see
#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_iterator *gsi, struct lower_data *data)
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, struct lower_data *data)
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)),
diff --git a/gcc/gimple-pretty-print.cc b/gcc/gimple-pretty-print.cc
index a87e2ae..7ec079f 100644
--- a/gcc/gimple-pretty-print.cc
+++ b/gcc/gimple-pretty-print.cc
@@ -2052,6 +2052,31 @@ dump_gimple_omp_return (pretty_printer *buffer, const gimple *gs, int spc,
}
}
+/* 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, const gimple *gs, int spc,
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);
diff --git a/gcc/gimple-walk.cc b/gcc/gimple-walk.cc
index c40f280..cad36f7 100644
--- a/gcc/gimple-walk.cc
+++ b/gcc/gimple-walk.cc
@@ -485,6 +485,12 @@ walk_gimple_op (gimple *stmt, walk_tree_fn callback_op,
}
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);
@@ -707,6 +713,13 @@ walk_gimple_stmt (gimple_stmt_iterator *gsi, walk_stmt_fn callback_stmt,
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 (
as_a <gtransaction *> (stmt)),
diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index 4d45311..6c23dd7 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -1290,6 +1290,18 @@ gimple_build_omp_atomic_store (tree val, enum omp_memory_order mo)
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)));
diff --git a/gcc/gimple.def b/gcc/gimple.def
index 296c73c..7c617cd 100644
--- a/gcc/gimple.def
+++ b/gcc/gimple.def
@@ -406,3 +406,8 @@ DEFGSCODE(GIMPLE_PREDICT, "gimple_predict", GSS_BASE)
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)
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 77ac149..adbeb06 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -825,6 +825,20 @@ struct GTY((tag("GSS_OMP_ATOMIC_STORE_LAYOUT")))
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 (const gimple *gs)
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 (const gimple *gs)
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 (gimple_seq, tree);
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 (gomp_continue *cont_stmt, tree use)
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. */
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index d4209ea..42a996d 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -3569,7 +3569,33 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
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;
}
diff --git a/gcc/gsstruct.def b/gcc/gsstruct.def
index 19e1088..c3f64ef 100644
--- a/gcc/gsstruct.def
+++ b/gcc/gsstruct.def
@@ -50,4 +50,5 @@ DEFGSSTRUCT(GSS_OMP_SINGLE_LAYOUT, gimple_statement_omp_single_layout, false)
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)
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index de608bd4..9471f54 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -4526,5 +4526,4 @@ expand_TRAP (internal_fn, gcall *)
void
expand_ASSUME (internal_fn, gcall *)
{
- gcc_unreachable ();
}
diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc
index fa89634..5439651 100644
--- a/gcc/lto-streamer-in.cc
+++ b/gcc/lto-streamer-in.cc
@@ -1318,6 +1318,7 @@ input_struct_function_base (struct function *fn, class data_in *data_in,
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);
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index 2e7af03..1e38904 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -2278,6 +2278,7 @@ output_struct_function_base (struct output_block *ob, struct function *fn)
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);
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index dc42c75..a880973 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -202,6 +202,7 @@ static bool omp_maybe_offloaded_ctx (omp_context *ctx);
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, omp_context *ctx)
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);
diff --git a/gcc/omp-oacc-kernels-decompose.cc b/gcc/omp-oacc-kernels-decompose.cc
index 524060e..df333ba 100644
--- a/gcc/omp-oacc-kernels-decompose.cc
+++ b/gcc/omp-oacc-kernels-decompose.cc
@@ -189,6 +189,7 @@ adjust_region_code_walk_stmt_fn (gimple_stmt_iterator *gsi_p,
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. */
diff --git a/gcc/passes.cc b/gcc/passes.cc
index 78a07f8..3bbf525 100644
--- a/gcc/passes.cc
+++ b/gcc/passes.cc
@@ -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);
diff --git a/gcc/passes.def b/gcc/passes.def
index 939ec3e..193b579 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -407,6 +407,7 @@ along with GCC; see the file COPYING3. If not see
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);
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume5.C b/gcc/testsuite/g++.dg/cpp23/attr-assume5.C
new file mode 100644
index 0000000..fec2209
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume5.C
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume1.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume6.C b/gcc/testsuite/g++.dg/cpp23/attr-assume6.C
new file mode 100644
index 0000000..4a81df1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume6.C
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume3.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume7.C b/gcc/testsuite/g++.dg/cpp23/attr-assume7.C
new file mode 100644
index 0000000..441242c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume7.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;
+}
diff --git a/gcc/timevar.def b/gcc/timevar.def
index eac4370..63d9b00 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -226,6 +226,7 @@ DEFTIMEVAR (TV_TREE_WIDEN_MUL , "gimple widening/fma detection")
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")
diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
index ae78187..9b2c0f6 100644
--- a/gcc/tree-cfg.cc
+++ b/gcc/tree-cfg.cc
@@ -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;
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 01d4700..8091ba8 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -1736,6 +1736,11 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
(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);
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 4dfe05e..8480d41 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -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_prop (gcc::context *ctxt);
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);
diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc
index 85c0460..9778e77 100644
--- a/gcc/tree-ssa-ccp.cc
+++ b/gcc/tree-ssa-ccp.cc
@@ -4253,6 +4253,12 @@ pass_fold_builtins::execute (function *fun)
}
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);
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index 93482e5..1adb15c 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -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);
+}