]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
middle-end IFN_ASSUME support [PR106654]
authorJakub Jelinek <jakub@redhat.com>
Tue, 18 Oct 2022 08:31:20 +0000 (10:31 +0200)
committerJakub Jelinek <jakub@redhat.com>
Tue, 18 Oct 2022 08:39:09 +0000 (10:39 +0200)
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.

31 files changed:
gcc/cgraph.cc
gcc/cgraphunit.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/function.h
gcc/gimple-low.cc
gcc/gimple-pretty-print.cc
gcc/gimple-walk.cc
gcc/gimple.cc
gcc/gimple.def
gcc/gimple.h
gcc/gimplify.cc
gcc/gsstruct.def
gcc/internal-fn.cc
gcc/lto-streamer-in.cc
gcc/lto-streamer-out.cc
gcc/omp-low.cc
gcc/omp-oacc-kernels-decompose.cc
gcc/passes.cc
gcc/passes.def
gcc/testsuite/g++.dg/cpp23/attr-assume5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp23/attr-assume6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp23/attr-assume7.C [new file with mode: 0644]
gcc/timevar.def
gcc/tree-cfg.cc
gcc/tree-inline.cc
gcc/tree-pass.h
gcc/tree-ssa-ccp.cc
gcc/tree-vrp.cc

index 8d6ed38efa25de53c9b5ceb6d0a746cf943f4897..0417b059965da2405580b7044460965a8480f49e 100644 (file)
@@ -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;
 
index 7b5be0f15391c3e2a8c7579c355b661f95c6457c..b05d790bf8da82b74252b7afe876a2a7a092eebb 100644 (file)
@@ -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 ();
          }
index 2608475897cdd49703055f5de5f6fc9de40bb76e..28c339869b83d1d401d661351e18e694c1de8f4d 100644 (file)
@@ -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);
index e2607f09c1967efa7563b4a6bef438edbeab5ccf..60a251010493d483ae1ca4584f2d38601ffca197 100644 (file)
@@ -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);
index 9ddfb027ff9ced7c7651b399dfb88104b3ae8ba4..a39c5f0d24b54f0f58831bdc5b6a70675786e32d 100644 (file)
@@ -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,
index e4dca9d4f9dabeeac4cbefb8eb0112a13b8bad66..5eddad900eae1e27ebe31eaf6db7d59a6dff605e 100644 (file)
@@ -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;
index 098613766bea42d2c77b2eddd9482fd5e4e0ba71..d7deaebee9c40785eca6df2e5e772ba3c54f8901 100644 (file)
@@ -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.  */
index 4cd27dd19b05ac67c65b6a935422a3abb53646c4..512aa9feadad60a7a42ab9e735d5f63d38eb8d28 100644 (file)
@@ -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)),
index a87e2ae68b355355e733def3d3bff7d6e9de2f30..7ec079f15c638cf64c1a4a93b27edd83b7a3fbf1 100644 (file)
@@ -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);
index c40f2808197d9fffc938a48e68a6d04fcb4146bd..cad36f76e904a155a00d4329036b84332773c172 100644 (file)
@@ -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)),
index 4d45311b45cdd77e8d8e54c9635c1f1b15cb7d9d..6c23dd77609396624c225a83b6717b3d2c06ae91 100644 (file)
@@ -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)));
index 296c73c2d5233ca0ce0ed65b8dca3580d51b8e44..7c617cdccc4219d21bd7ef372459e8f6ce267ff4 100644 (file)
@@ -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)
index 77ac1495c46def4af050a86237ca03e1c655744f..adbeb063186d4a8e009e4dd184d73609d2c5d78c 100644 (file)
@@ -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.  */
@@ -1268,6 +1282,14 @@ is_a_helper <const gswitch *>::test (const gimple *gs)
   return gs->code == GIMPLE_SWITCH;
 }
 
+template <>
+template <>
+inline bool
+is_a_helper <gimple_statement_assume *>::test (gimple *gs)
+{
+  return gs->code == GIMPLE_ASSUME;
+}
+
 template <>
 template <>
 inline bool
@@ -1494,6 +1516,14 @@ is_a_helper <const greturn *>::test (const gimple *gs)
   return gs->code == GIMPLE_RETURN;
 }
 
+template <>
+template <>
+inline bool
+is_a_helper <const gimple_statement_assume *>::test (const gimple *gs)
+{
+  return gs->code == GIMPLE_ASSUME;
+}
+
 template <>
 template <>
 inline bool
@@ -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.  */
 
index d4209ea74a3dc54e4a23047873efa82b7ea88c62..42a996dfeb911002692a983715ea0f6d268452bb 100644 (file)
@@ -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;
        }
 
index 19e1088b718be1085efdabb362767f5866865a71..c3f64ef1ad3cd877103eeaadab563ad5aee6271b 100644 (file)
@@ -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)
index de608bd4802873cc6556cda101cebaa08a539a65..9471f543191edf0aea125ff0fc426511b2306169 100644 (file)
@@ -4526,5 +4526,4 @@ expand_TRAP (internal_fn, gcall *)
 void
 expand_ASSUME (internal_fn, gcall *)
 {
-  gcc_unreachable ();
 }
index fa896340dafd7ddfc6698abc01429922bea0ed18..5439651a576c0bbfc46f14cae65e8600051a0e6f 100644 (file)
@@ -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);
index 2e7af03888b38b9262b6c4dd42fc32571a6f3165..1e3890490626af7c212732aeb7e6e562c2280d22 100644 (file)
@@ -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);
index dc42c752017311ffeca934f94d40186b5352b783..a8809739001cc4e4a7f3c904e0bfe479cfec3369 100644 (file)
@@ -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);
index 524060e2fca2841ae9f9cdc804499b4dacdd7a4f..df333ba34ab261924ee7865760a0abaebdca7716 100644 (file)
@@ -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.  */
index 78a07f8691a2dc9bd2f5232b85efdd1705194982..3bbf525ac801a37c45b57bad9b4690e9cb0c18d9 100644 (file)
@@ -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);
index 939ec3e29c86dfab7cfbb710420940e3d97a2fd0..193b579474962f862cd66080f0d867f68b64c934 100644 (file)
@@ -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 (file)
index 0000000..fec2209
--- /dev/null
@@ -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 (file)
index 0000000..4a81df1
--- /dev/null
@@ -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 (file)
index 0000000..441242c
--- /dev/null
@@ -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;
+}
index eac4370431f019002f818fc4327b6e02aeabed51..63d9b005180eea2e64e15b0788f817e16060fc20 100644 (file)
@@ -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")
index ae781871a19dcd3e43364562712aa4aac6ab6392..9b2c0f6956c41471cb25dae1c939b5c86ea54335 100644 (file)
@@ -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;
index 01d4700076383a3fdf9962373d1be6f02acb1da4..8091ba8f13bd792869ca1526aa71b082a81147ed 100644 (file)
@@ -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);
index 4dfe05ed8e0de3f0121785ea49b2bd1503f8da58..8480d41384b7a6287fd794bc05a57f65d5898ab1 100644 (file)
@@ -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);
index 85c046078249ccffa9de11ffc298708a3e0c29c5..9778e776cf27f992f61a709842a7dbf895697067 100644 (file)
@@ -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);
index 93482e5d1027778a53fabf97f1f5e5f6fa8c953e..1adb15c99344b9f923292b1f7c9e6ca6f0a8a58f 100644 (file)
@@ -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);
+}