public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] middle-end IFN_ASSUME support [PR106654]
@ 2022-10-10  8:54 Jakub Jelinek
  2022-10-10 21:09 ` Jason Merrill
                   ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Jakub Jelinek @ 2022-10-10  8:54 UTC (permalink / raw)
  To: Richard Biener, Jan Hubicka, Aldy Hernandez; +Cc: gcc-patches

Hi!

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 actually throws even the simplest assumptions on the floor,
we don't expect optimizations and the assumptions are there to allow
optimizations.  Otherwise, it keeps the handling of the simplest
assumptions as is, and 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
  D.2591 = .ASSUME ();
  if (D.2591 != 0) goto <D.2593>; else goto <D.2592>;
  <D.2593>:
  {
    i = i + 1;
    D.2591 = i == 44;
  }
  <D.2592>:
  .ASSUME (D.2591);
with the condition wrapped into a GIMPLE_BIND (I admit the above isn't
extra clean but it is just something to hold it from gimplifier until
gimple low pass; it reassembles if (condition_never_true) { cond; };
an alternative would be introduce GOMP_ASSUME statement that would have
the guard var as operand and the GIMPLE_BIND as body, but for the
few passes (tree-nested and omp lowering) in between that looked like
an overkill to me) which is then pattern matched during gimple lowering
and outlined into a separate function.  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 RTL
expansion, which isn't done for them nor any later pass, on the other
side bodies are not freed when done.

Earlier version of the patch has been successfully bootstrapped/regtested
on x86_64-linux and i686-linux and all assume tests have passed even with
this version.  Ok for trunk if it succeeds on another bootstrap/regtest?

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-10  Jakub Jelinek  <jakub@redhat.com>

	PR c++/106654
gcc/
	* function.h (struct function): Add assume_function bitfield.
	* gimplify.cc (gimplify_call_expr): For -O0, always throw
	away assumptions on the floor immediately.  Otherwise if the
	assumption isn't simple enough, expand it into IFN_ASSUME
	guarded block.
	* gimple-low.cc (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 IFN_ASSUME guarded block.
	* 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.
	* cfgexpand.cc (pass_expand::execute): Don't expand assume_function
	into RTL, just destroy loops and exit.
	* internal-fn.cc (expand_ASSUME): Remove gcc_unreachable.
	* passes.cc (pass_rest_of_compilation::gate): Return false also for
	fun->assume_function.
	* tree-vectorizer.cc (pass_vectorize::gate,
	pass_slp_vectorize::gate): Likewise.
	* ipa-icf.cc (sem_function::parse): Punt for func->assume_function.
gcc/cp/
	* parser.cc (cp_parser_omp_assumption_clauses): Wrap IFN_ASSUME
	argument with fold_build_cleanup_point_expr.
	* cp-gimplify.cc (process_stmt_assume_attribute): Likewise.
	* pt.cc (tsubst_copy_and_build): Likewise.
gcc/testsuite/
	* g++.dg/cpp23/attr-assume5.C: New test.
	* g++.dg/cpp23/attr-assume6.C: New test.
	* g++.dg/cpp23/attr-assume7.C: New test.

--- gcc/function.h.jj	2022-10-10 09:31:22.051478926 +0200
+++ gcc/function.h	2022-10-10 09:59:49.283646705 +0200
@@ -438,6 +438,10 @@ struct GTY(()) function {
 
   /* Set if there are any OMP_TARGET regions in the function.  */
   unsigned int has_omp_target : 1;
+
+  /* Set for artificial function created for [[assume (cond)]].
+     These should be GIMPLE optimized, but not expanded to RTL.  */
+  unsigned int assume_function : 1;
 };
 
 /* Add the decl D to the local_decls list of FUN.  */
--- gcc/gimplify.cc.jj	2022-10-10 09:31:57.518983613 +0200
+++ gcc/gimplify.cc	2022-10-10 09:59:49.285646677 +0200
@@ -3556,6 +3556,12 @@ gimplify_call_expr (tree *expr_p, gimple
 
       if (ifn == IFN_ASSUME)
 	{
+	  /* If not optimizing, ignore the assumptions.  */
+	  if (!optimize)
+	    {
+	      *expr_p = NULL_TREE;
+	      return GS_ALL_DONE;
+	    }
 	  if (simple_condition_p (CALL_EXPR_ARG (*expr_p, 0)))
 	    {
 	      /* If the [[assume (cond)]]; condition is simple
@@ -3569,7 +3575,46 @@ gimplify_call_expr (tree *expr_p, gimple
 						     fndecl, 0));
 	      return GS_OK;
 	    }
-	  /* FIXME: Otherwise expand it specially.  */
+	  /* Temporarily, until gimple lowering, transform
+	     .ASSUME (cond);
+	     into:
+	     guard = .ASSUME ();
+	     if (guard) goto label_true; else label_false;
+	     label_true:;
+	     {
+	       guard = cond;
+	     }
+	     label_false:;
+	     .ASSUME (guard);
+	     such that gimple lowering can outline the condition into
+	     a separate function easily.  */
+	  tree guard = create_tmp_var (boolean_type_node);
+	  gcall *call = gimple_build_call_internal (ifn, 0);
+	  gimple_call_set_nothrow (call, TREE_NOTHROW (*expr_p));
+	  gimple_set_location (call, loc);
+	  gimple_call_set_lhs (call, guard);
+	  gimple_seq_add_stmt (pre_p, call);
+	  *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);
+	  tree label_false = create_artificial_label (UNKNOWN_LOCATION);
+	  tree label_true = create_artificial_label (UNKNOWN_LOCATION);
+	  gcond *cond_stmt = gimple_build_cond (NE_EXPR, guard,
+						boolean_false_node,
+						label_true, label_false);
+	  gimplify_seq_add_stmt (pre_p, cond_stmt);
+	  gimplify_seq_add_stmt (pre_p, gimple_build_label (label_true));
+	  push_gimplify_context ();
+	  gimple_seq body = NULL;
+	  gimple *g = gimplify_and_return_first (*expr_p, &body);
+	  pop_gimplify_context (g);
+	  gimplify_seq_add_seq (pre_p, body);
+	  gimplify_seq_add_stmt (pre_p, gimple_build_label (label_false));
+	  call = gimple_build_call_internal (ifn, 1, guard);
+	  gimple_call_set_nothrow (call, TREE_NOTHROW (*expr_p));
+	  gimple_set_location (call, loc);
+	  gimple_seq_add_stmt (pre_p, call);
+	  *expr_p = NULL_TREE;
 	  return GS_ALL_DONE;
 	}
 
--- gcc/gimple-low.cc.jj	2022-10-10 09:31:22.107478144 +0200
+++ gcc/gimple-low.cc	2022-10-10 10:22:05.891999132 +0200
@@ -33,6 +33,13 @@ along with GCC; see the file COPYING3.
 #include "predict.h"
 #include "gimple-predict.h"
 #include "gimple-fold.h"
+#include "cgraph.h"
+#include "tree-ssa.h"
+#include "value-range.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-inline.h"
+#include "gimple-walk.h"
 
 /* The differences between High GIMPLE and Low GIMPLE are the
    following:
@@ -237,6 +244,383 @@ lower_omp_directive (gimple_stmt_iterato
   gsi_next (gsi);
 }
 
+static tree
+create_assumption_fn (location_t loc)
+{
+  tree name = clone_function_name_numbered (current_function_decl, "_assume");
+  /* For now, will be changed later.  */
+  tree type = TREE_TYPE (current_function_decl);
+  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);
+  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);
+  DECL_FUNCTION_VERSIONED (decl)
+    = DECL_FUNCTION_VERSIONED (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:
+   guard = .ASSUME ();
+   if (guard) goto label_true; else label_false;
+   label_true:;
+   {
+     guard = cond;
+   }
+   label_false:;
+   .ASSUME (guard);
+   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_call_lhs (stmt);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (guard);
+  gsi_remove (gsi, true);
+  stmt = gsi_stmt (*gsi);
+  gcond *cond = as_a <gcond *> (stmt);
+  gcc_assert (gimple_cond_lhs (cond) == guard
+	      || gimple_cond_rhs (cond) == guard);
+  tree l1 = gimple_cond_true_label (cond);
+  tree l2 = gimple_cond_false_label (cond);
+  gsi_remove (gsi, true);
+  stmt = gsi_stmt (*gsi);
+  glabel *lab = as_a <glabel *> (stmt);
+  gcc_assert (gimple_label_label (lab) == l1
+	      || gimple_label_label (lab) == l2);
+  gsi_remove (gsi, true);
+  gimple *bind = gsi_stmt (*gsi);
+  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;
+
+  struct walk_stmt_info wi;
+  memset (&wi, 0, sizeof (wi));
+  wi.info = (void *) &lad;
+  walk_gimple_stmt (gsi, 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;
+      if (TREE_CODE (v) == SSA_NAME)
+	{
+	  newv = make_ssa_name (remap_type (TREE_TYPE (v), &lad.id));
+	  decl_map.put (v, newv);
+	}
+      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;
+	}
+    }
+  memset (&wi, 0, sizeof (wi));
+  wi.info = (void *) &lad;
+  walk_gimple_stmt (gsi, adjust_assumption_stmt_r,
+		    adjust_assumption_stmt_op, &wi);
+  gsi_remove (gsi, 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);
+  greturn *gr = gimple_build_return (lad.guard_copy);
+  gimple_seq_add_stmt (&body, gr);
+  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);
+  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);
+      vargs[i - sz] = lad.decls[i - 1];
+      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);
+    }
+
+  stmt = gsi_stmt (*gsi);
+  lab = as_a <glabel *> (stmt);
+  gcc_assert (gimple_label_label (lab) == l1
+	      || gimple_label_label (lab) == l2);
+  gsi_remove (gsi, true);
+  stmt = gsi_stmt (*gsi);
+  gcc_assert (gimple_call_internal_p (stmt, IFN_ASSUME)
+	      && gimple_call_num_args (stmt) == 1
+	      && gimple_call_arg (stmt, 0) == guard);
+  data->cannot_fallthru = false;
+  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
@@ -354,6 +738,13 @@ lower_stmt (gimple_stmt_iterator *gsi, s
 	tree decl = gimple_call_fndecl (stmt);
 	unsigned i;
 
+	if (gimple_call_internal_p (stmt, IFN_ASSUME)
+	    && gimple_call_num_args (stmt) == 0)
+	  {
+	    lower_assumption (gsi, data);
+	    return;
+	  }
+
 	for (i = 0; i < gimple_call_num_args (stmt); i++)
 	  {
 	    tree arg = gimple_call_arg (stmt, i);
--- gcc/tree-ssa-ccp.cc.jj	2022-10-10 09:31:22.472473047 +0200
+++ gcc/tree-ssa-ccp.cc	2022-10-10 09:59:49.286646663 +0200
@@ -4253,6 +4253,12 @@ pass_fold_builtins::execute (function *f
 	    }
 
 	  callee = gimple_call_fndecl (stmt);
+	  if (!callee
+	      && gimple_call_internal_p (stmt, IFN_ASSUME))
+	    {
+	      gsi_remove (&i, true);
+	      continue;
+	    }
 	  if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
 	    {
 	      gsi_next (&i);
--- gcc/lto-streamer-out.cc.jj	2022-10-10 09:31:22.331475016 +0200
+++ gcc/lto-streamer-out.cc	2022-10-10 09:59:49.287646649 +0200
@@ -2278,6 +2278,7 @@ output_struct_function_base (struct outp
   bp_pack_value (&bp, fn->calls_eh_return, 1);
   bp_pack_value (&bp, fn->has_force_vectorize_loops, 1);
   bp_pack_value (&bp, fn->has_simduid_loops, 1);
+  bp_pack_value (&bp, fn->assume_function, 1);
   bp_pack_value (&bp, fn->va_list_fpr_size, 8);
   bp_pack_value (&bp, fn->va_list_gpr_size, 8);
   bp_pack_value (&bp, fn->last_clique, sizeof (short) * 8);
--- gcc/lto-streamer-in.cc.jj	2022-10-10 09:31:22.329475044 +0200
+++ gcc/lto-streamer-in.cc	2022-10-10 09:59:49.287646649 +0200
@@ -1318,6 +1318,7 @@ input_struct_function_base (struct funct
   fn->calls_eh_return = bp_unpack_value (&bp, 1);
   fn->has_force_vectorize_loops = bp_unpack_value (&bp, 1);
   fn->has_simduid_loops = bp_unpack_value (&bp, 1);
+  fn->assume_function = bp_unpack_value (&bp, 1);
   fn->va_list_fpr_size = bp_unpack_value (&bp, 8);
   fn->va_list_gpr_size = bp_unpack_value (&bp, 8);
   fn->last_clique = bp_unpack_value (&bp, sizeof (short) * 8);
--- gcc/cgraphunit.cc.jj	2022-10-10 09:31:21.647484568 +0200
+++ gcc/cgraphunit.cc	2022-10-10 09:59:49.288646635 +0200
@@ -1882,6 +1882,16 @@ cgraph_node::expand (void)
   ggc_collect ();
   timevar_pop (TV_REST_OF_COMPILATION);
 
+  if (DECL_STRUCT_FUNCTION (decl)
+      && DECL_STRUCT_FUNCTION (decl)->assume_function)
+    {
+      /* Assume functions aren't expanded into RTL, on the other side
+	 we don't want to release their body.  */
+      if (cfun)
+	pop_cfun ();
+      return;
+    }
+
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
   if (cfun)
--- gcc/cfgexpand.cc.jj	2022-10-10 09:31:21.554485867 +0200
+++ gcc/cfgexpand.cc	2022-10-10 09:59:49.288646635 +0200
@@ -6597,6 +6597,14 @@ pass_expand::execute (function *fun)
   rtx_insn *var_seq, *var_ret_seq;
   unsigned i;
 
+  if (cfun->assume_function)
+    {
+      /* Assume functions should not be expanded to RTL.  */
+      cfun->curr_properties &= ~PROP_loops;
+      loop_optimizer_finalize ();
+      return 0;
+    }
+
   timevar_push (TV_OUT_OF_SSA);
   rewrite_out_of_ssa (&SA);
   timevar_pop (TV_OUT_OF_SSA);
--- gcc/internal-fn.cc.jj	2022-10-10 09:31:22.246476203 +0200
+++ gcc/internal-fn.cc	2022-10-10 09:59:49.289646621 +0200
@@ -4526,5 +4526,4 @@ expand_TRAP (internal_fn, gcall *)
 void
 expand_ASSUME (internal_fn, gcall *)
 {
-  gcc_unreachable ();
 }
--- gcc/passes.cc.jj	2022-10-10 09:31:22.379474346 +0200
+++ gcc/passes.cc	2022-10-10 09:59:49.289646621 +0200
@@ -647,11 +647,12 @@ public:
   {}
 
   /* opt_pass methods: */
-  bool gate (function *) final override
+  bool gate (function *fun) final override
     {
       /* Early return if there were errors.  We can run afoul of our
 	 consistency checks, and there's not really much point in fixing them.  */
-      return !(rtl_dump_and_exit || flag_syntax_only || seen_error ());
+      return !(rtl_dump_and_exit || fun->assume_function
+	       || flag_syntax_only || seen_error ());
     }
 
 }; // class pass_rest_of_compilation
--- gcc/tree-vectorizer.cc.jj	2022-10-10 09:31:22.516472432 +0200
+++ gcc/tree-vectorizer.cc	2022-10-10 09:59:49.290646607 +0200
@@ -1213,6 +1213,10 @@ public:
   /* opt_pass methods: */
   bool gate (function *fun) final override
     {
+      /* Vectorization makes range analysis of assume functions
+	 harder, not easier.  */
+      if (fun->assume_function)
+	return false;
       return flag_tree_loop_vectorize || fun->has_force_vectorize_loops;
     }
 
@@ -1490,7 +1494,14 @@ public:
 
   /* opt_pass methods: */
   opt_pass * clone () final override { return new pass_slp_vectorize (m_ctxt); }
-  bool gate (function *) final override { return flag_tree_slp_vectorize != 0; }
+  bool gate (function *fun) final override
+  {
+    /* Vectorization makes range analysis of assume functions harder,
+       not easier.  */
+    if (fun->assume_function)
+      return false;
+    return flag_tree_slp_vectorize != 0;
+  }
   unsigned int execute (function *) final override;
 
 }; // class pass_slp_vectorize
--- gcc/ipa-icf.cc.jj	2022-06-28 13:03:30.834690968 +0200
+++ gcc/ipa-icf.cc	2022-10-10 10:29:31.187766299 +0200
@@ -1517,6 +1517,9 @@ sem_function::parse (cgraph_node *node,
   if (!func || (!node->has_gimple_body_p () && !node->thunk))
     return NULL;
 
+  if (func->assume_function)
+    return NULL;
+
   if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
     return NULL;
 
--- gcc/cp/parser.cc.jj	2022-10-10 09:31:57.405985191 +0200
+++ gcc/cp/parser.cc	2022-10-10 09:59:49.295646537 +0200
@@ -46031,6 +46031,8 @@ cp_parser_omp_assumption_clauses (cp_par
 		t = contextual_conv_bool (t, tf_warning_or_error);
 	      if (is_assume && !error_operand_p (t))
 		{
+		  if (!processing_template_decl)
+		    t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
 		  t = build_call_expr_internal_loc (eloc, IFN_ASSUME,
 						    void_type_node, 1, t);
 		  finish_expr_stmt (t);
--- gcc/cp/cp-gimplify.cc.jj	2022-10-10 09:31:57.309986531 +0200
+++ gcc/cp/cp-gimplify.cc	2022-10-10 09:59:49.296646524 +0200
@@ -3139,6 +3139,8 @@ process_stmt_assume_attribute (tree std_
 	    arg = contextual_conv_bool (arg, tf_warning_or_error);
 	  if (error_operand_p (arg))
 	    continue;
+	  if (!processing_template_decl)
+	    arg = fold_build_cleanup_point_expr (TREE_TYPE (arg), arg);
 	  statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME,
 						    void_type_node, 1, arg);
 	  finish_expr_stmt (statement);
--- gcc/cp/pt.cc.jj	2022-10-10 09:31:21.947480379 +0200
+++ gcc/cp/pt.cc	2022-10-10 09:59:49.299646482 +0200
@@ -21105,6 +21105,8 @@ tsubst_copy_and_build (tree t,
 		      ret = error_mark_node;
 		      break;
 		    }
+		  if (!processing_template_decl)
+		    arg = fold_build_cleanup_point_expr (TREE_TYPE (arg), arg);
 		  ret = build_call_expr_internal_loc (EXPR_LOCATION (t),
 						      IFN_ASSUME,
 						      void_type_node, 1,
--- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj	2022-10-10 09:59:49.299646482 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C	2022-10-10 09:59:49.299646482 +0200
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume1.C"
--- gcc/testsuite/g++.dg/cpp23/attr-assume6.C.jj	2022-10-10 09:59:49.300646468 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume6.C	2022-10-10 09:59:49.300646468 +0200
@@ -0,0 +1,5 @@
+// P1774R8 - Portable assumptions
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+
+#include "attr-assume3.C"
--- gcc/testsuite/g++.dg/cpp23/attr-assume7.C.jj	2022-10-10 09:59:49.300646468 +0200
+++ gcc/testsuite/g++.dg/cpp23/attr-assume7.C	2022-10-10 10:05:51.472593600 +0200
@@ -0,0 +1,42 @@
+// 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;
+}

	Jakub


^ permalink raw reply	[flat|nested] 35+ messages in thread
* [PATCH] middle-end IFN_ASSUME support [PR106654]
@ 2022-11-08  9:19 Pilar Latiesa
  2022-11-08 12:10 ` Jakub Jelinek
  0 siblings, 1 reply; 35+ messages in thread
