+2015-09-28 Oleg Endo <olegendo@gcc.gnu.org>
+
+ PR target/54236
+ * config/sh/predicates.md (t_reg_operand, negt_reg_operand): Allow
+ and handle ne and eq codes.
+ * config/sh/sh.c (sh_rtx_costs): Adjust matching of tst #imm,r0 insn.
+ (sh_recog_treg_set_expr): Early accept negt_reg_operand. Eearly reject
+ CONST_INT_P. Use reverse_condition.
+ (sh_split_treg_set_expr): Likewise.
+
2015-09-28 James Greenhalgh <james.greenhalgh@arm.com>
* config/arm/types.md (type): Add rotate_imm.
;; A predicate describing the T bit register in any form.
(define_predicate "t_reg_operand"
- (match_code "reg,subreg,sign_extend,zero_extend")
+ (match_code "reg,subreg,sign_extend,zero_extend,ne,eq")
{
switch (GET_CODE (op))
{
+ case EQ:
+ return t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))
+ && XEXP (op, 1) == const1_rtx;
+
+ case NE:
+ return t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))
+ && XEXP (op, 1) == const0_rtx;
+
case REG:
return REGNO (op) == T_REG;
;; A predicate describing a negated T bit register.
(define_predicate "negt_reg_operand"
- (match_code "subreg,xor")
+ (match_code "subreg,xor,ne,eq")
{
switch (GET_CODE (op))
{
+ case EQ:
+ return t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))
+ && XEXP (op, 1) == const0_rtx;
+
+ case NE:
+ return t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))
+ && XEXP (op, 1) == const1_rtx;
+
case XOR:
return t_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)))
- && satisfies_constraint_M (XEXP (op, 1));
+ && XEXP (op, 1) == const1_rtx;
case SUBREG:
return negt_reg_operand (XEXP (op, 0), GET_MODE (XEXP (op, 0)));
case EQ:
/* An and with a constant compared against zero is
- most likely going to be a TST #imm, R0 instruction.
- Notice that this does not catch the zero_extract variants from
- the md file. */
+ most likely going to be a TST #imm, R0 instruction. */
if (XEXP (x, 1) == const0_rtx
- && (GET_CODE (XEXP (x, 0)) == AND
- || (SUBREG_P (XEXP (x, 0))
- && GET_CODE (SUBREG_REG (XEXP (x, 0))) == AND)))
+ && ((GET_CODE (XEXP (x, 0)) == AND
+ || (SUBREG_P (XEXP (x, 0))
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == AND))
+ || GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT))
{
*total = 1;
return true;
return false;
/* Early accept known possible operands before doing recog. */
- if (op == const0_rtx || op == const1_rtx || t_reg_operand (op, mode))
+ if (op == const0_rtx || op == const1_rtx || t_reg_operand (op, mode)
+ || negt_reg_operand (op, mode))
return true;
/* Early reject impossible operands before doing recog.
such as lower-subreg will bail out. Some insns such as SH4A movua are
done with UNSPEC, so must reject those, too, or else it would result
in an invalid reg -> treg move. */
- if (register_operand (op, mode) || memory_operand (op, mode)
- || sh_unspec_insn_p (op))
+ if (CONST_INT_P (op) || register_operand (op, mode)
+ || memory_operand (op, mode) || sh_unspec_insn_p (op))
return false;
if (!can_create_pseudo_p ())
SET_PREV_INSN (i) = NULL;
SET_NEXT_INSN (i) = NULL;
+ /* If the comparison op doesn't have a result mode, set it to SImode. */
+ machine_mode prev_op_mode = GET_MODE (op);
+ if (COMPARISON_P (op) && prev_op_mode == VOIDmode)
+ PUT_MODE (op, SImode);
+
int result = recog (PATTERN (i), i, 0);
- /* It seems there is no insn like that. Create a simple negated
- version and try again. If we hit a negated form, we'll allow that
- and append a nott sequence when splitting out the insns. Insns that
- do the split can then remove the trailing nott if they know how to
- deal with it. */
- if (result < 0 && GET_CODE (op) == EQ)
+ /* It seems there is no insn like that. Create a negated version and
+ try again. If we hit a negated form, we'll allow that and append a
+ nott sequence when splitting out the insns. Insns that do the split
+ can then remove the trailing nott if they know how to deal with it. */
+ if (result < 0 && COMPARISON_P (op))
{
- PUT_CODE (op, NE);
- result = recog (PATTERN (i), i, 0);
- PUT_CODE (op, EQ);
- }
- if (result < 0 && GET_CODE (op) == NE)
- {
- PUT_CODE (op, EQ);
+ machine_mode cmp_mode = GET_MODE (XEXP (op, 0));
+ if (cmp_mode == VOIDmode)
+ cmp_mode = GET_MODE (XEXP (op, 1));
+
+ rtx_code prev_code = GET_CODE (op);
+ PUT_CODE (op, reverse_condition (GET_CODE (op)));
result = recog (PATTERN (i), i, 0);
- PUT_CODE (op, NE);
+ PUT_CODE (op, prev_code);
}
+ PUT_MODE (op, prev_op_mode);
recog_data = prev_recog_data;
return result >= 0;
}
fprintf (dump_file, "\n");
}
+ /* If the insn is not found, we will try a negated form and append
+ a nott. */
+ bool append_nott = false;
+
/* We are going to invoke recog/split_insns in a re-entrant way and thus
have to capture its current state and restore it afterwards. */
recog_data_d prev_recog_data = recog_data;
- int insn_code = recog (PATTERN (i), i, 0);
-
- /* If the insn was not found, see if we matched the negated form before
- and append a nott. */
- bool append_nott = false;
-
- if (insn_code < 0 && GET_CODE (x) == EQ)
+ if (negt_reg_operand (x, GET_MODE (x)))
{
- PUT_CODE (x, NE);
- insn_code = recog (PATTERN (i), i, 0);
- if (insn_code >= 0)
- append_nott = true;
- else
- PUT_CODE (x, EQ);
+ /* This is a normal movt followed by a nott. It will be converted
+ into a movrt after initial expansion. */
+ XEXP (PATTERN (i), 1) = get_t_reg_rtx ();
+ append_nott = true;
}
- if (insn_code < 0 && GET_CODE (x) == NE)
+ else
{
- PUT_CODE (x, EQ);
- insn_code = recog (PATTERN (i), i, 0);
- if (insn_code >= 0)
- append_nott = true;
- else
- PUT_CODE (x, NE);
- }
+ /* If the comparison op doesn't have a mode set, set it to SImode. */
+ if (COMPARISON_P (x) && GET_MODE (x) == VOIDmode)
+ PUT_MODE (x, SImode);
+
+ int insn_code = recog (PATTERN (i), i, 0);
- gcc_assert (insn_code >= 0);
+ if (insn_code < 0 && COMPARISON_P (x))
+ {
+ machine_mode cmp_mode = GET_MODE (XEXP (x, 0));
+ if (cmp_mode == VOIDmode)
+ cmp_mode = GET_MODE (XEXP (x, 1));
+
+ PUT_CODE (x, reverse_condition (GET_CODE (x)));
+ insn_code = recog (PATTERN (i), i, 0);
+ append_nott = true;
+ }
+
+ gcc_assert (insn_code >= 0);
+ }
/* Try to recursively split the insn. Some insns might refuse to split
any further while we are in the treg_set_expr splitting phase. They
+2015-09-28 Oleg Endo <olegendo@gcc.gnu.org>
+
+ PR target/54236
+ * gcc.target/sh/pr54236-1.c (test_09, test_10, test_11): New.
+ * gcc.target/sh/pr59533-1.c (test_23, test_24, test_25, test_26,
+ test_27): New.
+ * gcc.target/sh/pr54236-5.c: New.
+ * gcc.target/sh/pr54236-6.c: New.
+
2015-09-28 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR rtl-optimization/67456
/* { dg-do compile } */
/* { dg-options "-O1" } */
/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */
-/* { dg-final { scan-assembler-times "addc" 4 } } */
-/* { dg-final { scan-assembler-times "subc" 3 } } */
+/* { dg-final { scan-assembler-times "addc" 6 } } */
+/* { dg-final { scan-assembler-times "subc" 4 } } */
/* { dg-final { scan-assembler-times "sett" 5 } } */
/* { dg-final { scan-assembler-times "negc" 2 { target { ! sh2a } } } } */
/* 1x addc, 1x sett */
return (a << 1) + 1;
}
+
+unsigned int
+test_09 (unsigned int x)
+{
+ /* 1x tst, 1x addc */
+ return x - (x != 0);
+}
+
+unsigned int
+test_10 (unsigned int x)
+{
+ /* 1x tst, 1x subc */
+ return x + (x == 0);
+}
+
+unsigned int
+test_11 (unsigned int x)
+{
+ /* 1x tst, 1x addc */
+ return x + (x != 0);
+}
+
--- /dev/null
+/* Check that addc and subc instructions are generated as expected in
+ combination with ifcvt. */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* { dg-final { scan-assembler-times "subc" 4 { target { ! sh2a } } } } */
+/* { dg-final { scan-assembler-times "addc" 4 { target { ! sh2a } } } } */
+/* { dg-final { scan-assembler-times "not\t" 0 { target { ! sh2a } } } } */
+
+/* { dg-final { scan-assembler-times "subc" 4 { target { sh2a } } } } */
+/* { dg-final { scan-assembler-times "addc" 4 { target { sh2a } } } } */
+/* { dg-final { scan-assembler-times "nott" 0 { target { sh2a } } } } */
+
+/* { dg-final { scan-assembler-times "tst\t" 4 } } */
+/* { dg-final { scan-assembler-times "cmp/eq" 1 } } */
+/* { dg-final { scan-assembler-times "cmp/pl" 2 } } */
+/* { dg-final { scan-assembler-times "cmp/gt" 1 } } */
+
+/* { dg-final { scan-assembler-not "movt" } } */
+/* { dg-final { scan-assembler-not "negc" } } */
+/* { dg-final { scan-assembler-not "movrt" } } */
+
+int
+test_00 (int x, int y)
+{
+ /* 1x tst, 1x subc */
+ if (y)
+ ++x;
+ return x;
+}
+
+int
+test_01 (int x, int y)
+{
+ /* 1x tst, 1x addc */
+ if (y)
+ --x;
+ return x;
+}
+
+int
+test_02 (int x, int y)
+{
+ /* 1x tst, 1x addc */
+ if (!y)
+ ++x;
+ return x;
+}
+
+int
+test_03 (int x, int y)
+{
+ /* 1x tst, 1x subc */
+ if (!y)
+ --x;
+ return x;
+}
+
+int
+test_04 (int x, int y)
+{
+ /* 1x cmp/eq, 1x addc */
+ if (y == x)
+ ++x;
+ return x;
+}
+
+int
+test_05 (int x, int y)
+{
+ /* 1x cmp/gt, 1x subc */
+ if (y < 4)
+ ++x;
+ return x;
+}
+
+int
+test_06 (int x)
+{
+ /* 1x cmp/pl, 1x addc */
+ return x > 0 ? x + 1 : x;
+}
+
+int
+test_07 (int x)
+{
+ /* 1x cmp/pl, 1x subc */
+ return x > 0 ? x - 1 : x;
+}
--- /dev/null
+/* In this snippet, there was a missed subc case:
+ tst #1,r0
+ movt r0
+ neg r0,r0
+
+ which should be:
+ tst #1,r0
+ subc r0,r0
+*/
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* { dg-final { scan-assembler-times {tst #1,r0} 1 } } */
+/* { dg-final { scan-assembler-times {subc r} 1 } } */
+
+/* { dg-final { scan-assembler-not "movt|not|neg\movrt" } } */
+
+
+struct inode
+{
+ unsigned int i_gid;
+};
+
+struct iattr
+{
+ unsigned int ia_valid;
+ unsigned int ia_gid;
+};
+
+struct task_struct
+{
+ unsigned long flags;
+ unsigned int cap_effective;
+};
+
+extern int in_group_p (unsigned int);
+
+static inline struct task_struct*
+get_current (void)
+{
+ struct task_struct *current;
+ return current;
+}
+
+static inline int
+capable (int cap)
+{
+ if (((get_current()->cap_effective) & (1 << (cap))))
+ {
+ get_current()->flags |= 0x00000100;
+ return 1;
+ }
+ return 0;
+}
+
+int
+inode_change_ok (struct inode *inode, struct iattr *attr)
+{
+ int retval = -1;
+ unsigned int ia_valid = attr->ia_valid;
+
+ if (ia_valid & 512)
+ goto fine;
+
+ if ((ia_valid & 4)
+ && (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)
+ && !capable(0))
+ goto error;
+
+fine:
+ retval = 0;
+error:
+ return retval;
+}
/* { dg-final { scan-assembler-times "and" 3 } } */
/* { dg-final { scan-assembler-times "extu.b" 5 } } */
-/* { dg-final { scan-assembler-times "cmp/pz" 22 { target { ! sh2a } } } } */
-/* { dg-final { scan-assembler-times "addc" 3 { target { ! sh2a } } } } */
-/* { dg-final { scan-assembler-times "subc" 12 { target { ! sh2a } } } } */
+/* { dg-final { scan-assembler-times "cmp/pz" 27 { target { ! sh2a } } } } */
+/* { dg-final { scan-assembler-times "addc" 4 { target { ! sh2a } } } } */
+/* { dg-final { scan-assembler-times "subc" 16 { target { ! sh2a } } } } */
-/* { dg-final { scan-assembler-times "cmp/pz" 20 { target { sh2a } } } } */
-/* { dg-final { scan-assembler-times "addc" 5 { target { sh2a } } } } */
-/* { dg-final { scan-assembler-times "subc" 10 { target { sh2a } } } } */
+/* { dg-final { scan-assembler-times "cmp/pz" 25 { target { sh2a } } } } */
+/* { dg-final { scan-assembler-times "addc" 6 { target { sh2a } } } } */
+/* { dg-final { scan-assembler-times "subc" 14 { target { sh2a } } } } */
/* { dg-final { scan-assembler-times "bld" 2 { target { sh2a } } } } */
int
/* 1x cmp/pz, 1x movt */
return (x >> 31) + 1;
}
+
+int
+test_23 (int x)
+{
+ /* 1x cmp/pz, 1x subc */
+ return x < 0 ? x + 1 : x;
+}
+
+unsigned int
+test_24 (unsigned int x)
+{
+ /* 1x cmp/pz, 1x subc */
+ return x & 0x80000000 ? x + 1 : x;
+}
+
+unsigned int
+test_25 (unsigned int x)
+{
+ /* 1x cmp/pz, 1x subc */
+ return x >> 31 ? x + 1 : x;
+}
+
+int
+test_26 (int x)
+{
+ /* 1x cmp/pz, 1x subc */
+ return x >> 31 ? x + 1 : x;
+}
+
+int
+test_27 (int x, int y, int z)
+{
+ /* 1x cmp/pz, 1x addc */
+ return 1 - ((x >> 4) < 0) + z;
+}