From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by sourceware.org (Postfix) with ESMTPS id 55050385483F for ; Fri, 14 Oct 2022 20:43:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 55050385483F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-ed1-x52c.google.com with SMTP id q19so8362607edd.10 for ; Fri, 14 Oct 2022 13:43:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:user-agent:references :in-reply-to:date:cc:to:from:subject:message-id:from:to:cc:subject :date:message-id:reply-to; bh=x8Gum14iHfcZeCZxbefGjppGog/PhebNRrf4onu/YH8=; b=X8JX45GPZpzBxoVTYiELTu0hviMMZAabQYz+cv6yp+GAjyc46NqNa+6iRJQUX/JlKU K9Z4eIGJHwBdb6QFuP6HB9N01E4+9zo+Vi83/7zwVhTiKfC8ElCgYwL5Y22KlaXd0toM 6c7+1ZvPhbo8NnbK86BxDak4chssQVxZdAnBNBOVlGKN+r2+eLt2DFbN+SL5M5qGHZ+W NdVwfK/VdIShR+dupW3Uw82YT9knasOzb/4zRXIMvPWhyB1zNdDcbweLqukG3rd63F6g PU3ZxbkA3cjZqOk/sth+5kWNqPsOs7i0P4R5dKntd2lET1CIBO5V9XwC+57/yl43Sa8f kx3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:user-agent:references :in-reply-to:date:cc:to:from:subject:message-id:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=x8Gum14iHfcZeCZxbefGjppGog/PhebNRrf4onu/YH8=; b=NNfWFZltJktAz7qVMYdkINgtIc9EtV/e9GIwH8cp6ddkTyW2UdTcTw7zjUfR0ukQi7 lbwe+fKtVTuJqWNP52WGS/4cQ616lxnbaHGDoD8ho6urzzjh1AAucrCAcHoqiAQT0DNX pUyMP1aRJ24F395ird1WVIUfoie6/EqGxgDfqExcEKOAvvUNiyJiq0mDBX/SSVAlClV8 uhx/l6jOzs44RTWYRBvhCndVr4NhuQAYPFVuuKfJRylNNslt3PAviKq90CpHaOTiIkdm 7Xb0BGnCqheRRI8hnnqaok88TXOmIK9ABGoHFCg2X+chNIR3SOAUHTluuNPI7sERw60q g/rw== X-Gm-Message-State: ACrzQf0TRIQGjudpCyiQILI42rbUHyRFweLsz4ImQxcNbBgsVpWTLhRP 7R6e4wUpO9P8GKPKTqmM2+4= X-Google-Smtp-Source: AMsMyM7mGuE7LkjGW9FdFbo7+ELeWIPzI+1EudVSe0TewxKZ6ajJ2D2jEKHHKGc5zrOwa9+duMlWcg== X-Received: by 2002:aa7:de10:0:b0:458:e101:fe54 with SMTP id h16-20020aa7de10000000b00458e101fe54mr5595269edv.80.1665780198731; Fri, 14 Oct 2022 13:43:18 -0700 (PDT) Received: from 2a02-8388-e203-9700-a1ba-94bc-d9c8-a446.cable.dynamic.v6.surfer.at (2a02-8388-e203-9700-a1ba-94bc-d9c8-a446.cable.dynamic.v6.surfer.at. [2a02:8388:e203:9700:a1ba:94bc:d9c8:a446]) by smtp.gmail.com with ESMTPSA id ss5-20020a170907038500b0073c80d008d5sm1993080ejb.122.2022.10.14.13.43.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 13:43:18 -0700 (PDT) Message-ID: <4dcc975beb8b39ab4d57b28334c6ab3348855bd9.camel@gmail.com> Subject: Re: [PATCH] middle-end IFN_ASSUME support [PR106654] From: Martin Uecker To: Jakub Jelinek Cc: "gcc-patches@gcc.gnu.org" Date: Fri, 14 Oct 2022 22:43:16 +0200 In-Reply-To: References: Content-Type: text/plain; charset="UTF-8" User-Agent: Evolution 3.30.5-1.1 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,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: Am Montag, den 10.10.2022, 10:54 +0200 schrieb Jakub Jelinek: > 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. My recommendation would be to only process side-effect-free assumptions and warn about the rest (clang does this for __builtin_assume). I do not think this is worth the complexity and I am not so sure the semantics of a hypothetical evaluation are terribly well defined. That you can not verify this properly by turning it into traps in debug mode (as this would execute the side effects) also makes this a terrible feature IMHO. MSVC which this feature was based does not seem to make much to sense to me: https://godbolt.org/z/4Ebar3G9b Martin > 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 >