From patchwork Mon Dec 6 18:43:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robin Dapp X-Patchwork-Id: 48547 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A476F3858428 for ; Mon, 6 Dec 2021 18:47:54 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A476F3858428 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1638816474; bh=e+++01CcF+kFVO8wqNH23U8Fcs1qNS2mdOOSi3W67tY=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=FrbouyJPsBjsTLbds3rPYxGBTOIwket18dvltXX5I/4qAf99mKBjEmYsXX1uSUJhc CHfwV+Mo9U5H7gRDyK/rdq/v3ZCxy5FOCQ9809lo9jQwmBIXqSc8afWjzOVKvwa1Ea Sq4JABA7Qr3ld2icJMi1rktgl8W6VujbM33I5aow= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by sourceware.org (Postfix) with ESMTPS id E9A453858C60 for ; Mon, 6 Dec 2021 18:46:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org E9A453858C60 Received: from pps.filterd (m0098404.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.1.2/8.16.1.2) with SMTP id 1B6IGCSP016309; Mon, 6 Dec 2021 18:46:01 GMT Received: from ppma06ams.nl.ibm.com (66.31.33a9.ip4.static.sl-reverse.com [169.51.49.102]) by mx0a-001b2d01.pphosted.com with ESMTP id 3csqna0knf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 06 Dec 2021 18:46:00 +0000 Received: from pps.filterd (ppma06ams.nl.ibm.com [127.0.0.1]) by ppma06ams.nl.ibm.com (8.16.1.2/8.16.1.2) with SMTP id 1B6IghV9026357; Mon, 6 Dec 2021 18:45:58 GMT Received: from b06cxnps4074.portsmouth.uk.ibm.com (d06relay11.portsmouth.uk.ibm.com [9.149.109.196]) by ppma06ams.nl.ibm.com with ESMTP id 3cqykhysk8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 06 Dec 2021 18:45:58 +0000 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 1B6IjtCX28049848 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 6 Dec 2021 18:45:55 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id F1D2811C04A; Mon, 6 Dec 2021 18:45:54 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A42D111C04C; Mon, 6 Dec 2021 18:45:54 +0000 (GMT) Received: from li-926bd7cc-2dd1-11b2-a85c-f6adc0f5efec.ibm.com (unknown [9.171.69.25]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 6 Dec 2021 18:45:54 +0000 (GMT) To: gcc-patches@gcc.gnu.org, richard.sandiford@arm.com Subject: [PATCH v3 1/7] ifcvt: Check if cmovs are needed. Date: Mon, 6 Dec 2021 19:43:46 +0100 Message-Id: <20211206184352.42264-2-rdapp@linux.ibm.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20211206184352.42264-1-rdapp@linux.ibm.com> References: <20211206184352.42264-1-rdapp@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-GUID: 2k5oSosIk7WeBG3aglh6QBd50-HA5N1n X-Proofpoint-ORIG-GUID: 2k5oSosIk7WeBG3aglh6QBd50-HA5N1n X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.790,Hydra:6.0.425,FMLib:17.11.62.513 definitions=2021-12-06_07,2021-12-06_02,2021-12-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 clxscore=1015 bulkscore=0 phishscore=0 impostorscore=0 spamscore=0 mlxscore=0 suspectscore=0 adultscore=0 priorityscore=1501 mlxlogscore=999 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2110150000 definitions=main-2112060111 X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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: Robin Dapp via Gcc-patches From: Robin Dapp Reply-To: Robin Dapp Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" When if-converting multiple SETs and we encounter a swap-style idiom if (a > b) { tmp = c; // [1] c = d; d = tmp; } ifcvt should not generate a conditional move for the instruction at [1]. In order to achieve that, this patch goes through all relevant SETs and marks the relevant instructions. This helps to evaluate costs. On top, only generate temporaries if the current cmov is going to overwrite one of the comparands of the initial compare. --- gcc/ifcvt.c | 174 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 150 insertions(+), 24 deletions(-) diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 017944f4f79..c98668d5646 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -98,6 +98,8 @@ static int dead_or_predicable (basic_block, basic_block, basic_block, edge, int); static void noce_emit_move_insn (rtx, rtx); static rtx_insn *block_has_only_trap (basic_block); +static void need_cmov_or_rewire (basic_block, hash_set *, + hash_map *); /* Count the number of non-jump active insns in BB. */ @@ -3203,6 +3205,11 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) auto_vec unmodified_insns; int count = 0; + hash_set need_no_cmov; + hash_map rewired_src; + + need_cmov_or_rewire (then_bb, &need_no_cmov, &rewired_src); + FOR_BB_INSNS (then_bb, insn) { /* Skip over non-insns. */ @@ -3213,26 +3220,49 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) gcc_checking_assert (set); rtx target = SET_DEST (set); - rtx temp = gen_reg_rtx (GET_MODE (target)); + rtx temp; + rtx new_val = SET_SRC (set); + if (int *ii = rewired_src.get (insn)) + new_val = simplify_replace_rtx (new_val, targets[*ii], + temporaries[*ii]); rtx old_val = target; - /* If we were supposed to read from an earlier write in this block, - we've changed the register allocation. Rewire the read. While - we are looking, also try to catch a swap idiom. */ - for (int i = count - 1; i >= 0; --i) - if (reg_overlap_mentioned_p (new_val, targets[i])) - { - /* Catch a "swap" style idiom. */ - if (find_reg_note (insn, REG_DEAD, new_val) != NULL_RTX) - /* The write to targets[i] is only live until the read - here. As the condition codes match, we can propagate - the set to here. */ - new_val = SET_SRC (single_set (unmodified_insns[i])); - else - new_val = temporaries[i]; - break; - } + /* As we are transforming + if (x > y) + { + a = b; + c = d; + } + into + a = (x > y) ... + c = (x > y) ... + + we potentially check x > y before every set. + Even though the check might be removed by subsequent passes, this means + that we cannot transform + if (x > y) + { + x = y; + ... + } + into + x = (x > y) ... + ... + since this would invalidate x and the following to-be-removed checks. + Therefore we introduce a temporary every time we are about to + overwrite a variable used in the check. Costing of a sequence with + these is going to be inaccurate so only use temporaries when + needed. */ + if (reg_overlap_mentioned_p (target, cond)) + temp = gen_reg_rtx (GET_MODE (target)); + else + temp = target; + + /* We have identified swap-style idioms before. A normal + set will need to be a cmov while the first instruction of a swap-style + idiom can be a regular move. This helps with costing. */ + bool need_cmov = !need_no_cmov.contains (insn); /* If we had a non-canonical conditional jump (i.e. one where the fallthrough is to the "else" case) we need to reverse @@ -3275,16 +3305,29 @@ noce_convert_multiple_sets (struct noce_if_info *if_info) old_val = lowpart_subreg (dst_mode, old_val, src_mode); } - /* Actually emit the conditional move. */ - rtx temp_dest = noce_emit_cmove (if_info, temp, cond_code, + rtx temp_dest = NULL_RTX; + + if (need_cmov) + { + /* Actually emit the conditional move. */ + temp_dest = noce_emit_cmove (if_info, temp, cond_code, x, y, new_val, old_val); - /* If we failed to expand the conditional move, drop out and don't - try to continue. */ - if (temp_dest == NULL_RTX) + /* If we failed to expand the conditional move, drop out and don't + try to continue. */ + if (temp_dest == NULL_RTX) + { + end_sequence (); + return FALSE; + } + } + else { - end_sequence (); - return FALSE; + if (if_info->then_else_reversed) + noce_emit_move_insn (temp, old_val); + else + noce_emit_move_insn (temp, new_val); + temp_dest = temp; } /* Bookkeeping. */ @@ -3808,6 +3851,89 @@ check_cond_move_block (basic_block bb, return TRUE; } +/* Find local swap-style idioms in BB and mark the first insn (1) + that is only a temporary as not needing a conditional move as + it is going to be dead afterwards anyway. + + (1) int tmp = a; + a = b; + b = tmp; + + ifcvt + --> + + tmp = a; + a = cond ? b : a_old; + b = cond ? tmp : b_old; + + Additionally, store the index of insns like (2) when a subsequent + SET reads from their destination. + + (2) int c = a; + int d = c; + + ifcvt + --> + + c = cond ? a : c_old; + d = cond ? d : c; // Need to use c rather than c_old here. +*/ + +static void +need_cmov_or_rewire (basic_block bb, + hash_set *need_no_cmov, + hash_map *rewired_src) +{ + rtx_insn *insn; + int count = 0; + auto_vec insns; + auto_vec dests; + + /* Iterate over all SETs, storing the destinations + in DEST. + - If we hit a SET that reads from a destination + that we have seen before and the corresponding register + is dead afterwards, the register does not need to be + moved conditionally. + - If we encounter a previously changed register, + rewire the read to the original source. */ + FOR_BB_INSNS (bb, insn) + { + rtx set, src, dest; + + if (!active_insn_p (insn)) + continue; + + set = single_set (insn); + if (set == NULL_RTX) + continue; + + src = SET_SRC (set); + if (SUBREG_P (src)) + src = SUBREG_REG (src); + dest = SET_DEST (set); + + /* Check if the current SET's source is the same + as any previously seen destination. + This is quadratic but the number of insns in BB + is bounded by PARAM_MAX_RTL_IF_CONVERSION_INSNS. */ + if (REG_P (src)) + for (int i = count - 1; i >= 0; --i) + if (reg_overlap_mentioned_p (src, dests[i])) + { + if (find_reg_note (insn, REG_DEAD, src) != NULL_RTX) + need_no_cmov->add (insns[i]); + else + rewired_src->put (insn, i); + } + + insns.safe_push (insn); + dests.safe_push (dest); + + count++; + } +} + /* Given a basic block BB suitable for conditional move conversion, a condition COND, and pointer maps THEN_VALS and ELSE_VALS containing the register values depending on COND, emit the insns in the block as