From patchwork Mon Feb 24 09:18:08 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhenqiang Chen X-Patchwork-Id: 25180 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-vc0-f197.google.com (mail-vc0-f197.google.com [209.85.220.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id D183A203C4 for ; Mon, 24 Feb 2014 09:19:15 +0000 (UTC) Received: by mail-vc0-f197.google.com with SMTP id hq11sf11315190vcb.8 for ; Mon, 24 Feb 2014 01:19:15 -0800 (PST) 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:date:message-id:subject:from:to:cc :x-original-sender:x-original-authentication-results:content-type; bh=UXBh4m0NcuZNAVsRZA+e3ZBNC18HzzVGrKuT4WZuUXQ=; b=Um7umMwfCmpss4PcPVsAAVLg7YLMK22hPdJthg0Yiuwgh5Ysl7C793DLlrfsHznwvK Y1aUVmsxWPifMs1AKXgBdIO8fDYeDxNtVtiuG8NlZZZ2uBUi8cErY6CyndsZ8PC2pTt4 X5XTBYOzxfT9x75XyCAzLnODic7A69qdYlFn968UODo35HsQ489z5w3Pg9fZw0vqxOIv 3L+ayTnIjXpwRC9mrAqUScefndVqBxact1QHNcApyvkniQ0WoN/jNgO2ZrdO1BpnIBst mXiM0TdDLmAYktUfImHmt/eoD0u4ThRRzQXIlWcX+jN3VpuDlhZSgZwKI+Jxw1yX1k5n 5Y0w== X-Gm-Message-State: ALoCoQnyEMiWjYsKFmCs8ozmFcsiQlUnujdcLWK1pP+Z/jTH1iI/KsdhbbJasqySTgKs9HqyoN97 X-Received: by 10.224.76.6 with SMTP id a6mr3373187qak.6.1393233555543; Mon, 24 Feb 2014 01:19:15 -0800 (PST) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.49.113 with SMTP id p104ls1916768qga.41.gmail; Mon, 24 Feb 2014 01:19:15 -0800 (PST) X-Received: by 10.220.67.18 with SMTP id p18mr11807140vci.14.1393233555409; Mon, 24 Feb 2014 01:19:15 -0800 (PST) Received: from mail-ve0-x22a.google.com (mail-ve0-x22a.google.com [2607:f8b0:400c:c01::22a]) by mx.google.com with ESMTPS id kl10si5654981vdb.129.2014.02.24.01.19.15 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 24 Feb 2014 01:19:15 -0800 (PST) Received-SPF: neutral (google.com: 2607:f8b0:400c:c01::22a is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=2607:f8b0:400c:c01::22a; Received: by mail-ve0-f170.google.com with SMTP id c14so1146855vea.15 for ; Mon, 24 Feb 2014 01:19:15 -0800 (PST) X-Received: by 10.220.106.84 with SMTP id w20mr11810474vco.18.1393233553263; Mon, 24 Feb 2014 01:19:13 -0800 (PST) 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.220.174.196 with SMTP id u4csp48027vcz; Mon, 24 Feb 2014 01:19:12 -0800 (PST) X-Received: by 10.66.11.202 with SMTP id s10mr24269242pab.86.1393233552240; Mon, 24 Feb 2014 01:19:12 -0800 (PST) Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id o7si16145250pbh.122.2014.02.24.01.19.11 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 24 Feb 2014 01:19:12 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-362278-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Received: (qmail 14576 invoked by alias); 24 Feb 2014 09:18:41 -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 14422 invoked by uid 89); 24 Feb 2014 09:18:39 -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-la0-f43.google.com Received: from mail-la0-f43.google.com (HELO mail-la0-f43.google.com) (209.85.215.43) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Mon, 24 Feb 2014 09:18:12 +0000 Received: by mail-la0-f43.google.com with SMTP id pv20so5281191lab.16 for ; Mon, 24 Feb 2014 01:18:08 -0800 (PST) MIME-Version: 1.0 X-Received: by 10.112.189.68 with SMTP id gg4mr10776344lbc.18.1393233488479; Mon, 24 Feb 2014 01:18:08 -0800 (PST) Received: by 10.112.173.137 with HTTP; Mon, 24 Feb 2014 01:18:08 -0800 (PST) Date: Mon, 24 Feb 2014 17:18:08 +0800 Message-ID: Subject: [PATCH 1/n] Add conditional compare framework in middle-end From: Zhenqiang Chen To: "gcc-patches@gcc.gnu.org" Cc: Richard Henderson , Richard Earnshaw X-IsSubscribed: yes X-Original-Sender: zhenqiang.chen@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 2607:f8b0:400c:c01::22a is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org; dkim=pass header.i=@gcc.gnu.org X-Google-Group-Id: 836684582541 Hi, The patch (http://gcc.gnu.org/ml/gcc-patches/2014-11/msg03622.html) is re-based (the arm port change is stripped as a separate patch), which includes only the middle-end changes. The basic logic for the patch is: 1) Identify conditional compare candidates when expanding one GIMPLE_COND. 2) For a candidate, it uses two target hooks to generate the expected codes: - gen_ccmp_first expands the first compare in CCMP. - gen_ccmp_next expands the following compares. Another hook select_ccmp_cmp_order is called to determine which compare is done first since not all combination of compares are legal in some target like ARM. We might get more chance when swapping the compares. For AARCH64 and IA64, we do not need such check. During expanding, we must make sure that no instruction can clobber the CC reg except the compares. If the final result is not used in a COND_EXPR, it calls cstorecc4 pattern to store the CC to a general register. Bootstrap and no make check regression on X86-64. Is is OK for next stage1? Thanks! -Zhenqiang ChangeLog: 2014-02-24 Zhenqiang Chen * cfgexpand.c (expand_gimple_cond): Check ccmp support. * doc/md.texi (ccmp): New index. * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST, TARGET_GEN_CCMP_NEXT): New hooks. * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST, TARGET_GEN_CCMP_NEXT): New hooks. * doc/tm.texi.in (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST, TARGET_GEN_CCMP_NEXT): New hooks. * expmed.c (emit_cstore): Make it global. * expr.c: Include tree-phinodes.h and ssa-iterators.h. (ccmp_candidate_p, used_in_cond_stmt_p, check_clobber_cc, clobber_cc_p, gen_ccmp_next, expand_ccmp_expr_1, expand_ccmp_expr): New functions. (expand_expr_real_1): Handle conditional compare. * optabs.c (get_rtx_code): Make it global and handle BIT_AND_EXPR and BIT_IOR_EXPR. * optabs.h (get_rtx_code, emit_cstore): New prototypes. * recog.c (ccmp_insn_p): New function. (simplify_while_replacing): Do not swap ccmp insn. * target.def (select_ccmp_cmp_order, gen_ccmp_first, gen_ccmp_next): Define hooks. * targhooks.c (default_select_ccmp_cmp_order): New function. * targhooks.h (default_select_ccmp_cmp_order): New prototypes. diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 06d494c..a57e11c 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -2046,9 +2046,10 @@ expand_gimple_cond (basic_block bb, gimple stmt) op0 = gimple_assign_rhs1 (second); op1 = gimple_assign_rhs2 (second); } - /* If jumps are cheap turn some more codes into - jumpy sequences. */ - else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4) + /* If jumps are cheap and the target does not support conditional + compare, turn some more codes into jumpy sequences. */ + else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4 + && (targetm.gen_ccmp_first == NULL)) { if ((code2 == BIT_AND_EXPR && TYPE_PRECISION (TREE_TYPE (op0)) == 1 diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index 746acc2..dfcdc61 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -6206,6 +6206,42 @@ A typical @code{ctrap} pattern looks like "@dots{}") @end smallexample +@cindex @code{ccmp} instruction pattern +@item @samp{ccmp} +Conditional compare instruction. Operand 2 and 5 are RTLs which perform +two comparisons. Operand 1 is AND or IOR, which operates on the result of +Operand 2 and 5. Operand 0 is the result of operand 1. +It uses recursive method to support more than two compares. e.g. + + CC0 = CMP (a, b); + CC1 = CCMP (NE (CC0, 0), CMP (e, f)); + ... + CCn = CCMP (NE (CCn-1, 0), CMP (...)); + +Two target hooks are used to generate conditional compares. GEN_CCMP_FISRT +is used to generate the first CMP. And GEN_CCMP_NEXT is used to generate the +following CCMPs. Operand 1 is AND or IOR. Operand 3 is the result of +GEN_CCMP_FISRT or a previous GEN_CCMP_NEXT. Operand 2 is NE. +Operand 4, 5 and 6 is another compare expression. + +A typical CCMP pattern looks like + +@smallexample +(define_insn "*ccmp_and_ior" + [(set (match_operand 6 "dominant_cc_register" "") + (compare + (match_operator 1 + (match_operator 2 "comparison_operator" + [(match_operand 3 "dominant_cc_register") + (const_int 0)]) + (match_operator 4 "comparison_operator" + [(match_operand 5 "register_operand") + (match_operand 6 "compare_operand"])) + (const_int 0)))] + "" + "@dots{}") +@end smallexample + @cindex @code{prefetch} instruction pattern @item @samp{prefetch} diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index f204936..0aedc31 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -11180,6 +11180,32 @@ This target hook is required only when the target has several different modes and they have different conditional execution capability, such as ARM. @end deftypefn +@deftypefn {Target Hook} int TARGET_SELECT_CCMP_CMP_ORDER (int @var{code1}, int @var{code2}) +For some target (like ARM), the order of two compares is sensitive for +conditional compare. cmp0-cmp1 might be an invalid combination. But when +swapping the order, cmp1-cmp0 is valid. The function will return + 1: Keep current order. + -1: Swap the two compares. + 0: Invalid combination. +@end deftypefn + +@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_FIRST (int @var{code}, rtx @var{op0}, rtx @var{op1}) +This function emits a comparison insn for the first of a sequence of + conditional comparisions. It returns a comparison expression appropriate + for passing to @code{gen_ccmp_next} or to @code{cbranch_optab}. + @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode. +@end deftypefn + +@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_NEXT (rtx @var{prev}, int @var{cmp_code}, rtx @var{op0}, rtx @var{op1}, int @var{bit_code}) +This function emits a conditional comparison within a sequence of + conditional comparisons. The @code{prev} expression is the result of a + prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return + @code{NULL} if the combination of @code{prev} and this comparison is + not supported, otherwise the result must be appropriate for passing to + @code{gen_ccmp_next} or @code{cbranch_optab}. @code{bit_code} + is AND or IOR, which is the op on the two compares. +@end deftypefn + @deftypefn {Target Hook} unsigned TARGET_LOOP_UNROLL_ADJUST (unsigned @var{nunroll}, struct loop *@var{loop}) This target hook returns a new value for the number of times @var{loop} should be unrolled. The parameter @var{nunroll} is the number of times diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 50f412c..ecbb8b6 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -8283,6 +8283,12 @@ build_type_attribute_variant (@var{mdecl}, @hook TARGET_HAVE_CONDITIONAL_EXECUTION +@hook TARGET_SELECT_CCMP_CMP_ORDER + +@hook TARGET_GEN_CCMP_FIRST + +@hook TARGET_GEN_CCMP_NEXT + @hook TARGET_LOOP_UNROLL_ADJUST @defmac POWI_MAX_MULTS diff --git a/gcc/expmed.c b/gcc/expmed.c index 7c1c979..4ff90ce 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -5160,7 +5160,7 @@ expand_and (enum machine_mode mode, rtx op0, rtx op1, rtx target) } /* Helper function for emit_store_flag. */ -static rtx +rtx emit_cstore (rtx target, enum insn_code icode, enum rtx_code code, enum machine_mode mode, enum machine_mode compare_mode, int unsignedp, rtx x, rtx y, int normalizep, diff --git a/gcc/expr.c b/gcc/expr.c index f6708da..d2c6f6b 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -67,6 +67,8 @@ along with GCC; see the file COPYING3. If not see #include "params.h" #include "tree-ssa-address.h" #include "cfgexpand.h" +#include "tree-phinodes.h" +#include "ssa-iterators.h" /* Decide whether a function's arguments should be processed from first to last or from last to first. @@ -9228,6 +9230,299 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode, } #undef REDUCE_BIT_FIELD +/* 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. + + Another hook select_ccmp_cmp_order is called to determine which compare + is done first since not all combination of compares are legal in some + target like ARM. We might get more chance when swapping the 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); + /* TBD: If we can convert all + _Bool t; + + if (t == 1) + goto ; + else + goto ; + to + if (t != 0) + goto ; + else + goto ; + we can remove the following check. */ + 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) + *(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 +gen_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; + + 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, cmp_order; + enum rtx_code rcode0, rcode1, rcode; + rtx op0, op1, op2, op3, tmp; + gimple first, next; + + 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); + + /* For some target (like ARM), the order of two compares is sensitive + for conditional compare. cmp0-cmp1 might be an invalid combination. + But when swapping the order, cmp1-cmp0 is valid. The target hook + select_ccmp_cmp_order will return + 1: Keep current order. + -1: Swap the two compares. + 0: Invalid combination. */ + + cmp_order = targetm.select_ccmp_cmp_order (rcode0, rcode1); + /* Invalid combination. */ + if (!cmp_order) + return NULL_RTX; + /* Swap the compare order. Do gs1 first. */ + if (cmp_order == -1) + { + first = gs1; + next = gs0; + rcode = rcode1; + } + else + { + first = gs0; + next = gs1; + rcode = rcode0; + } + expand_operands (gimple_assign_rhs1 (first), + gimple_assign_rhs2 (first), + NULL_RTX, &op0, &op1, EXPAND_NORMAL); + + /* Since the operands of NEXT might clobber CC reg, we expand the + operands of NEXT before GEN_CCMP_FIRST. */ + expand_operands (gimple_assign_rhs1 (next), + gimple_assign_rhs2 (next), + NULL_RTX, &op2, &op3, EXPAND_NORMAL); + tmp = targetm.gen_ccmp_first (rcode, op0, op1); + if (!tmp) + return NULL_RTX; + + return targetm.gen_ccmp_next (tmp, + rcode == rcode1 ? rcode0 : rcode1, + op2, op3, get_rtx_code (code, 0)); + } + gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR); + tmp = expand_ccmp_expr_1 (gs1); + if (tmp) + return gen_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 gen_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; +} + +static rtx +expand_ccmp_expr (gimple g) +{ + rtx last = get_last_insn (); + tree lhs = gimple_assign_lhs (g); + rtx tmp = expand_ccmp_expr_1 (g); + + if (tmp) + { + enum insn_code icode; + /* 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); + if (icode != CODE_FOR_nothing) + { + rtx target = gen_reg_rtx (word_mode); + tmp = emit_cstore (target, icode, NE, CCmode, CCmode, + 0, tmp, const0_rtx, 1, word_mode); + if (tmp) + return tmp; + } + } + + /* Clean up. */ + delete_insns_since (last); + return NULL_RTX; +} /* Return TRUE if expression STMT is suitable for replacement. Never consider memory loads as replaceable, because those don't ever lead @@ -9392,10 +9687,22 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, { rtx r; location_t saved_loc = curr_insn_location (); + tree rhs = gimple_assign_rhs_to_tree (g); set_curr_insn_location (gimple_location (g)); - r = expand_expr_real (gimple_assign_rhs_to_tree (g), target, - tmode, modifier, NULL, inner_reference_p); + + if ((targetm.gen_ccmp_first != NULL) && ccmp_candidate_p (g)) + { + gcc_checking_assert (targetm.gen_ccmp_next != NULL); + r = expand_ccmp_expr (g); + if (!r) + r = expand_expr_real (rhs, target, tmode, modifier, + NULL, inner_reference_p); + } + else + r = expand_expr_real (rhs, target, tmode, modifier, + NULL, inner_reference_p); + set_curr_insn_location (saved_loc); if (REG_P (r) && !REG_EXPR (r)) set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (exp), r); diff --git a/gcc/optabs.c b/gcc/optabs.c index cec25a4..7fdbf47 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -6398,7 +6398,7 @@ gen_cond_trap (enum rtx_code code, rtx op1, rtx op2, rtx tcode) /* Return rtx code for TCODE. Use UNSIGNEDP to select signed or unsigned operation code. */ -static enum rtx_code +enum rtx_code get_rtx_code (enum tree_code tcode, bool unsignedp) { enum rtx_code code; @@ -6448,6 +6448,12 @@ get_rtx_code (enum tree_code tcode, bool unsignedp) code = LTGT; break; + case BIT_AND_EXPR: + code = AND; + break; + case BIT_IOR_EXPR: + code = IOR; + break; default: gcc_unreachable (); } diff --git a/gcc/optabs.h b/gcc/optabs.h index 8ecaa41..4426270 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -91,7 +91,7 @@ extern rtx expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op, extern rtx expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0, rtx op1, rtx op2, rtx target, int unsignedp); - +extern enum rtx_code get_rtx_code (enum tree_code tcode, bool unsignedp); /* Expand a binary operation given optab and rtx operands. */ extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int, enum optab_methods); @@ -556,4 +556,9 @@ extern void gen_satfractuns_conv_libfunc (convert_optab, const char *, enum machine_mode); extern void init_tree_optimization_optabs (tree); +extern rtx emit_cstore (rtx target, enum insn_code icode, enum rtx_code code, + enum machine_mode mode, enum machine_mode compare_mode, + int unsignedp, rtx x, rtx y, int normalizep, + enum machine_mode target_mode); + #endif /* GCC_OPTABS_H */ diff --git a/gcc/recog.c b/gcc/recog.c index f9040dc..32a2648 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -556,6 +556,19 @@ cancel_changes (int num) #define CODE_FOR_extzv CODE_FOR_nothing #endif +static bool +ccmp_insn_p (rtx object) +{ + rtx x = PATTERN (object); + if (targetm.gen_ccmp_first + && GET_CODE (x) == SET + && GET_CODE (XEXP (x, 1)) == COMPARE + && (GET_CODE (XEXP (XEXP (x, 1), 0)) == IOR + || GET_CODE (XEXP (XEXP (x, 1), 0)) == AND)) + return true; + return false; +} + /* A subroutine of validate_replace_rtx_1 that tries to simplify the resulting rtx. */ @@ -567,7 +580,8 @@ simplify_while_replacing (rtx *loc, rtx to, rtx object, enum rtx_code code = GET_CODE (x); rtx new_rtx = NULL_RTX; - if (SWAPPABLE_OPERANDS_P (x) + /* Do not swap compares in conditional compare instruction. */ + if (SWAPPABLE_OPERANDS_P (x) && !ccmp_insn_p (object) && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1))) { validate_unshare_change (object, loc, diff --git a/gcc/target.def b/gcc/target.def index 3a64cd1..e34d4a1 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2334,6 +2334,38 @@ modes and they have different conditional execution capability, such as ARM.", bool, (void), default_have_conditional_execution) +DEFHOOK +(select_ccmp_cmp_order, + "For some target (like ARM), the order of two compares is sensitive for\n\ +conditional compare. cmp0-cmp1 might be an invalid combination. But when\n\ +swapping the order, cmp1-cmp0 is valid. The function will return\n\ + 1: Keep current order.\n\ + -1: Swap the two compares.\n\ + 0: Invalid combination.", + int, (int code1, int code2), + default_select_ccmp_cmp_order) + +DEFHOOK +(gen_ccmp_first, + "This function emits a comparison insn for the first of a sequence of\n\ + conditional comparisions. It returns a comparison expression appropriate\n\ + for passing to @code{gen_ccmp_next} or to @code{cbranch_optab}.\n\ + @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode.", + rtx, (int code, rtx op0, rtx op1), + NULL) + +DEFHOOK +(gen_ccmp_next, + "This function emits a conditional comparison within a sequence of\n\ + conditional comparisons. The @code{prev} expression is the result of a\n\ + prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return\n\ + @code{NULL} if the combination of @code{prev} and this comparison is\n\ + not supported, otherwise the result must be appropriate for passing to\n\ + @code{gen_ccmp_next} or @code{cbranch_optab}. @code{bit_code}\n\ + is AND or IOR, which is the op on the two compares.", + rtx, (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code), + NULL) + /* Return a new value for loop unroll size. */ DEFHOOK (loop_unroll_adjust, diff --git a/gcc/targhooks.c b/gcc/targhooks.c index f3b5d56..014fd85 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -76,6 +76,12 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssanames.h" #include "insn-codes.h" +/* Default return 1 to keep current order. */ +int +default_select_ccmp_cmp_order (int, int) +{ + return 1; +} bool default_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 9dd4c83..00456dc 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -208,3 +208,4 @@ extern tree build_va_arg_indirect_ref (tree); extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *); extern bool can_use_doloop_if_innermost (double_int, double_int, unsigned int, bool); +extern int default_select_ccmp_cmp_order (int, int);