From patchwork Thu Oct 13 06:50:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1975 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4ac7:0:0:0:0:0 with SMTP id y7csp126381wrs; Wed, 12 Oct 2022 23:51:35 -0700 (PDT) X-Google-Smtp-Source: AMsMyM7RVKdBhU10WvN7VjVQh+t1Ef1F+rdwdf95bzuK5sly1RZM03/CYyOyj5xOadeYLA4Vj0qc X-Received: by 2002:a05:6402:5ca:b0:445:c80a:3c2 with SMTP id n10-20020a05640205ca00b00445c80a03c2mr30830657edx.247.1665643894870; Wed, 12 Oct 2022 23:51:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1665643894; cv=none; d=google.com; s=arc-20160816; b=StAjavjE/7Hy/KL9kPJh95mXXEQiPpoWi6rHHdWiXBMlzxtEIJTQRDx3d+nxt8K4Pf vb7AlElg/tC1SXh1e9uqoayz8CjU7K8SIU3M89gvRahrFILAfHD+dq7D3BnBcoC0/vRq ALwlq4eSiP9Zw1gtCpVK6eYkxvyeIYJvIDURupQCYUdy3yP3t2fkVuYul3tacJpA/+pp c/Bg6d0OLzetqmzsW/jGQWKa7LcGF8GPLi4/rk//p+KCMLaxSv8Z9h1ZvG44N8sICkxj xcFinxOnjaIDLtY2awHy9RhFJR/8k2YCZnYz1BvTmOEOY6f+NwKortqTe0jU6D1OlFis iycw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:from:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence :content-disposition:in-reply-to:mime-version:references:message-id :subject:to:date:dmarc-filter:delivered-to:dkim-signature :dkim-filter; bh=cColTEqImQB9nhnY/OlS4Rp5mPMjC77gEaCIgCnxvtc=; b=D2eRYh4Lm4ZVp4j3sjh+VhK2aUNPCjFcUXsGUngduG0/pg7XC6lIlXq3+U0YvfVVrz v35AcI/XepG5X7SxHy1bXouJydFKERWKNkFpFuqfGEPlpC2jcyL6hIzcsU0LRsgJF2nB DiXG7XyNcKiOcrGjdyIl3xNMm2lSz5R9ffUgKu9anp86NBxP6M6mS7jpVqEVzQNnJP+U R/AJ7qwrzUcJw/Fn2ijSCsp4bMj/daUgpBhc6DhU8B5bN/dHdK/GaGt2AI+jE9EMF0e3 QrVDMtKqVFbfFQFyvK47ElCN1vD0civfwXLjOpdEnjq7EOFlDriMlr5GoPq5WU7yIZTp rPJQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=djw7ZOJJ; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id fj2-20020a1709069c8200b0078d8b6976eesi18716264ejc.140.2022.10.12.23.51.34 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Oct 2022 23:51:34 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=djw7ZOJJ; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C8173385383F for ; Thu, 13 Oct 2022 06:51:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C8173385383F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1665643893; bh=cColTEqImQB9nhnY/OlS4Rp5mPMjC77gEaCIgCnxvtc=; h=Date:To:Subject:References:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=djw7ZOJJBetLsu58TqGKED9mePIt4hQpV1BV0OPDpFtbrpLAD9LvMREfWeF0fFsML iwqathSXMELXvMgpasJfhebQwmrmFQiY4/ZXtOPKH2/pTNXhAe/RcH1adCPSbBgikJ tm21ZL1YUox+nXbnKCUTcr04prKzG62sfTsNOD7k= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 9CB933858C56 for ; Thu, 13 Oct 2022 06:50:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 9CB933858C56 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-664-Msw-PGQBNZORGbajTzkjOQ-1; Thu, 13 Oct 2022 02:50:46 -0400 X-MC-Unique: Msw-PGQBNZORGbajTzkjOQ-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B1883380670B; Thu, 13 Oct 2022 06:50:45 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.55]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2FB9F4A9254; Thu, 13 Oct 2022 06:50:45 +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 29D6ogNQ2960126 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Thu, 13 Oct 2022 08:50:42 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 29D6ofpZ2960125; Thu, 13 Oct 2022 08:50:41 +0200 Date: Thu, 13 Oct 2022 08:50:40 +0200 To: Jason Merrill , Jan Hubicka , Richard Biener Subject: [PATCH] middle-end, v3: IFN_ASSUME support [PR106654] Message-ID: References: <479fa663-fb3e-a90d-ae7a-0fdd5acbfa00@redhat.com> MIME-Version: 1.0 In-Reply-To: <479fa663-fb3e-a90d-ae7a-0fdd5acbfa00@redhat.com> X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-4.1 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1746554213007813677?= X-GMAIL-MSGID: =?utf-8?q?1746554213007813677?= On Wed, Oct 12, 2022 at 11:48:27AM -0400, Jason Merrill wrote: > > --- 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, > > This starts to seem worth factoring out a build_assume_call function. Ok, below is an updated version of the patch that does that. Bootstrapped/regtested on x86_64-linux and i686-linux. > > I'll leave the middle-end review to others. Ok. 2022-10-13 Jakub Jelinek PR c++/106654 gcc/ * function.h (struct function): Add assume_function bitfield. * gimplify.cc (gimplify_call_expr): If the assumption isn't simple enough, expand it into IFN_ASSUME guarded block or for -O0 drop it. * 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/ * 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. Jakub --- gcc/function.h.jj 2022-10-10 11:57:40.163722972 +0200 +++ gcc/function.h 2022-10-12 19:48:28.887554771 +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 11:57:40.165722944 +0200 +++ gcc/gimplify.cc 2022-10-12 19:48:28.890554730 +0200 @@ -3569,7 +3569,52 @@ gimplify_call_expr (tree *expr_p, gimple 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: + 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 11:57:40.163722972 +0200 +++ gcc/gimple-low.cc 2022-10-12 19:48:28.890554730 +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 11:57:40.203722414 +0200 +++ gcc/tree-ssa-ccp.cc 2022-10-12 19:48:28.891554716 +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 11:57:40.202722428 +0200 +++ gcc/lto-streamer-out.cc 2022-10-12 19:48:28.891554716 +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 11:57:40.201722442 +0200 +++ gcc/lto-streamer-in.cc 2022-10-12 19:48:28.891554716 +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 11:57:40.152723125 +0200 +++ gcc/cgraphunit.cc 2022-10-12 19:48:28.892554703 +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 11:57:40.152723125 +0200 +++ gcc/cfgexpand.cc 2022-10-12 19:48:28.893554689 +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 11:57:40.166722930 +0200 +++ gcc/internal-fn.cc 2022-10-12 19:48:28.893554689 +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 11:57:40.202722428 +0200 +++ gcc/passes.cc 2022-10-12 19:48:28.893554689 +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 11:57:40.204722400 +0200 +++ gcc/tree-vectorizer.cc 2022-10-12 19:48:28.894554675 +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-10-10 11:57:40.201722442 +0200 +++ gcc/ipa-icf.cc 2022-10-12 19:48:28.894554675 +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/cp-tree.h.jj 2022-10-12 17:51:00.911944744 +0200 +++ gcc/cp/cp-tree.h 2022-10-12 19:53:17.072615254 +0200 @@ -8284,6 +8284,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); --- gcc/cp/parser.cc.jj 2022-10-12 17:51:00.951944199 +0200 +++ gcc/cp/parser.cc 2022-10-12 19:52:15.855452024 +0200 @@ -46006,11 +46006,7 @@ cp_parser_omp_assumption_clauses (cp_par 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, --- gcc/cp/cp-gimplify.cc.jj 2022-10-12 17:51:00.909944772 +0200 +++ gcc/cp/cp-gimplify.cc 2022-10-12 19:51:47.140844525 +0200 @@ -3096,6 +3096,17 @@ process_stmt_hotness_attribute (tree std 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 @@ -3132,9 +3143,7 @@ process_stmt_assume_attribute (tree std_ 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); --- gcc/cp/pt.cc.jj 2022-10-12 17:51:00.957944117 +0200 +++ gcc/cp/pt.cc 2022-10-12 19:52:45.204050862 +0200 @@ -21116,10 +21116,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; --- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj 2022-10-12 19:48:28.903554553 +0200 +++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C 2022-10-12 19:48:28.903554553 +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-12 19:48:28.903554553 +0200 +++ gcc/testsuite/g++.dg/cpp23/attr-assume6.C 2022-10-12 19:48:28.903554553 +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-12 19:48:28.903554553 +0200 +++ gcc/testsuite/g++.dg/cpp23/attr-assume7.C 2022-10-12 19:48:28.903554553 +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; +}