From: Pilar Latiesa @ 2022-11-08  9:19 UTC (permalink / raw)
  To: ma.uecker, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 315 bytes --]

On Mon, Oct 17, 2022 at 05:32:32AM +0200, Martin Uecker wrote:
> Hm, that already seems to work with
>
> if (!std::isfinite(x))
>   __builtin_unreachable();
>
> https://godbolt.org/z/hj3WrEhjb

Not anymore. Perhaps after making ranger the VRP default, because I get the
mentioned outcome with --param=vrp1-mode=vrp

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2022-11-08 12:11 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-10  8:54 [PATCH] middle-end IFN_ASSUME support [PR106654] Jakub Jelinek
2022-10-10 21:09 ` Jason Merrill
2022-10-10 21:19   ` Jakub Jelinek
2022-10-11 13:36     ` [PATCH] middle-end, v2: " Jakub Jelinek
2022-10-12 15:48       ` Jason Merrill
2022-10-13  6:50         ` [PATCH] middle-end, v3: " Jakub Jelinek
2022-10-14 11:27           ` Richard Biener
2022-10-14 18:33             ` Jakub Jelinek
2022-10-17  6:55               ` Richard Biener
2022-10-17 15:44             ` [PATCH] middle-end, v4: " Jakub Jelinek
2022-10-18  7:00               ` Richard Biener
2022-10-18 21:31               ` Andrew MacLeod
2022-10-19 16:06                 ` Jakub Jelinek
2022-10-19 16:55                   ` Andrew MacLeod
2022-10-19 17:39                     ` Jakub Jelinek
2022-10-19 17:41                       ` Jakub Jelinek
2022-10-19 18:25                         ` Andrew MacLeod
2022-10-19 17:14                   ` Andrew MacLeod
2022-10-11 18:05 ` [PATCH] middle-end " Andrew MacLeod
2022-10-12 10:15   ` Jakub Jelinek
2022-10-12 14:31     ` Andrew MacLeod
2022-10-12 14:39       ` Jakub Jelinek
2022-10-12 16:12         ` Andrew MacLeod
2022-10-13  8:11           ` Richard Biener
2022-10-13  9:53             ` Jakub Jelinek
2022-10-13 13:16               ` Andrew MacLeod
2022-10-13  9:57           ` Jakub Jelinek
2022-10-17 17:53     ` Andrew MacLeod
2022-10-14 20:43 ` Martin Uecker
2022-10-14 21:20   ` Jakub Jelinek
2022-10-15  8:07     ` Martin Uecker
2022-10-15  8:53       ` Jakub Jelinek
2022-10-17  5:52         ` Martin Uecker
2022-11-08  9:19 Pilar Latiesa
2022-11-08 12:10 ` 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).