From patchwork Thu Oct 11 21:56:34 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleg Endo X-Patchwork-Id: 191010 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 7AA912C0083 for ; Fri, 12 Oct 2012 08:57:05 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1350597426; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Message-ID:Subject:From:To:Date:Content-Type:Mime-Version: Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:Sender:Delivered-To; bh=cxiQfC7wj9PBMCwjUCdR 23pP0BQ=; b=ZC9XVgByrCRDbCAaF7FvXiq50HemDCkTSfTBQ+vCG8ZNU3y3CeiG Pw5Y5qNBwgsc++keV8Eqz/MVqnjzCMDm6FO7cltWBiolPVSDPNC+SBadt8yOWKl5 UVs5O1ptE34qOapa4nP9dcJZoVONZzNIL0lD95X+HLXLfu2nY2/5vgQ= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:Received:Message-ID:Subject:From:To:Date:Content-Type:Mime-Version:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=vX4kgngDdEi//wYUyaL1jDk+XYESmy6w3Vvo0pLd0NB+dZGXUKFqRPSZGKHowa jtr4pXZNVQYr0C53AUprjRYw4shuWkVsIgr4ytGEKELi5RChoJmaSuD9KCppu/Mp 0jDGc6kD9RCgTviBK1yJYyDJWY3TMcqySncfKcrwHZGVQ=; Received: (qmail 5341 invoked by alias); 11 Oct 2012 21:57:01 -0000 Received: (qmail 5331 invoked by uid 22791); 11 Oct 2012 21:56:59 -0000 X-SWARE-Spam-Status: No, hits=-3.5 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_NONE, RCVD_IN_HOSTKARMA_NO, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, TW_EG, TW_VR, UNPARSEABLE_RELAY X-Spam-Check-By: sourceware.org Received: from mailout07.t-online.de (HELO mailout07.t-online.de) (194.25.134.83) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 11 Oct 2012 21:56:49 +0000 Received: from fwd08.aul.t-online.de (fwd08.aul.t-online.de ) by mailout07.t-online.de with smtp id 1TMQkJ-0005pA-AK; Thu, 11 Oct 2012 23:56:47 +0200 Received: from [192.168.0.100] (E234o4ZL8huuTVnD8q7ajBncnDiQASP9EEdXaffuGRqZw6CqSLyaz1-dgPIIfSIZWa@[87.157.58.44]) by fwd08.t-online.de with esmtp id 1TMQkC-20rZtA0; Thu, 11 Oct 2012 23:56:40 +0200 Message-ID: <1349992594.8747.32.camel@yam-132-YW-E178-FTW> Subject: [SH] PR 51244 - Improve T bit store and cbranch From: Oleg Endo To: gcc-patches Date: Thu, 11 Oct 2012 23:56:34 +0200 Mime-Version: 1.0 X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk 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 Hello, This one further improves T bit stores and conditional branches on SH for cases like described in comment #53 in the PR. Tested on rev 192200 with make -k check RUNTESTFLAGS="--target_board=sh-sim \{-m2/-ml,-m2/-mb,-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}" and no new failures. Cheers, Oleg gcc/ChangeLog: PR target/51244 * config/sh/sh.md (negsi_cond, negdi_cond, stack_protect_test): Remove get_t_reg_rtx when invoking gen_branch_true or gen_branch_false. (*zero_extendsi2_compact): Convert to insn_and_split. Convert zero extensions of T bit stores to reg moves in splitter. Remove obsolete unnamed peephole2 that caught zero extensions after negc T bit stores. (*branch_true_eq, *branch_false_ne): Delete. (branch_true, branch_false): Convert insn to expander. Move actual insn logic to... (*cbranch_t): ...this new insn_and_split. Try to find preceding redundant T bit stores and tests and combine them with the conditional branch if possible in the splitter. (movrt_xor, *movt_movrt): New insn_and_split. * config/sh/predicates.md (cbranch_treg_value): New predicate. * config/sh/sh-protos.h (sh_eval_treg_value): Forward declare... * config/sh/sh.c (sh_eval_treg_value): ...this new function. (expand_cbranchsi4, expand_cbranchdi4): Remove get_t_reg_rtx when invoking gen_branch_true or gen_branch_false. testsuite/ChangeLog: PR target/51244 * gcc.target/sh/pr51244-13.c: New. * gcc.target/sh/pr51244-14.c: New. * gcc.target/sh/pr51244-15.c: New. * gcc.target/sh/pr51244-16.c: New. Index: gcc/config/sh/sh.c =================================================================== --- gcc/config/sh/sh.c (revision 192200) +++ gcc/config/sh/sh.c (working copy) @@ -2059,7 +2059,7 @@ void expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability) { - rtx (*branch_expander) (rtx, rtx) = gen_branch_true; + rtx (*branch_expander) (rtx) = gen_branch_true; comparison = prepare_cbranch_operands (operands, SImode, comparison); switch (comparison) { @@ -2071,7 +2071,7 @@ emit_insn (gen_rtx_SET (VOIDmode, get_t_reg_rtx (), gen_rtx_fmt_ee (comparison, SImode, operands[1], operands[2]))); - rtx jump = emit_jump_insn (branch_expander (operands[3], get_t_reg_rtx ())); + rtx jump = emit_jump_insn (branch_expander (operands[3])); if (probability >= 0) add_reg_note (jump, REG_BR_PROB, GEN_INT (probability)); } @@ -2123,7 +2123,7 @@ if (TARGET_CMPEQDI_T) { emit_insn (gen_cmpeqdi_t (operands[1], operands[2])); - emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ())); + emit_jump_insn (gen_branch_true (operands[3])); return true; } msw_skip = NE; @@ -2150,7 +2150,7 @@ if (TARGET_CMPEQDI_T) { emit_insn (gen_cmpeqdi_t (operands[1], operands[2])); - emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ())); + emit_jump_insn (gen_branch_false (operands[3])); return true; } msw_taken = NE; @@ -2281,6 +2281,43 @@ return true; } +/* Given an operand, return 1 if the evaluated operand plugged into an + if_then_else will result in a branch_true, 0 if branch_false, or + -1 if neither nor applies. The truth table goes like this: + + op | cmpval | code | result + ---------+--------+---------+-------------------- + T (0) | 0 | EQ (1) | 0 = 0 ^ (0 == 1) + T (0) | 1 | EQ (1) | 1 = 0 ^ (1 == 1) + T (0) | 0 | NE (0) | 1 = 0 ^ (0 == 0) + T (0) | 1 | NE (0) | 0 = 0 ^ (1 == 0) + !T (1) | 0 | EQ (1) | 1 = 1 ^ (0 == 1) + !T (1) | 1 | EQ (1) | 0 = 1 ^ (1 == 1) + !T (1) | 0 | NE (0) | 0 = 1 ^ (0 == 0) + !T (1) | 1 | NE (0) | 1 = 1 ^ (1 == 0) */ +int +sh_eval_treg_value (rtx op) +{ + enum rtx_code code = GET_CODE (op); + if ((code != EQ && code != NE) || !CONST_INT_P (XEXP (op, 1))) + return -1; + + int cmpop = code == EQ ? 1 : 0; + int cmpval = INTVAL (XEXP (op, 1)); + if (cmpval != 0 && cmpval != 1) + return -1; + + int t; + if (t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))) + t = 0; + else if (negt_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))) + t = 1; + else + return -1; + + return t ^ (cmpval == cmpop); +} + /* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4. */ static void @@ -2485,9 +2522,9 @@ sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode); if (branch_code == code) - emit_jump_insn (gen_branch_true (operands[3], get_t_reg_rtx ())); + emit_jump_insn (gen_branch_true (operands[3])); else - emit_jump_insn (gen_branch_false (operands[3], get_t_reg_rtx ())); + emit_jump_insn (gen_branch_false (operands[3])); } void @@ -2521,7 +2558,7 @@ { lab = gen_label_rtx (); sh_emit_scc_to_t (EQ, op0, op1); - emit_jump_insn (gen_branch_true (lab, get_t_reg_rtx ())); + emit_jump_insn (gen_branch_true (lab)); code = GT; } else Index: gcc/config/sh/sh.md =================================================================== --- gcc/config/sh/sh.md (revision 192200) +++ gcc/config/sh/sh.md (working copy) @@ -5322,8 +5322,8 @@ emit_insn (gen_movsi (operands[0], operands[1])); emit_jump_insn (INTVAL (operands[3]) - ? gen_branch_true (skip_neg_label, get_t_reg_rtx ()) - : gen_branch_false (skip_neg_label, get_t_reg_rtx ())); + ? gen_branch_true (skip_neg_label) + : gen_branch_false (skip_neg_label)); emit_label_after (skip_neg_label, emit_insn (gen_negsi2 (operands[0], operands[1]))); @@ -5391,8 +5391,8 @@ emit_insn (gen_movsi (high_dst, high_src)); emit_jump_insn (INTVAL (operands[3]) - ? gen_branch_true (skip_neg_label, get_t_reg_rtx ()) - : gen_branch_false (skip_neg_label, get_t_reg_rtx ())); + ? gen_branch_true (skip_neg_label) + : gen_branch_false (skip_neg_label)); if (!INTVAL (operands[3])) emit_insn (gen_clrt ()); @@ -5575,11 +5575,57 @@ [(set (match_operand:SI 0 "arith_reg_dest") (zero_extend:SI (match_operand:QIHI 1 "zero_extend_operand")))]) -(define_insn "*zero_extendsi2_compact" +(define_insn_and_split "*zero_extendsi2_compact" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (zero_extend:SI (match_operand:QIHI 1 "arith_reg_operand" "r")))] "TARGET_SH1" "extu. %1,%0" + "&& can_create_pseudo_p ()" + [(set (match_dup 0) (match_dup 2))] +{ + /* Sometimes combine fails to combine a T bit or negated T bit store to a + reg with a following zero extension. In the split pass after combine, + try to figure the extended reg was set. If it originated from the T + bit we can replace the zero extension with a reg move, which will be + eliminated. Notice that this also helps the *cbranch_t splitter when + it tries to post-combine tests and conditional branches, as it does not + check for zero extensions. */ + rtx ext_reg; + if (REG_P (operands[1])) + ext_reg = operands[1]; + else if (GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1]))) + ext_reg = SUBREG_REG (operands[1]); + else + FAIL; + + /* Reg moves must be of the same mode. */ + if (GET_MODE (ext_reg) != SImode) + FAIL; + + operands[2] = NULL_RTX; + for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX; + i = prev_nonnote_insn_bb (i)) + { + if (LABEL_P (i) || BARRIER_P (i)) + break; + if (!NONJUMP_INSN_P (i)) + continue; + + if (reg_set_p (ext_reg, i)) + { + rtx set_op = XEXP (set_of (ext_reg, i), 1); + if (set_op == NULL_RTX) + break; + if (t_reg_operand (set_op, VOIDmode) + || negt_reg_operand (set_op, VOIDmode)) + operands[2] = ext_reg; + break; + } + } + + if (operands[2] == NULL_RTX) + FAIL; +} [(set_attr "type" "arith")]) (define_insn "*zero_extendhisi2_media" @@ -8108,47 +8154,126 @@ ;; Define the real conditional branch instructions. ;; ------------------------------------------------------------------------ -(define_insn "branch_true" - [(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "") - (const_int 0)) - (label_ref (match_operand 0 "" "")) +(define_expand "branch_true" + [(set (pc) (if_then_else (ne (reg:SI T_REG) (const_int 0)) + (label_ref (match_operand 0)) (pc)))] - "TARGET_SH1" -{ - return output_branch (1, insn, operands); -} - [(set_attr "type" "cbranch")]) + "TARGET_SH1") -(define_insn "*branch_true_eq" - [(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "") - (const_int 1)) - (label_ref (match_operand 0 "" "")) +(define_expand "branch_false" + [(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0)) + (label_ref (match_operand 0)) (pc)))] - "TARGET_SH1" -{ - return output_branch (1, insn, operands); -} - [(set_attr "type" "cbranch")]) + "TARGET_SH1") -(define_insn "branch_false" - [(set (pc) (if_then_else (eq (match_operand 1 "t_reg_operand" "") - (const_int 0)) - (label_ref (match_operand 0 "" "")) +(define_insn_and_split "*cbranch_t" + [(set (pc) (if_then_else (match_operand 1 "cbranch_treg_value") + (label_ref (match_operand 0)) (pc)))] "TARGET_SH1" { - return output_branch (0, insn, operands); + return output_branch (sh_eval_treg_value (operands[1]), insn, operands); } - [(set_attr "type" "cbranch")]) - -(define_insn "*branch_false_ne" - [(set (pc) (if_then_else (ne (match_operand 1 "t_reg_operand" "") - (const_int 1)) - (label_ref (match_operand 0 "" "")) + "&& can_create_pseudo_p ()" + [(set (pc) (if_then_else (eq (reg:SI T_REG) (match_dup 2)) + (label_ref (match_dup 0)) (pc)))] - "TARGET_SH1" { - return output_branch (0, insn, operands); + /* Try to find missed test and branch combine opportunities which result + in redundant T bit tests before conditional branches. + FIXME: Probably this would not be needed if CCmode was used + together with TARGET_FIXED_CONDITION_CODE_REGS. */ + + const int treg_value = sh_eval_treg_value (operands[1]); + operands[2] = NULL_RTX; + + /* Scan the insns backwards for an insn that sets the T bit by testing a + reg against zero like: + (set (reg T_REG) (eq (reg) (const_int 0))) */ + rtx testing_insn = NULL_RTX; + rtx tested_reg = NULL_RTX; + + for (rtx i = prev_nonnote_insn_bb (curr_insn); i != NULL_RTX; + i = prev_nonnote_insn_bb (i)) + { + if (LABEL_P (i) || BARRIER_P (i)) + break; + if (!NONJUMP_INSN_P (i)) + continue; + + rtx p = PATTERN (i); + if (p != NULL_RTX + && GET_CODE (p) == SET && t_reg_operand (XEXP (p, 0), VOIDmode) + && GET_CODE (XEXP (p, 1)) == EQ + && REG_P (XEXP (XEXP (p, 1), 0)) + && satisfies_constraint_Z (XEXP (XEXP (p, 1), 1))) + { + testing_insn = i; + tested_reg = XEXP (XEXP (p, 1), 0); + break; + } + } + + if (testing_insn == NULL_RTX) + FAIL; + + /* Continue scanning the insns backwards and try to find the insn that + sets the tested reg which we found above. If the reg is set by storing + the T bit or the negated T bit we can eliminate the test insn before + the branch. Notice that the branch condition has to be inverted if the + test is eliminated. */ + + /* If the T bit is used between the testing insn and the brach insn + leave it alone. */ + if (reg_used_between_p (get_t_reg_rtx (), testing_insn, curr_insn)) + FAIL; + + for (rtx i = prev_nonnote_insn_bb (testing_insn); i != NULL_RTX; + i = prev_nonnote_insn_bb (i)) + { + if (LABEL_P (i) || BARRIER_P (i)) + break; + if (!NONJUMP_INSN_P (i)) + continue; + + if (reg_set_p (tested_reg, i)) + { + const_rtx tested_reg_set = set_of (tested_reg, i); + + /* It could also be a clobber... */ + if (tested_reg_set == NULL_RTX || GET_CODE (tested_reg_set) != SET) + break; + + rtx set_op1 = XEXP (tested_reg_set, 1); + if (t_reg_operand (set_op1, VOIDmode)) + operands[2] = GEN_INT (treg_value ^ 1); + else if (negt_reg_operand (set_op1, VOIDmode)) + operands[2] = GEN_INT (treg_value); + else if (REG_P (set_op1)) + { + /* If it's a reg-reg copy follow the copied reg. This can + happen e.g. when T bit store zero-extensions are + eliminated. */ + tested_reg = set_op1; + continue; + } + + /* It's only safe to remove the testing insn if the T bit is not + modified between the testing insn and the insn that stores the + T bit. Notice that some T bit stores such as negc also modify + the T bit. */ + if (modified_between_p (get_t_reg_rtx (), i, testing_insn) + || modified_in_p (get_t_reg_rtx (), i)) + operands[2] = NULL_RTX; + + break; + } + } + + if (operands[2] == NULL_RTX) + FAIL; + + set_insn_deleted (testing_insn); } [(set_attr "type" "cbranch")]) @@ -10960,26 +11085,54 @@ (set (reg:SI T_REG) (const_int 1)) (use (match_dup 2))])]) -;; In some cases the zero extension does not get combined away and a -;; sequence like the following might remain: -;; mov #-1,r2 -;; tst r1,r1 -;; negc r2,r1 -;; extu.b r1,r1 -(define_peephole2 - [(parallel - [(set (match_operand:SI 0 "arith_reg_dest" "") - (xor:SI (match_operand:SI 1 "t_reg_operand" "") (const_int 1))) - (set (reg:SI T_REG) (const_int 1)) - (use (match_operand:SI 2 "arith_reg_operand" ""))]) - (set (match_dup 0) - (zero_extend:SI (match_operand 3 "arith_reg_operand" "")))] - "TARGET_SH1 && REGNO (operands[0]) == REGNO (operands[3])" - [(parallel - [(set (match_dup 0) (xor:SI (match_dup 1) (const_int 1))) - (set (reg:SI T_REG) (const_int 1)) - (use (match_dup 2))])]) +;; Store the negated T bit in a reg using r0 and xor. This one doesn't +;; clobber the T bit, which is useful when storing the T bit and the +;; negated T bit in parallel. On SH2A the movrt insn can be used for that. +;; Usually we don't want this insn to be matched, except for cases where the +;; T bit clobber is really not appreciated. Hence the extra use on T_REG. +(define_insn_and_split "movrt_xor" + [(set (match_operand:SI 0 "arith_reg_dest" "=z") + (xor:SI (match_operand:SI 1 "t_reg_operand") (const_int 1))) + (use (reg:SI T_REG))] + "TARGET_SH1 && !TARGET_SH2A" + "#" + "&& reload_completed" + [(set (match_dup 0) (reg:SI T_REG)) + (set (match_dup 0) (xor:SI (match_dup 0) (const_int 1)))]) +;; Store the T bit and the negated T bit in two regs in parallel. There is +;; no real insn to do that, but specifying this pattern will give combine +;; some opportunities. +(define_insn_and_split "*movt_movrt" + [(parallel [(set (match_operand:SI 0 "arith_reg_dest") + (match_operand:SI 1 "negt_reg_operand")) + (set (match_operand:SI 2 "arith_reg_dest") + (match_operand:SI 3 "t_reg_operand"))])] + "TARGET_SH1" + "#" + "&& 1" + [(const_int 0)] +{ + rtx i = TARGET_SH2A + ? gen_movrt (operands[0], get_t_reg_rtx ()) + : gen_movrt_xor (operands[0], get_t_reg_rtx ()); + + emit_insn (i); + emit_insn (gen_movt (operands[2], get_t_reg_rtx ())); + DONE; +}) + +(define_insn_and_split "*movt_movrt" + [(parallel [(set (match_operand:SI 0 "arith_reg_dest") + (match_operand:SI 1 "t_reg_operand")) + (set (match_operand:SI 2 "arith_reg_dest") + (match_operand:SI 3 "negt_reg_operand"))])] + "TARGET_SH1" + "#" + "&& 1" + [(parallel [(set (match_dup 2) (match_dup 3)) + (set (match_dup 0) (match_dup 1))])]) + ;; Use negc to store the T bit in a MSB of a reg in the following way: ;; T = 1: 0x80000000 -> reg ;; T = 0: 0x7FFFFFFF -> reg @@ -15165,7 +15318,7 @@ else { emit_insn (gen_stack_protect_test_si (operands[0], operands[1])); - emit_jump_insn (gen_branch_true (operands[2], get_t_reg_rtx ())); + emit_jump_insn (gen_branch_true (operands[2])); } DONE; Index: gcc/config/sh/predicates.md =================================================================== --- gcc/config/sh/predicates.md (revision 192200) +++ gcc/config/sh/predicates.md (working copy) @@ -1048,6 +1048,14 @@ } }) +;; A predicate that returns true if OP is a valid construct around the T bit +;; that can be used as an operand for conditional branches. +(define_predicate "cbranch_treg_value" + (match_code "eq,ne,reg,subreg,xor,sign_extend,zero_extend") +{ + return sh_eval_treg_value (op) >= 0; +}) + ;; Returns true of OP is arith_reg_operand or t_reg_operand. (define_predicate "arith_reg_or_t_reg_operand" (ior (match_operand 0 "arith_reg_operand") Index: gcc/config/sh/sh-protos.h =================================================================== --- gcc/config/sh/sh-protos.h (revision 192200) +++ gcc/config/sh/sh-protos.h (working copy) @@ -162,6 +162,7 @@ extern void sh_canonicalize_comparison (enum rtx_code&, rtx&, rtx&, enum machine_mode mode = VOIDmode); extern rtx sh_find_equiv_gbr_addr (rtx cur_insn, rtx mem); +extern int sh_eval_treg_value (rtx op); #endif /* RTX_CODE */ extern void sh_cpu_cpp_builtins (cpp_reader* pfile); Index: gcc/testsuite/gcc.target/sh/pr51244-14.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr51244-14.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr51244-14.c (revision 0) @@ -0,0 +1,107 @@ +/* This is a case extracted from CSiBE which would sometimes contain the + following sequence: + cmp/eq r12,r13 + movt r0 + xor #1,r0 + extu.b r0,r0 + movt r3 + tst r0,r0 + bf/s .L35 + where the negated T bit store did not combine properly. Since there are + other movt insns we only check for the xor and the extu. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */ +/* { dg-final { scan-assembler-not "xor|extu" } } */ + +typedef struct transaction_s transaction_t; + +struct journal_head +{ + transaction_t * b_transaction; + struct journal_head *b_cpnext, *b_cpprev; +}; + +struct transaction_s +{ + struct journal_head * t_checkpoint_list; + transaction_t *t_cpnext, *t_cpprev; +}; + +struct journal_s +{ + transaction_t * j_checkpoint_transactions; + unsigned long j_first, j_last; +}; + +typedef struct journal_s journal_t; + +extern int __try_to_free_cp_buf (struct journal_head *jh); +extern int __cleanup_transaction (journal_t *journal, transaction_t *transaction); +extern void __flush_batch (void **bhs, int *batch_count); +extern void* jh2bh (void*); + +static int +__flush_buffer (journal_t *journal, struct journal_head *jh, + void **bhs, int *batch_count, int *drop_count) +{ + void *bh = jh2bh (jh); + int ret = 0; + if (bh) + { + bhs[*batch_count] = bh; + (*batch_count)++; + if (*batch_count == 64) + ret = 1; + } + else + { + int last_buffer = 0; + if (jh->b_cpnext == jh) + last_buffer = 1; + if (__try_to_free_cp_buf (jh)) + { + (*drop_count)++; + ret = last_buffer; + } + } + return ret; +} + +int +log_do_checkpoint (journal_t *journal, int nblocks) +{ + transaction_t *transaction, *last_transaction, *next_transaction; + int batch_count = 0; + void *bhs[64]; + +repeat: + transaction = journal->j_checkpoint_transactions; + if (transaction == ((void *)0)) + return 0; + last_transaction = transaction->t_cpprev; + next_transaction = transaction; + do + { + struct journal_head *jh, *last_jh, *next_jh; + int drop_count = 0; + int cleanup_ret, retry = 0; + transaction = next_transaction; + next_transaction = transaction->t_cpnext; + jh = transaction->t_checkpoint_list; + last_jh = jh->b_cpprev; + next_jh = jh; + do + { + jh = next_jh; + next_jh = jh->b_cpnext; + retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count); + } while (jh != last_jh && !retry); + + if (retry) + goto repeat; + + cleanup_ret = __cleanup_transaction(journal, transaction); + goto repeat; + } while (transaction != last_transaction); +} Index: gcc/testsuite/gcc.target/sh/pr51244-16.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr51244-16.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr51244-16.c (revision 0) @@ -0,0 +1,11 @@ +/* Check that the redundant test removal code in the *cbranch_t split works + as expected on SH2A targets. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */ +/* { dg-final { scan-assembler-times "tst" 6 } } */ +/* { dg-final { scan-assembler-times "movt" 3 } } */ +/* { dg-final { scan-assembler-times "movrt" 3 } } */ +/* { dg-final { scan-assembler-not "extu|exts|negc" } } */ + +#include "pr51244-15.c" Index: gcc/testsuite/gcc.target/sh/pr51244-13.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr51244-13.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr51244-13.c (revision 0) @@ -0,0 +1,85 @@ +/* This is a case extracted from CSiBE which contained the following + sequence: + shll r0 + movt r0 + tst r0,r0 + bf .L11 + where the 'tst r0,r0' before the branch can be omitted by inverting the + branch condition. The tested function contains two other tst insns. If + everything goes as expected we will be seeing only those other two tst + insns. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */ +/* { dg-final { scan-assembler-times "tst" 2 } } */ + +static __inline__ int +__test_bit (unsigned long nr, volatile void * addr) +{ + /* This is on purpose. */ + int oldbit; + return oldbit & 1; +} + +static __inline__ int +__constant_test_bit (unsigned long nr, volatile void * addr) +{ + return (((volatile char *) addr)[(nr>>3)^7] & (1<<(nr&7))) != 0; +} + +struct list_head +{ + struct list_head *next, *prev; +}; + +static inline void +__list_del (struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +list_del (struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = 0; + entry->prev = 0; +} + +extern int nr_active_pages; +extern int nr_inactive_pages; +extern struct list_head active_list; + +typedef struct page +{ + unsigned long flags; + struct list_head lru; +} mem_map_t; + +void +activate_page_nolock (struct page * page) +{ + if ((__builtin_constant_p((6)) + ? __constant_test_bit((6),(&(page)->flags)) + : __test_bit((6),(&(page)->flags)) ) + && !(__builtin_constant_p((7)) + ? __constant_test_bit((7),(&(page)->flags)) + : __test_bit((7),(&(page)->flags)) )) + { + list_del(&(page)->lru); + nr_inactive_pages--; + if (!(__builtin_constant_p(6) ? __constant_test_bit((6),(&(page)->flags)) + : __test_bit((6),(&(page)->flags)))) + printk("", "", 43); + + if ((__builtin_constant_p(7) ? __constant_test_bit((7),(&(page)->flags)) + : __test_bit((7),(&(page)->flags)))) + printk("", "", 43); + + (__builtin_constant_p(7) ? __constant_set_bit((7),(&(page)->flags)) + : __set_bit((7),(&(page)->flags)) ); + list_add(&(page)->lru, &active_list); + nr_active_pages++; + } +} Index: gcc/testsuite/gcc.target/sh/pr51244-15.c =================================================================== --- gcc/testsuite/gcc.target/sh/pr51244-15.c (revision 0) +++ gcc/testsuite/gcc.target/sh/pr51244-15.c (revision 0) @@ -0,0 +1,71 @@ +/* Check that the redundant test removal code in the *cbranch_t split works + as expected on non-SH2A targets. Because on SH2A the movrt instruction + is used, this test is re-used and checked differently in pr51244-16.c. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O2" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" "-m2a*" } { "" } } */ +/* { dg-final { scan-assembler-times "tst" 6 } } */ +/* { dg-final { scan-assembler-times "movt" 6 } } */ +/* { dg-final { scan-assembler-times "xor" 3 } } */ +/* { dg-final { scan-assembler-not "extu|exts|negc" } } */ + +typedef char bool; + +int +test_0 (int a, int b, int c, int* d) +{ + /* non SH2A: 1x tst, 1x movt, 1x xor + SH2A: 1x tst, 1x movrt */ + bool x = a == 0; + d[2] = !x; + return x ? b : c; +} + +int +test_1 (int a, int b, int c, int* d) +{ + /* 1x tst, 1x movt */ + bool x = a != 0; + d[2] = !x; + return x ? b : c; +} + +int +test_2 (int a, int b, int c, char* d) +{ + /* Check that there is no sign/zero-extension before the store. + non SH2A: 1x tst, 1x movt, 1x xor + SH2A: 1x tst, 1x movrt */ + bool x = a == 0; + d[2] = !x; + return x ? b : c; +} + +int +test_3 (int a, int b, int c, char* d) +{ + /* Check that there is no sign/zero-extension before the store. + 1x tst, 1x movt */ + bool x = a != 0; + d[2] = !x; + return x ? b : c; +} + +int +test_4 (int a, int b, int c, char* d) +{ + /* 1x tst, 1x movt */ + bool x = a != 0; + d[2] = !x; + return !x ? b : c; +} + +int +test_5 (int a, int b, int c, char* d) +{ + /* non SH2A: 1x tst, 1x movt, 1x xor + SH2A: 1x tst, 1x movrt */ + bool x = a == 0; + d[2] = !x; + return !x ? b : c; +}