From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 89A413858D37 for ; Mon, 10 Oct 2022 08:54:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 89A413858D37 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1665392092; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type; bh=RK7oJ6HEdhMMqdCdPH8zSJDSJlsiNtzquC7O+uVF9Qw=; b=h1YSmeWwTH4cZ5b3UMqEQBmcWGrAGYrt5plS0HMRN9RXetpnZzjCj9KOw24at4tsrAvYSW 5CsyvJxyHMDVpbqMN7lH4WZ4JBpcTpdDqq0wjPJc3HJR3VAP9kutDJiBeqbJXhCwu5o5wt Y0BsDxYBQ2FVop1v40UHv8WocVxw8jw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-570-I1HfQm0UOHSGYXtf4HOSdw-1; Mon, 10 Oct 2022 04:54:48 -0400 X-MC-Unique: I1HfQm0UOHSGYXtf4HOSdw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7B29485A59D; Mon, 10 Oct 2022 08:54:48 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.55]) by smtp.corp.redhat.com (Postfix) with ESMTPS id EEEA4145BEEE; Mon, 10 Oct 2022 08:54:47 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 29A8siDp3173381 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 10 Oct 2022 10:54:45 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 29A8sgd93173380; Mon, 10 Oct 2022 10:54:42 +0200 Date: Mon, 10 Oct 2022 10:54:42 +0200 From: Jakub Jelinek To: Richard Biener , Jan Hubicka , Aldy Hernandez Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] middle-end IFN_ASSUME support [PR106654] Message-ID: Reply-To: Jakub Jelinek MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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; [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 ; else goto ; : { i = i + 1; D.2591 = i == 44; } : .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 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 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 (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 (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 (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 (stmt)); + else + lab = gimple_cond_false_label (as_a (stmt)); + } + else if (gimple_code (stmt) == GIMPLE_LABEL) + { + tree label = gimple_label_label (as_a (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 (stmt), + data->return_false_label); + else if (idx == 0) + gimple_cond_set_true_label (as_a (stmt), + data->return_false_label); + else + gimple_cond_set_false_label (as_a (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 (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 (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 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 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 (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