From patchwork Tue Jul 1 08:01:01 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhenqiang Chen X-Patchwork-Id: 32845 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ve0-f199.google.com (mail-ve0-f199.google.com [209.85.128.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 59F1F203C0 for ; Tue, 1 Jul 2014 08:01:28 +0000 (UTC) Received: by mail-ve0-f199.google.com with SMTP id oy12sf22587533veb.10 for ; Tue, 01 Jul 2014 01:01:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:mailing-list:precedence:list-id :list-unsubscribe:list-archive:list-post:list-help:sender :delivered-to:mime-version:in-reply-to:references:date:message-id :subject:from:to:cc:x-original-sender :x-original-authentication-results:content-type; bh=xTTkigSo4GopdQPyfavj/ilpDN0fHzFbrg1FhOSztiI=; b=DYCwAECTRX+zZLcmdVygMYAV1aHsfOBWQr0RrQAs73Oe2Ofa005yhi9evTlIMQhPEp /zpMB/wc7ZO6NVkG0ICct9PI4RVRrOxUsjmWtzTI2ac/Si6rVzYV+U7urTl2sjy2O/ow NKyvUhhtIce64GjIEbtA3hsVitQcGbJZxkZ5xZKQexqs3wblWBw9Wk+DlGjn2EVmUy3+ KXjP0ukuEsQs9HvsEvbLUF1achsIPzhdijpMRQHs3F3+3ozSB5bPQwOcHzFFuMvezV6n EhqYs4KzHZzwBW8cYZ8kQIxB2BHffcW1MR7cqpgr6e07qmscrTkzHkmoWZlCrWUpWuO9 OU7A== X-Gm-Message-State: ALoCoQndUtavdWd1RQSAoRNqukrgPn5NpovTC8VtR2x32MgJWSWgBgxDDsQBihcA139fW5tMtHqI X-Received: by 10.58.114.41 with SMTP id jd9mr10427145veb.9.1404201688177; Tue, 01 Jul 2014 01:01:28 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.94.107 with SMTP id f98ls633916qge.65.gmail; Tue, 01 Jul 2014 01:01:28 -0700 (PDT) X-Received: by 10.53.8.162 with SMTP id dl2mr36116356vdd.24.1404201688027; Tue, 01 Jul 2014 01:01:28 -0700 (PDT) Received: from mail-ve0-x22e.google.com (mail-ve0-x22e.google.com [2607:f8b0:400c:c01::22e]) by mx.google.com with ESMTPS id m2si11123875vek.107.2014.07.01.01.01.27 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 01 Jul 2014 01:01:27 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 2607:f8b0:400c:c01::22e as permitted sender) client-ip=2607:f8b0:400c:c01::22e; Received: by mail-ve0-f174.google.com with SMTP id jx11so9142916veb.5 for ; Tue, 01 Jul 2014 01:01:27 -0700 (PDT) X-Received: by 10.220.195.67 with SMTP id eb3mr577419vcb.30.1404201687806; Tue, 01 Jul 2014 01:01:27 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp197378vcb; Tue, 1 Jul 2014 01:01:27 -0700 (PDT) X-Received: by 10.68.245.100 with SMTP id xn4mr58232765pbc.152.1404201686956; Tue, 01 Jul 2014 01:01:26 -0700 (PDT) Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id ey3si13415274pbb.194.2014.07.01.01.01.26 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 01 Jul 2014 01:01:26 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-return-371558-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Received: (qmail 16259 invoked by alias); 1 Jul 2014 08:01:11 -0000 Mailing-List: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 16249 invoked by uid 89); 1 Jul 2014 08:01:10 -0000 X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.5 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-lb0-f172.google.com Received: from mail-lb0-f172.google.com (HELO mail-lb0-f172.google.com) (209.85.217.172) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 01 Jul 2014 08:01:05 +0000 Received: by mail-lb0-f172.google.com with SMTP id c11so6747869lbj.3 for ; Tue, 01 Jul 2014 01:01:01 -0700 (PDT) MIME-Version: 1.0 X-Received: by 10.152.4.73 with SMTP id i9mr6476315lai.50.1404201661368; Tue, 01 Jul 2014 01:01:01 -0700 (PDT) Received: by 10.112.13.36 with HTTP; Tue, 1 Jul 2014 01:01:01 -0700 (PDT) In-Reply-To: <53AAE7BB.8070709@arm.com> References: <53AAE7BB.8070709@arm.com> Date: Tue, 1 Jul 2014 16:01:01 +0800 Message-ID: Subject: Re: [PATCH, 4/10] expand ccmp From: Zhenqiang Chen To: Richard Earnshaw Cc: "gcc-patches@gcc.gnu.org" X-IsSubscribed: yes X-Original-Sender: zhenqiang.chen@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 2607:f8b0:400c:c01::22e as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org; dkim=pass header.i=@gcc.gnu.org X-Google-Group-Id: 836684582541 On 25 June 2014 23:16, Richard Earnshaw wrote: > On 23/06/14 07:59, Zhenqiang Chen wrote: >> Hi, >> >> This patch includes the main logic to expand ccmp instructions. >> >> In the patch, >> * ccmp_candidate_p is used to identify the CCMP candidate >> * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1 >> to expand CCMP. >> * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP. >> It calls gen_ccmp_first and gen_ccmp_next to generate CCMP instructions. >> >> During expanding, we must make sure that no instruction can clobber the >> CC reg except the compares. So clobber_cc_p and check_clobber_cc are >> introduced to do the check. >> >> * If the final result is not used in a COND_EXPR (checked by function >> used_in_cond_stmt_p), it calls cstorecc4 pattern to store the CC to a >> general register. >> >> Bootstrap and no make check regression on X86-64. >> >> OK for trunk? >> >> Thanks! >> -Zhenqiang >> >> ChangeLog: >> 2014-06-23 Zhenqiang Chen >> >> * ccmp.c (ccmp_candidate_p, used_in_cond_stmt_p, check_clobber_cc, >> clobber_cc_p, expand_ccmp_next, expand_ccmp_expr_1, expand_ccmp_expr): >> New functions to expand ccmp. >> * ccmp.h (expand_ccmp_expr): New prototype. >> * expr.c: #include "ccmp.h" >> (expand_expr_real_1): Try to expand ccmp. >> >> diff --git a/gcc/ccmp.c b/gcc/ccmp.c >> index 665c2a5..97b3910 100644 >> --- a/gcc/ccmp.c >> +++ b/gcc/ccmp.c >> @@ -47,6 +47,262 @@ along with GCC; see the file COPYING3. If not see >> #include "expmed.h" >> #include "ccmp.h" >> >> +/* The following functions expand conditional compare (CCMP) instructions. >> + Here is a short description about the over all algorithm: >> + * ccmp_candidate_p is used to identify the CCMP candidate >> + >> + * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1 >> + to expand CCMP. >> + >> + * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP. >> + It calls two target hooks gen_ccmp_first and gen_ccmp_next to generate >> + CCMP instructions. >> + - gen_ccmp_first expands the first compare in CCMP. >> + - gen_ccmp_next expands the following compares. >> + >> + During expanding, we must make sure that no instruction can clobber the >> + CC reg except the compares. So clobber_cc_p and check_clobber_cc are >> + introduced to do the check. >> + >> + * If the final result is not used in a COND_EXPR (checked by function >> + used_in_cond_stmt_p), it calls cstorecc4 pattern to store the CC to a >> + general register. */ >> + >> +/* Check whether G is a potential conditional compare candidate. */ >> +static bool >> +ccmp_candidate_p (gimple g) >> +{ >> + tree rhs = gimple_assign_rhs_to_tree (g); >> + tree lhs, op0, op1; >> + gimple gs0, gs1; >> + enum tree_code tcode, tcode0, tcode1; >> + tcode = TREE_CODE (rhs); >> + >> + if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR) >> + return false; >> + >> + lhs = gimple_assign_lhs (g); >> + op0 = TREE_OPERAND (rhs, 0); >> + op1 = TREE_OPERAND (rhs, 1); >> + >> + if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME) >> + || !has_single_use (lhs)) >> + return false; >> + >> + gs0 = get_gimple_for_ssa_name (op0); >> + gs1 = get_gimple_for_ssa_name (op1); >> + if (!gs0 || !gs1 || !is_gimple_assign (gs0) || !is_gimple_assign (gs1) >> + /* g, gs0 and gs1 must be in the same basic block, since current stage >> + is out-of-ssa. We can not guarantee the correctness when forwording >> + the gs0 and gs1 into g whithout DATAFLOW analysis. */ >> + || gimple_bb (gs0) != gimple_bb (gs1) >> + || gimple_bb (gs0) != gimple_bb (g)) >> + return false; >> + >> + if (!(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0))) >> + || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0)))) >> + || !(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))) >> + || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))))) >> + return false; >> + >> + tcode0 = gimple_assign_rhs_code (gs0); >> + tcode1 = gimple_assign_rhs_code (gs1); >> + if (TREE_CODE_CLASS (tcode0) == tcc_comparison >> + && TREE_CODE_CLASS (tcode1) == tcc_comparison) >> + return true; >> + if (TREE_CODE_CLASS (tcode0) == tcc_comparison >> + && ccmp_candidate_p (gs1)) >> + return true; >> + else if (TREE_CODE_CLASS (tcode1) == tcc_comparison >> + && ccmp_candidate_p (gs0)) >> + return true; >> + /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since >> + there is no way to set the CC flag. */ >> + return false; >> +} >> + >> +/* Check whether EXP is used in a GIMPLE_COND statement or not. */ >> +static bool >> +used_in_cond_stmt_p (tree exp) >> +{ >> + bool expand_cond = false; >> + imm_use_iterator ui; >> + gimple use_stmt; >> + FOR_EACH_IMM_USE_STMT (use_stmt, ui, exp) >> + if (gimple_code (use_stmt) == GIMPLE_COND) >> + { >> + tree op1 = gimple_cond_rhs (use_stmt); >> + if (integer_zerop (op1)) >> + expand_cond = true; >> + BREAK_FROM_IMM_USE_STMT (ui); >> + } >> + return expand_cond; >> +} >> + >> +/* If SETTER clobber CC reg, set DATA to TRUE. */ >> +static void >> +check_clobber_cc (rtx reg, const_rtx setter, void *data) >> +{ >> + if (GET_CODE (setter) == CLOBBER && GET_MODE (reg) == CCmode) > > You shouldn't assume that CC regs will only ever have mode CCmode. It's > probably better to check for the mode class being MODE_CC. Also, not > all architectures have a single CC register, so this test might be > unduly restrictive on targets with multiple predicate registers. Thanks for the comments. The code segment is removed as the update for the following comments "hoist any preparation operations above the entire conditional compare sequence" >> + *(bool *)data = true; >> +} >> + >> +/* Check whether INSN and all its NEXT_INSN clobber CC reg or not. */ >> +static bool >> +clobber_cc_p (rtx insn) >> +{ >> + bool clobber = false; >> + for (; insn; insn = NEXT_INSN (insn)) >> + { >> + note_stores (PATTERN (insn), check_clobber_cc, &clobber); >> + if (clobber) >> + return true; >> + } >> + return false; >> +} >> + >> +/* Help function to generate conditional compare. PREV is the result of >> + GEN_CCMP_FIRST or GEN_CCMP_NEXT. G is the next compare. >> + CODE is BIT_AND_EXPR or BIT_IOR_EXPR. */ >> +static rtx >> +expand_ccmp_next (rtx prev, gimple g, enum tree_code code) >> +{ >> + rtx op0, op1; >> + int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (g))); >> + enum rtx_code rcode = get_rtx_code (gimple_assign_rhs_code (g), unsignedp); >> + rtx last = get_last_insn (); >> + >> + expand_operands (gimple_assign_rhs1 (g), >> + gimple_assign_rhs2 (g), >> + NULL_RTX, &op0, &op1, EXPAND_NORMAL); >> + >> + /* If any operand clobbers CC reg, we will give up. */ >> + if (clobber_cc_p (NEXT_INSN (last))) >> + return NULL_RTX; >> + > > I suspect it might be better to just hoist any preparation operations > above the entire conditional compare sequence, so that by the time we > start the ccmp expansion we're dealing with operands that are in the > 'natural' sizes for the machine (breaking up the conditional compare > sequence for what are almost side-effect operationssounds like a source > of potential bugs). This would also ensure that the back-end can safely > re-order at least some comparison operations if this leads a workable > conditional compare sequence. Updated. All the preparation operations are moved above the entire conditional compare sequence. > For example, in ARM > > (a < b && c <= d) > > has to be computed as > cmp c, d > cmple a, b > ...lt true-op > ...ge false-op > > since the sequence > cmp a, b > cmplt c, d > ...le true-op > > would lead to the wrong behaviour when a == b and c == d. > > >> + return targetm.gen_ccmp_next (prev, rcode, op0, op1, get_rtx_code (code, 0)); >> +} >> + >> +/* Expand conditional compare gimple G. A typical CCMP sequence is like: >> + >> + CC0 = CMP (a, b); >> + CC1 = CCMP (NE (CC0, 0), CMP (e, f)); >> + ... >> + CCn = CCMP (NE (CCn-1, 0), CMP (...)); >> + >> + hook gen_ccmp_first is used to expand the first compare. >> + hook gen_ccmp_next is used to expand the following CCMP. */ >> +static rtx >> +expand_ccmp_expr_1 (gimple g) >> +{ >> + tree exp = gimple_assign_rhs_to_tree (g); >> + enum tree_code code = TREE_CODE (exp); >> + gimple gs0 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 0)); >> + gimple gs1 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 1)); >> + rtx tmp; >> + enum tree_code code0 = gimple_assign_rhs_code (gs0); >> + enum tree_code code1 = gimple_assign_rhs_code (gs1); >> + >> + gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR); >> + gcc_assert (gs0 && gs1 && is_gimple_assign (gs0) && is_gimple_assign (gs1)); >> + >> + if (TREE_CODE_CLASS (code0) == tcc_comparison) >> + { >> + if (TREE_CODE_CLASS (code1) == tcc_comparison) >> + { >> + int unsignedp0, unsignedp1; >> + enum rtx_code rcode0, rcode1; >> + rtx op0, op1, op2, op3, tmp; >> + >> + unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0))); >> + rcode0 = get_rtx_code (code0, unsignedp0); >> + unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1))); >> + rcode1 = get_rtx_code (code1, unsignedp1); >> + >> + expand_operands (gimple_assign_rhs1 (gs0), >> + gimple_assign_rhs2 (gs0), >> + NULL_RTX, &op0, &op1, EXPAND_NORMAL); >> + >> + /* Since the operands of GS1 might clobber CC reg, we expand the >> + operands of GS1 before GEN_CCMP_FIRST. */ >> + expand_operands (gimple_assign_rhs1 (gs1), >> + gimple_assign_rhs2 (gs1), >> + NULL_RTX, &op2, &op3, EXPAND_NORMAL); >> + tmp = targetm.gen_ccmp_first (rcode0, op0, op1); >> + if (!tmp) >> + return NULL_RTX; >> + >> + return targetm.gen_ccmp_next (tmp, rcode1, op2, op3, >> + get_rtx_code (code, 0)); >> + } >> + else >> + { >> + gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR); >> + >> + /* Note: We swap the order to make the recursive function work. */ >> + tmp = expand_ccmp_expr_1 (gs1); >> + if (tmp) >> + return expand_ccmp_next (tmp, gs0, code); >> + } >> + } >> + else >> + { >> + gcc_assert (gimple_assign_rhs_code (gs0) == BIT_AND_EXPR >> + || gimple_assign_rhs_code (gs0) == BIT_IOR_EXPR); >> + if (TREE_CODE_CLASS (gimple_assign_rhs_code (gs1)) == tcc_comparison) >> + { >> + tmp = expand_ccmp_expr_1 (gs0); >> + if (tmp) >> + return expand_ccmp_next (tmp, gs1, code); >> + } >> + else >> + { >> + gcc_assert (gimple_assign_rhs_code (gs1) == BIT_AND_EXPR >> + || gimple_assign_rhs_code (gs1) == BIT_IOR_EXPR); >> + } >> + } >> + >> + return NULL_RTX; >> +} >> + >> +rtx >> +expand_ccmp_expr (gimple g) >> +{ >> + rtx last, tmp; >> + >> + if (!ccmp_candidate_p (g)) >> + return NULL_RTX; >> + >> + last = get_last_insn (); >> + tmp = expand_ccmp_expr_1 (g); >> + >> + if (tmp) >> + { >> + enum insn_code icode; >> + tree lhs = gimple_assign_lhs (g); >> + /* TMP should be CC. If it is used in a GIMPLE_COND, just return it. >> + Note: Target needs to define "cbranchcc4". */ >> + if (used_in_cond_stmt_p (lhs)) >> + return tmp; >> + >> + /* If TMP is not used in a GIMPLE_COND, store it with a csctorecc4_optab. >> + Note: Target needs to define "cstorecc4". */ >> + icode = optab_handler (cstore_optab, CCmode); > > I think you should be using SELECT_CC_MODE here. Updated. ops.op0 = gimple_assign_rhs1 (g); diff --git a/gcc/ccmp.c b/gcc/ccmp.c index fb51129..e0670f1 100644 --- a/gcc/ccmp.c +++ b/gcc/ccmp.c @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "tm.h" #include "rtl.h" +#include "tm_p.h" #include "tree.h" #include "stringpool.h" #include "regs.h" @@ -47,6 +48,245 @@ along with GCC; see the file COPYING3. If not see #include "expmed.h" #include "ccmp.h" +/* The following functions expand conditional compare (CCMP) instructions. + Here is a short description about the over all algorithm: + * ccmp_candidate_p is used to identify the CCMP candidate + + * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1 + to expand CCMP. + + * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP. + It calls two target hooks gen_ccmp_first and gen_ccmp_next to generate + CCMP instructions. + - gen_ccmp_first expands the first compare in CCMP. + - gen_ccmp_next expands the following compares. + + * If the final result is not used in a COND_EXPR (checked by function + used_in_cond_stmt_p), it calls cstorecc4 pattern to store the CC to a + general register. */ + +/* Check whether G is a potential conditional compare candidate. */ +static bool +ccmp_candidate_p (gimple g) +{ + tree rhs = gimple_assign_rhs_to_tree (g); + tree lhs, op0, op1; + gimple gs0, gs1; + enum tree_code tcode, tcode0, tcode1; + tcode = TREE_CODE (rhs); + + if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR) + return false; + + lhs = gimple_assign_lhs (g); + op0 = TREE_OPERAND (rhs, 0); + op1 = TREE_OPERAND (rhs, 1); + + if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME) + || !has_single_use (lhs)) + return false; + + gs0 = get_gimple_for_ssa_name (op0); + gs1 = get_gimple_for_ssa_name (op1); + if (!gs0 || !gs1 || !is_gimple_assign (gs0) || !is_gimple_assign (gs1) + /* g, gs0 and gs1 must be in the same basic block, since current stage + is out-of-ssa. We can not guarantee the correctness when forwording + the gs0 and gs1 into g whithout DATAFLOW analysis. */ + || gimple_bb (gs0) != gimple_bb (gs1) + || gimple_bb (gs0) != gimple_bb (g)) + return false; + + if (!(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0))) + || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0)))) + || !(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))) + || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1))))) + return false; + + tcode0 = gimple_assign_rhs_code (gs0); + tcode1 = gimple_assign_rhs_code (gs1); + if (TREE_CODE_CLASS (tcode0) == tcc_comparison + && TREE_CODE_CLASS (tcode1) == tcc_comparison) + return true; + if (TREE_CODE_CLASS (tcode0) == tcc_comparison + && ccmp_candidate_p (gs1)) + return true; + else if (TREE_CODE_CLASS (tcode1) == tcc_comparison + && ccmp_candidate_p (gs0)) + return true; + /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since + there is no way to set the CC flag. */ + return false; +} + +/* Check whether EXP is used in a GIMPLE_COND statement or not. */ +static bool +used_in_cond_stmt_p (tree exp) +{ + bool expand_cond = false; + imm_use_iterator ui; + gimple use_stmt; + FOR_EACH_IMM_USE_STMT (use_stmt, ui, exp) + if (gimple_code (use_stmt) == GIMPLE_COND) + { + tree op1 = gimple_cond_rhs (use_stmt); + if (integer_zerop (op1)) + expand_cond = true; + BREAK_FROM_IMM_USE_STMT (ui); + } + return expand_cond; +} + +/* Expand conditional compare gimple G. A typical CCMP sequence is like: + + CC0 = CMP (a, b); + CC1 = CCMP (NE (CC0, 0), CMP (e, f)); + ... + CCn = CCMP (NE (CCn-1, 0), CMP (...)); + + hook gen_ccmp_first is used to expand the first compare. + hook gen_ccmp_next is used to expand the following CCMP. */ +static rtx +expand_ccmp_expr_1 (gimple g) +{ + tree exp = gimple_assign_rhs_to_tree (g); + enum tree_code code = TREE_CODE (exp); + gimple gs0 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 0)); + gimple gs1 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 1)); + rtx tmp; + enum tree_code code0 = gimple_assign_rhs_code (gs0); + enum tree_code code1 = gimple_assign_rhs_code (gs1); + + gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR); + gcc_assert (gs0 && gs1 && is_gimple_assign (gs0) && is_gimple_assign (gs1)); + + if (TREE_CODE_CLASS (code0) == tcc_comparison) + { + if (TREE_CODE_CLASS (code1) == tcc_comparison) + { + int unsignedp0, unsignedp1; + enum rtx_code rcode0, rcode1; + rtx op0, op1, op2, op3, tmp; + + unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0))); + rcode0 = get_rtx_code (code0, unsignedp0); + unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1))); + rcode1 = get_rtx_code (code1, unsignedp1); + + expand_operands (gimple_assign_rhs1 (gs0), + gimple_assign_rhs2 (gs0), + NULL_RTX, &op0, &op1, EXPAND_NORMAL); + + /* Since the operands of GS1 might clobber CC reg, we expand the + operands of GS1 before GEN_CCMP_FIRST. */ + expand_operands (gimple_assign_rhs1 (gs1), + gimple_assign_rhs2 (gs1), + NULL_RTX, &op2, &op3, EXPAND_NORMAL); + tmp = targetm.gen_ccmp_first (rcode0, op0, op1); + if (!tmp) + return NULL_RTX; + + return targetm.gen_ccmp_next (tmp, rcode1, op2, op3, + get_rtx_code (code, 0)); + } + else + { + rtx op0, op1; + enum rtx_code rcode; + int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0))); + + rcode = get_rtx_code (gimple_assign_rhs_code (gs0), unsignedp); + + /* Hoist the preparation operations above the entire + conditional compare sequence. */ + expand_operands (gimple_assign_rhs1 (gs0), + gimple_assign_rhs2 (gs0), + NULL_RTX, &op0, &op1, EXPAND_NORMAL); + + gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR); + + /* Note: We swap the order to make the recursive function work. */ + tmp = expand_ccmp_expr_1 (gs1); + if (tmp) + return targetm.gen_ccmp_next (tmp, rcode, op0, op1, + get_rtx_code (code, 0)); + } + } + else + { + gcc_assert (gimple_assign_rhs_code (gs0) == BIT_AND_EXPR + || gimple_assign_rhs_code (gs0) == BIT_IOR_EXPR); + + if (TREE_CODE_CLASS (gimple_assign_rhs_code (gs1)) == tcc_comparison) + { + rtx op0, op1; + enum rtx_code rcode; + int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1))); + + rcode = get_rtx_code (gimple_assign_rhs_code (gs1), unsignedp); + + /* Hoist the preparation operations above the entire + conditional compare sequence. */ + expand_operands (gimple_assign_rhs1 (gs1), + gimple_assign_rhs2 (gs1), + NULL_RTX, &op0, &op1, EXPAND_NORMAL); + tmp = expand_ccmp_expr_1 (gs0); + if (tmp) + return targetm.gen_ccmp_next (tmp, rcode, op0, op1, + get_rtx_code (code, 0)); + } + else + { + gcc_assert (gimple_assign_rhs_code (gs1) == BIT_AND_EXPR + || gimple_assign_rhs_code (gs1) == BIT_IOR_EXPR); + } + } + + return NULL_RTX; +} + +rtx +expand_ccmp_expr (gimple g) +{ + rtx last, tmp; + + if (!ccmp_candidate_p (g)) + return NULL_RTX; + + last = get_last_insn (); + tmp = expand_ccmp_expr_1 (g); + + if (tmp) + { + enum insn_code icode; + enum machine_mode cc_mode = CCmode; + + tree lhs = gimple_assign_lhs (g); + /* TMP should be CC. If it is used in a GIMPLE_COND, just return it. + Note: Target needs to define "cbranchcc4". */ + if (used_in_cond_stmt_p (lhs)) + return tmp; + +#ifdef SELECT_CC_MODE + cc_mode = SELECT_CC_MODE (NE, tmp, const0_rtx); +#endif + /* If TMP is not used in a GIMPLE_COND, store it with a csctorecc4_optab. + Note: Target needs to define "cstorecc4". */ + icode = optab_handler (cstore_optab, cc_mode); + if (icode != CODE_FOR_nothing) + { + rtx target = gen_reg_rtx (word_mode); + tmp = emit_cstore (target, icode, NE, cc_mode, cc_mode, + 0, tmp, const0_rtx, 1, word_mode); + if (tmp) + return tmp; + } + } + + /* Clean up. */ + delete_insns_since (last); + return NULL_RTX; +} + bool ccmp_insn_p (rtx object) { diff --git a/gcc/ccmp.h b/gcc/ccmp.h index 7e139aa..56f3ac2 100644 --- a/gcc/ccmp.h +++ b/gcc/ccmp.h @@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_CCMP_H #define GCC_CCMP_H +extern rtx expand_ccmp_expr (gimple); + extern bool ccmp_insn_p (rtx); #endif /* GCC_CCMP_H */ diff --git a/gcc/expr.c b/gcc/expr.c index 04cf56e..4c31521 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-address.h" #include "cfgexpand.h" #include "builtins.h" +#include "ccmp.h" #ifndef STACK_PUSH_CODE #ifdef STACK_GROWS_DOWNWARD @@ -9379,6 +9380,15 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, /* Fallthru */ case GIMPLE_BINARY_RHS: ops.op1 = gimple_assign_rhs2 (g); + + /* Try to expand conditonal compare. */ + if (targetm.gen_ccmp_first != NULL) + { + gcc_checking_assert (targetm.gen_ccmp_next != NULL); + r = expand_ccmp_expr (g); + if (r) + break; + } /* Fallthru */ case GIMPLE_UNARY_RHS: