public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-3353] middle-end IFN_ASSUME support [PR106654]
@ 2022-10-18  8:40 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2022-10-18  8:40 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:4dda30e9910c4f04a6258b053401249e213f29be

commit r13-3353-g4dda30e9910c4f04a6258b053401249e213f29be
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Tue Oct 18 10:31:20 2022 +0200

    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.

Diff:
---
 gcc/cgraph.cc                             |   4 +-
 gcc/cgraphunit.cc                         |  14 ++
 gcc/cp/cp-gimplify.cc                     |  15 +-
 gcc/cp/cp-tree.h                          |   1 +
 gcc/cp/parser.cc                          |   6 +-
 gcc/cp/pt.cc                              |   5 +-
 gcc/function.h                            |   4 +
 gcc/gimple-low.cc                         | 395 ++++++++++++++++++++++++++++++
 gcc/gimple-pretty-print.cc                |  29 +++
 gcc/gimple-walk.cc                        |  13 +
 gcc/gimple.cc                             |  19 ++
 gcc/gimple.def                            |   5 +
 gcc/gimple.h                              |  78 ++++++
 gcc/gimplify.cc                           |  28 ++-
 gcc/gsstruct.def                          |   1 +
 gcc/internal-fn.cc                        |   1 -
 gcc/lto-streamer-in.cc                    |   1 +
 gcc/lto-streamer-out.cc                   |   1 +
 gcc/omp-low.cc                            |   4 +
 gcc/omp-oacc-kernels-decompose.cc         |   1 +
 gcc/passes.cc                             |   9 +
 gcc/passes.def                            |   1 +
 gcc/testsuite/g++.dg/cpp23/attr-assume5.C |   5 +
 gcc/testsuite/g++.dg/cpp23/attr-assume6.C |   5 +
 gcc/testsuite/g++.dg/cpp23/attr-assume7.C |  56 +++++
 gcc/timevar.def                           |   1 +
 gcc/tree-cfg.cc                           |   7 +
 gcc/tree-inline.cc                        |   5 +
 gcc/tree-pass.h                           |   6 +-
 gcc/tree-ssa-ccp.cc                       |   6 +
 gcc/tree-vrp.cc                           |  35 +++
 31 files changed, 745 insertions(+), 16 deletions(-)

diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index 8d6ed38efa2..0417b059965 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 7b5be0f1539..b05d790bf8d 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 2608475897c..28c339869b8 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 e2607f09c19..60a25101049 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 9ddfb027ff9..a39c5f0d24b 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 e4dca9d4f9d..5eddad900ea 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 098613766be..d7deaebee9c 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 4cd27dd19b0..512aa9feada 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 a87e2ae68b3..7ec079f15c6 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 c40f2808197..cad36f76e90 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 4d45311b45c..6c23dd77609 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 296c73c2d52..7c617cdccc4 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 77ac1495c46..adbeb063186 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.  */
@@ -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.  */
 
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index d4209ea74a3..42a996dfeb9 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 19e1088b718..c3f64ef1ad3 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 de608bd4802..9471f543191 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 fa896340daf..5439651a576 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 2e7af03888b..1e389049062 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 dc42c752017..a8809739001 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 524060e2fca..df333ba34ab 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 78a07f8691a..3bbf525ac80 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 939ec3e29c8..193b5794749 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 00000000000..fec220925c5
--- /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 00000000000..4a81df17544
--- /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 00000000000..441242ce050
--- /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 eac4370431f..63d9b005180 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 ae781871a19..9b2c0f6956c 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 01d47000763..8091ba8f13b 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 4dfe05ed8e0..8480d41384b 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 85c04607824..9778e776cf2 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 93482e5d102..1adb15c9934 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);
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-18  8:40 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-18  8:40 [gcc r13-3353] middle-end IFN_ASSUME support [PR106654] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).